negotium 0.1.48 → 0.2.0

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 (51) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-helpers.js +58 -74
  3. package/dist/agent-helpers.js.map +15 -15
  4. package/dist/background-bash.js +22 -10
  5. package/dist/background-bash.js.map +3 -3
  6. package/dist/browser-runtime.js +35 -68
  7. package/dist/browser-runtime.js.map +7 -7
  8. package/dist/{chunk-d9jyt6xe.js → chunk-kc5kmr95.js} +162 -12
  9. package/dist/{chunk-d9jyt6xe.js.map → chunk-kc5kmr95.js.map} +4 -4
  10. package/dist/hosted-agent.js +36 -18
  11. package/dist/hosted-agent.js.map +7 -7
  12. package/dist/main.js +562 -98
  13. package/dist/main.js.map +21 -19
  14. package/dist/mcp-factories.js +602 -223
  15. package/dist/mcp-factories.js.map +16 -16
  16. package/dist/prompts.js +22 -10
  17. package/dist/prompts.js.map +3 -3
  18. package/dist/query-runtime.js +22 -10
  19. package/dist/query-runtime.js.map +4 -4
  20. package/dist/registry.js +286 -10
  21. package/dist/registry.js.map +9 -7
  22. package/dist/rollout.js +14 -2
  23. package/dist/rollout.js.map +3 -3
  24. package/dist/runtime/src/agents/maestro-registry.ts +1 -1
  25. package/dist/runtime/src/agents/rollout/shared.ts +12 -4
  26. package/dist/runtime/src/mcp/mcp-helpers.ts +1 -1
  27. package/dist/runtime/src/mcp/wiki-server.ts +1 -1
  28. package/dist/runtime/src/migration/single-user.ts +621 -0
  29. package/dist/runtime/src/platform/config.ts +24 -11
  30. package/dist/runtime/src/platform/mcp-config.ts +4 -4
  31. package/dist/runtime/src/platform/playwright/manager.ts +10 -28
  32. package/dist/runtime/src/runtime/attachments.ts +3 -3
  33. package/dist/runtime/src/storage/browser-profiles.ts +11 -9
  34. package/dist/runtime/src/storage/conversations.ts +5 -18
  35. package/dist/runtime/src/storage/storage-host.ts +1 -1
  36. package/dist/runtime/src/storage/tasks.ts +2 -14
  37. package/dist/runtime/src/storage/vault.ts +3 -2
  38. package/dist/runtime/src/version.ts +1 -1
  39. package/dist/runtime-helpers.js +22 -10
  40. package/dist/runtime-helpers.js.map +5 -5
  41. package/dist/storage.js +5 -19
  42. package/dist/storage.js.map +5 -5
  43. package/dist/types/packages/core/src/mcp/mcp-helpers.d.ts +1 -1
  44. package/dist/types/packages/core/src/platform/config.d.ts +9 -1
  45. package/dist/types/packages/core/src/runtime/attachments.d.ts +1 -1
  46. package/dist/types/packages/core/src/storage/conversations.d.ts +2 -2
  47. package/dist/types/packages/core/src/version.d.ts +1 -1
  48. package/dist/vault.js +25 -12
  49. package/dist/vault.js.map +4 -4
  50. package/install-browser-rs.mjs +1 -1
  51. package/package.json +2 -2
@@ -155,7 +155,7 @@ function resolveBrowserRsBin(envValue) {
155
155
  if (!override && !versionAtLeast(BROWSER_RS_VERSION.replace(/^v/, ""), BROWSER_RS_MIN_SECURE_VERSION)) {
156
156
  return;
157
157
  }
158
- const candidate = override ? resolve(override) : resolve(STATE_DIR, "bin", "browser-rs", BROWSER_RS_VERSION, "browser-rs");
158
+ const candidate = override ? resolve(override) : resolve(BINARIES_DIR, "browser-rs", BROWSER_RS_VERSION, "browser-rs");
159
159
  try {
160
160
  accessSync(candidate, constants.X_OK);
161
161
  return browserRsMeetsMinimumVersion(candidate) ? candidate : undefined;
@@ -167,7 +167,7 @@ function resolveBrowserMcpBin(envValue) {
167
167
  const override = envValue?.trim();
168
168
  if (override)
169
169
  return resolve(override);
170
- return BROWSER_RS_BIN ?? resolve(STATE_DIR, "bin", "browser-rs", BROWSER_RS_VERSION, "browser-rs");
170
+ return BROWSER_RS_BIN ?? resolve(BINARIES_DIR, "browser-rs", BROWSER_RS_VERSION, "browser-rs");
171
171
  }
172
172
  function resolveBrowserProxy() {
173
173
  const raw = envText("BROWSER_PROXY_URL");
@@ -199,7 +199,7 @@ function resolveTopicWorkspaceDir(topicId) {
199
199
  }
200
200
  function loadOrCreateLocalSecret(envKey, filename, options = {}) {
201
201
  const envValue = envText(envKey);
202
- const secretFile = resolve(STATE_DIR, filename);
202
+ const secretFile = resolve(SECRETS_DIR, filename);
203
203
  mkdirSync(dirname(secretFile), { recursive: true });
204
204
  if (envValue) {
205
205
  if (options.persistEnvValue) {
@@ -240,7 +240,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
240
240
  function codexAuthFilePath() {
241
241
  return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(homedir(), ".codex", "auth.json");
242
242
  }
243
- var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CONTEXTS_DIR, BROWSER_PROFILES_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.16", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
243
+ var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, CONTEXTS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.16", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
244
244
  var init_config = __esm(() => {
245
245
  init_config_helpers();
246
246
  init_logger();
@@ -252,10 +252,14 @@ var init_config = __esm(() => {
252
252
  WORKSPACE_DIR = resolveLocalStateDir("NEGOTIUM_WORKSPACE_DIR", "workspace");
253
253
  TOPIC_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "topics");
254
254
  SHARED_WIKI_DIR = resolve(WORKSPACE_DIR, "wiki");
255
- CONTEXTS_DIR = resolve(WORKSPACE_DIR, "contexts");
256
- BROWSER_PROFILES_DIR = resolve(WORKSPACE_DIR, "browser-profiles");
257
- DM_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "dm");
258
- SESSION_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "sessions");
255
+ CRON_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "cron");
256
+ BROWSER_DIR = resolveLocalStateDir("NEGOTIUM_BROWSER_DIR", "browser");
257
+ BROWSER_PROFILES_DIR = resolve(BROWSER_DIR, "profiles");
258
+ BINARIES_DIR = resolve(STATE_DIR, "binaries");
259
+ SECRETS_DIR = resolve(STATE_DIR, "secrets");
260
+ CONTEXTS_DIR = resolve(STATE_DIR, "data", "contexts");
261
+ DM_WORKSPACE_DIR = resolve(STATE_DIR, "data", "dm");
262
+ SESSION_WORKSPACE_DIR = resolve(STATE_DIR, "data", "sessions");
259
263
  CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
260
264
  CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;
261
265
  BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
@@ -284,10 +288,12 @@ var init_config = __esm(() => {
284
288
  hostname = process.env.HOSTNAME || "127.0.0.1";
285
289
  DATA_DIR = resolveLocalStateDir("NEGOTIUM_DATA_DIR", "data");
286
290
  LOG_DIR = resolveLocalStateDir("NEGOTIUM_LOG_DIR", "logs");
291
+ UPLOADS_DIR = resolve(DATA_DIR, "uploads");
292
+ VAULT_DIR = resolve(DATA_DIR, "vault");
287
293
  SESSIONS_DB = process.env.SESSIONS_DB_PATH ? resolve(process.env.SESSIONS_DB_PATH) : resolve(DATA_DIR, "sessions.db");
288
294
  DEBUG_FILE = resolve(DATA_DIR, "debug-users.json");
289
295
  USERS_LOG_DIR = resolve(DATA_DIR, "users");
290
- RUN_DIR = resolveLocalStateDir("NEGOTIUM_RUN_DIR", "run");
296
+ RUN_DIR = resolveLocalStateDir("NEGOTIUM_RUN_DIR", "runtime");
291
297
  PROGRESS_DIR = resolve(RUN_DIR, "progress");
292
298
  DM_CMD_DIR = resolve(RUN_DIR, "dm-commands");
293
299
  DM_RESP_DIR = resolve(RUN_DIR, "dm-responses");
@@ -298,6 +304,8 @@ var init_config = __esm(() => {
298
304
  PLAYWRIGHT_PORTS_DIR = resolve(RUN_DIR, "playwright-ports");
299
305
  mkdirSync(STATE_DIR, { recursive: true });
300
306
  mkdirSync(DATA_DIR, { recursive: true });
307
+ mkdirSync(UPLOADS_DIR, { recursive: true });
308
+ mkdirSync(VAULT_DIR, { recursive: true, mode: 448 });
301
309
  mkdirSync(LOG_DIR, { recursive: true });
302
310
  mkdirSync(PROGRESS_DIR, { recursive: true });
303
311
  mkdirSync(DM_CMD_DIR, { recursive: true });
@@ -308,10 +316,14 @@ var init_config = __esm(() => {
308
316
  mkdirSync(WORKSPACE_DIR, { recursive: true });
309
317
  mkdirSync(TOPIC_WORKSPACE_DIR, { recursive: true });
310
318
  mkdirSync(SHARED_WIKI_DIR, { recursive: true });
319
+ mkdirSync(CRON_WORKSPACE_DIR, { recursive: true });
311
320
  mkdirSync(CONTEXTS_DIR, { recursive: true });
312
- mkdirSync(BROWSER_PROFILES_DIR, { recursive: true });
313
321
  mkdirSync(DM_WORKSPACE_DIR, { recursive: true });
314
322
  mkdirSync(SESSION_WORKSPACE_DIR, { recursive: true });
323
+ mkdirSync(BROWSER_DIR, { recursive: true });
324
+ mkdirSync(BROWSER_PROFILES_DIR, { recursive: true });
325
+ mkdirSync(BINARIES_DIR, { recursive: true });
326
+ mkdirSync(SECRETS_DIR, { recursive: true, mode: 448 });
315
327
  ACTIVE_QUERY_STALE_MS = 10 * 60 * 1000;
316
328
  AGENTS_PROMPTS_DIR = resolve(PROJECT_ROOT, "src/prompts/agents");
317
329
  RESOURCES_DIR = resolve(PROJECT_ROOT, "src/resources");
@@ -530,7 +542,12 @@ var init_shared = __esm(() => {
530
542
  init_config();
531
543
  init_logger();
532
544
  __dirname2 = dirname3(fileURLToPath2(import.meta.url));
533
- trustedWorkspaceRoots = [WORKSPACE_DIR, DM_WORKSPACE_DIR, TOPIC_WORKSPACE_DIR].map((root) => resolve3(root));
545
+ trustedWorkspaceRoots = [
546
+ WORKSPACE_DIR,
547
+ DM_WORKSPACE_DIR,
548
+ SESSION_WORKSPACE_DIR,
549
+ TOPIC_WORKSPACE_DIR
550
+ ].map((root) => resolve3(root));
534
551
  FIXTURES_DIR = join3(__dirname2, "..", "fixtures");
535
552
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
536
553
  });
@@ -583,6 +600,13 @@ function parseJsonlText(raw) {
583
600
  return raw.trim().split(`
584
601
  `).filter(Boolean).map((line) => JSON.parse(line));
585
602
  }
603
+ function readJsonFile(filePath) {
604
+ try {
605
+ return JSON.parse(readFileSync3(filePath, "utf-8"));
606
+ } catch {
607
+ return null;
608
+ }
609
+ }
586
610
  function lockStaleMs() {
587
611
  const raw = Number.parseInt(process.env.NEGOTIUM_JSONL_LOCK_STALE_MS ?? "", 10);
588
612
  return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_LOCK_STALE_MS;
@@ -1486,7 +1510,7 @@ function defaultWorkspaceDir() {
1486
1510
  return envPath("NEGOTIUM_WORKSPACE_DIR", join7(defaultStateDir(), "workspace"));
1487
1511
  }
1488
1512
  function defaultSessionAsksDir() {
1489
- const runDir = envPath("NEGOTIUM_RUN_DIR", join7(defaultStateDir(), "run"));
1513
+ const runDir = envPath("NEGOTIUM_RUN_DIR", join7(defaultStateDir(), "runtime"));
1490
1514
  return join7(runDir, "session-asks");
1491
1515
  }
1492
1516
  function defaultSessionsDatabasePath() {
@@ -1592,15 +1616,8 @@ import {
1592
1616
  writeFileSync as writeFileSync5
1593
1617
  } from "fs";
1594
1618
  import { dirname as dirname7, join as join8 } from "path";
1595
- function safeUserIdComponent(userId) {
1596
- const str = String(userId);
1597
- if (!str || /[/\\]|\.\./.test(str)) {
1598
- throw new Error(`conversations: refusing unsafe userId path component: ${str}`);
1599
- }
1600
- return str;
1601
- }
1602
- function conversationDir(userId) {
1603
- return join8(resolveStorageDataDir(), "conversations", safeUserIdComponent(userId));
1619
+ function conversationDir(_userId) {
1620
+ return join8(resolveStorageDataDir(), "conversations");
1604
1621
  }
1605
1622
  function topicFilename(topicName) {
1606
1623
  const t = sanitizeTopicName(topicName, true);
@@ -1838,7 +1855,7 @@ function writeRollout(options) {
1838
1855
  version: 1,
1839
1856
  cwd: options.cwd,
1840
1857
  createdAt: existingCreatedAt(path) ?? new Date().toISOString(),
1841
- sdkVersion: "0.1.53"
1858
+ sdkVersion: "0.1.54"
1842
1859
  }
1843
1860
  },
1844
1861
  ...messages
@@ -3498,7 +3515,7 @@ var init_registry = __esm(async () => {
3498
3515
  });
3499
3516
 
3500
3517
  // ../../packages/core/src/agents/fork.ts
3501
- import { existsSync as existsSync9, unlinkSync as unlinkSync8 } from "fs";
3518
+ import { existsSync as existsSync10, unlinkSync as unlinkSync8 } from "fs";
3502
3519
  function createAgentForkHelpers(host) {
3503
3520
  return {
3504
3521
  async forkAgentSession(options) {
@@ -3530,7 +3547,7 @@ var init_fork = __esm(async () => {
3530
3547
  await init_registry();
3531
3548
  defaultForkHelpers = createAgentForkHelpers({
3532
3549
  forkSession: (agent, options) => getRegistryOperations(agent).forkSession(options),
3533
- exists: existsSync9,
3550
+ exists: existsSync10,
3534
3551
  unlink: unlinkSync8,
3535
3552
  warn: (details, message) => logger.warn(details, message)
3536
3553
  });
@@ -3923,7 +3940,7 @@ var init_runtime_process_leases = __esm(async () => {
3923
3940
 
3924
3941
  // ../../packages/core/src/platform/playwright/browser-processes.ts
3925
3942
  import { execFileSync as execFileSync2 } from "child_process";
3926
- import { readdirSync as readdirSync3, unlinkSync as unlinkSync9 } from "fs";
3943
+ import { readdirSync as readdirSync4, unlinkSync as unlinkSync9 } from "fs";
3927
3944
  import { createServer } from "net";
3928
3945
  import { resolve as resolve7, sep } from "path";
3929
3946
  function isPortInUse(port) {
@@ -4048,7 +4065,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
4048
4065
  }
4049
4066
  function cleanSingletonFiles(userDataDir) {
4050
4067
  try {
4051
- const files = readdirSync3(userDataDir);
4068
+ const files = readdirSync4(userDataDir);
4052
4069
  for (const f of files) {
4053
4070
  if (f.startsWith("Singleton")) {
4054
4071
  try {
@@ -4239,7 +4256,7 @@ var init_vault_crypto = __esm(() => {
4239
4256
 
4240
4257
  // ../../packages/core/src/storage/vault.ts
4241
4258
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync9 } from "fs";
4242
- import { join as join11 } from "path";
4259
+ import { join as join12 } from "path";
4243
4260
  function initializeVaultDatabase(database) {
4244
4261
  database.exec("PRAGMA journal_mode = WAL");
4245
4262
  database.exec("PRAGMA busy_timeout = 5000");
@@ -4274,8 +4291,9 @@ function initializeVaultDatabase(database) {
4274
4291
  }
4275
4292
  }
4276
4293
  function openVaultDatabase(dataDir) {
4277
- const path = join11(dataDir, "vault.db");
4278
- mkdirSync9(dataDir, { recursive: true });
4294
+ const vaultDir = join12(dataDir, "vault");
4295
+ const path = join12(vaultDir, "vault.db");
4296
+ mkdirSync9(vaultDir, { recursive: true, mode: 448 });
4279
4297
  const database = new Database(path, { create: true });
4280
4298
  chmodSync2(path, 384);
4281
4299
  initializeVaultDatabase(database);
@@ -4381,7 +4399,7 @@ import { randomBytes as randomBytes4, timingSafeEqual } from "crypto";
4381
4399
  import { chmodSync as chmodSync3 } from "fs";
4382
4400
  import { createServer as createServer2 } from "net";
4383
4401
  import { tmpdir } from "os";
4384
- import { join as join12 } from "path";
4402
+ import { join as join13 } from "path";
4385
4403
  function deepMapStrings(value2, transform) {
4386
4404
  if (typeof value2 === "string")
4387
4405
  return transform(value2);
@@ -4468,7 +4486,7 @@ function authorized(actual, expected) {
4468
4486
  }
4469
4487
  async function createBrowserVaultBroker(userId) {
4470
4488
  const token = randomBytes4(32).toString("hex");
4471
- const socketPath = join12(process.platform === "win32" ? tmpdir() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes4(8).toString("hex")}.sock`);
4489
+ const socketPath = join13(process.platform === "win32" ? tmpdir() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes4(8).toString("hex")}.sock`);
4472
4490
  const retainedForms = new Map;
4473
4491
  const leases = new Map;
4474
4492
  const sockets = new Set;
@@ -4628,7 +4646,7 @@ function createBrowserProfile(ownerId, rawName) {
4628
4646
  const name = normalizeBrowserProfileName(rawName);
4629
4647
  if (name === DEFAULT_BROWSER_PROFILE)
4630
4648
  return name;
4631
- db.query("INSERT OR IGNORE INTO browser_profiles (owner_id, name, created_at) VALUES (?, ?, ?)").run(ownerId, name, new Date().toISOString());
4649
+ db.query("INSERT OR IGNORE INTO browser_profiles (owner_id, name, created_at) VALUES (?, ?, ?)").run("local", name, new Date().toISOString());
4632
4650
  return name;
4633
4651
  }
4634
4652
  function assignTopicBrowserProfile(opts) {
@@ -4661,19 +4679,18 @@ var init_browser_profiles = __esm(async () => {
4661
4679
  });
4662
4680
 
4663
4681
  // ../../packages/core/src/platform/playwright/manager.ts
4664
- import { execFileSync as execFileSync3, spawn as spawn2 } from "child_process";
4682
+ import { execFileSync as execFileSync3, spawn as spawn3 } from "child_process";
4665
4683
  import { createHash as createHash2, randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "crypto";
4666
4684
  import {
4667
4685
  cpSync,
4668
- existsSync as existsSync10,
4686
+ existsSync as existsSync11,
4669
4687
  mkdirSync as mkdirSync10,
4670
- readFileSync as readFileSync8,
4671
- renameSync as renameSync5,
4688
+ readFileSync as readFileSync9,
4672
4689
  rmSync,
4673
4690
  unlinkSync as unlinkSync10,
4674
4691
  writeFileSync as writeFileSync7
4675
4692
  } from "fs";
4676
- import { dirname as dirname8, join as join13, resolve as resolve9 } from "path";
4693
+ import { dirname as dirname8, join as join14, resolve as resolve9 } from "path";
4677
4694
  function makeInstanceKey(userId, topic) {
4678
4695
  return resolvePlaywrightTopicBinding(userId, topic).instanceKey;
4679
4696
  }
@@ -4684,7 +4701,7 @@ function defaultTopicBinding(userId, topic) {
4684
4701
  if (!topic)
4685
4702
  return managerHost.resolveNamedBinding(userId, "default");
4686
4703
  const ownerId = getBrowserProfileOwner(topic, userId);
4687
- const profile = migrateLegacyTopicProfile(ownerId, topic);
4704
+ const profile = getTopicBrowserProfile(topic);
4688
4705
  return managerHost.resolveNamedBinding(ownerId, profile);
4689
4706
  }
4690
4707
  function resolvePlaywrightProfileBinding(ownerId, rawProfile) {
@@ -4696,34 +4713,15 @@ function validateProfileBinding(binding) {
4696
4713
  }
4697
4714
  return Object.freeze({ ...binding });
4698
4715
  }
4699
- function defaultNamedBinding(ownerId, rawProfile) {
4716
+ function defaultNamedBinding(_ownerId, rawProfile) {
4700
4717
  const profile = normalizeBrowserProfileName(rawProfile);
4701
- const instanceKey = `profile:${encodeURIComponent(ownerId)}:${profile}`;
4718
+ const instanceKey = `profile:${profile}`;
4702
4719
  return {
4703
4720
  instanceKey,
4704
- ownerId,
4721
+ ownerId: "local",
4705
4722
  profile
4706
4723
  };
4707
4724
  }
4708
- function legacyBrowserProfileName(topic) {
4709
- return `legacy_${createHash2("sha256").update(topic).digest("hex").slice(0, 12)}`;
4710
- }
4711
- function migrateLegacyTopicProfile(ownerId, topic) {
4712
- const current2 = getTopicBrowserProfile(topic);
4713
- if (current2 !== "default" || !hasBrowserProfileTopic(topic))
4714
- return current2;
4715
- const legacyDir = resolve9(BROWSER_PROFILES_DIR, sanitizeTopicName(topic));
4716
- if (!existsSync10(legacyDir))
4717
- return current2;
4718
- const profile = legacyBrowserProfileName(topic);
4719
- const profileDir = defaultProfileDir(ownerId, profile);
4720
- mkdirSync10(dirname8(profileDir), { recursive: true });
4721
- if (!existsSync10(profileDir))
4722
- renameSync5(legacyDir, profileDir);
4723
- assignTopicBrowserProfile({ topicId: topic, actorUserId: ownerId, profile });
4724
- logger.info({ ownerId, topic, profile }, "Adopted legacy topic browser profile");
4725
- return profile;
4726
- }
4727
4725
  function defaultChildEnvironment(context) {
4728
4726
  const childEnvironment = {};
4729
4727
  for (const key of [
@@ -4754,6 +4752,10 @@ function defaultChildEnvironment(context) {
4754
4752
  return childEnvironment;
4755
4753
  }
4756
4754
  function parseInstanceKey(instanceKey) {
4755
+ const shared = /^profile:(.+)$/.exec(instanceKey);
4756
+ if (shared && !shared[1].includes(":")) {
4757
+ return { ownerId: "local", profile: normalizeBrowserProfileName(shared[1]) };
4758
+ }
4757
4759
  const match = /^profile:([^:]+):(.+)$/.exec(instanceKey);
4758
4760
  if (!match)
4759
4761
  return { ownerId: "legacy", profile: sanitizeTopicName(instanceKey) };
@@ -4768,14 +4770,14 @@ function portFileName(instanceKey) {
4768
4770
  function writePortFile(instanceKey, port) {
4769
4771
  try {
4770
4772
  mkdirSync10(managerHost.portsDir, { recursive: true });
4771
- writeFileSync7(join13(managerHost.portsDir, portFileName(instanceKey)), String(port));
4773
+ writeFileSync7(join14(managerHost.portsDir, portFileName(instanceKey)), String(port));
4772
4774
  } catch (e) {
4773
4775
  logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
4774
4776
  }
4775
4777
  }
4776
4778
  function deletePortFile(instanceKey) {
4777
4779
  try {
4778
- unlinkSync10(join13(managerHost.portsDir, portFileName(instanceKey)));
4780
+ unlinkSync10(join14(managerHost.portsDir, portFileName(instanceKey)));
4779
4781
  } catch (e) {
4780
4782
  logger.warn({ err: e, instanceKey }, "Failed to delete playwright port file");
4781
4783
  }
@@ -4896,12 +4898,8 @@ async function allocatePort() {
4896
4898
  function releasePort(port) {
4897
4899
  usedPorts.delete(port);
4898
4900
  }
4899
- function ownerDirectory(ownerId) {
4900
- const digest = createHash2("sha256").update(ownerId).digest("hex").slice(0, 16);
4901
- return `${sanitizeTopicName(ownerId).slice(0, 24)}_${digest}`;
4902
- }
4903
- function defaultProfileDir(ownerId, profile) {
4904
- return resolve9(BROWSER_PROFILES_DIR, "profiles", ownerDirectory(ownerId), profile);
4901
+ function defaultProfileDir(_ownerId, profile) {
4902
+ return resolve9(BROWSER_PROFILES_DIR, profile);
4905
4903
  }
4906
4904
  function resolveUserDataDir(instanceKey) {
4907
4905
  return managerHost.resolveInstanceDataDir(instanceKey);
@@ -5029,7 +5027,7 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
5029
5027
  }
5030
5028
  let proc;
5031
5029
  try {
5032
- proc = spawn2(spawnSpec.command, spawnSpec.args, {
5030
+ proc = spawn3(spawnSpec.command, spawnSpec.args, {
5033
5031
  stdio: ["ignore", "ignore", "pipe"],
5034
5032
  detached: false,
5035
5033
  env: childEnv
@@ -5262,11 +5260,11 @@ async function cloneProfileForChild(opts) {
5262
5260
  killInstance(dstKey);
5263
5261
  await delay(500);
5264
5262
  }
5265
- if (!existsSync10(srcDir)) {
5263
+ if (!existsSync11(srcDir)) {
5266
5264
  return { copied: false, srcDir, dstDir, reason: "src-missing" };
5267
5265
  }
5268
5266
  try {
5269
- if (existsSync10(dstDir)) {
5267
+ if (existsSync11(dstDir)) {
5270
5268
  rmSync(dstDir, { recursive: true, force: true });
5271
5269
  }
5272
5270
  mkdirSync10(dirname8(dstDir), { recursive: true });
@@ -5945,15 +5943,8 @@ RULES:
5945
5943
  - Keep the entire summary under 1500 words.`, CHARS_PER_TEXT_TOKEN = 3.5, CHARS_PER_CJK_TOKEN = 0.9;
5946
5944
 
5947
5945
  // ../../packages/core/src/storage/tasks.ts
5948
- import { existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync9, renameSync as renameSync6, statSync as statSync4, writeFileSync as writeFileSync8 } from "fs";
5949
- import { dirname as dirname9, join as join14 } from "path";
5950
- function safeUserIdComponent2(userId) {
5951
- const str = String(userId);
5952
- if (!str || /[/\\]|\.\./.test(str)) {
5953
- throw new Error(`tasks: refusing unsafe userId path component: ${str}`);
5954
- }
5955
- return str;
5956
- }
5946
+ import { existsSync as existsSync12, mkdirSync as mkdirSync11, readFileSync as readFileSync10, renameSync as renameSync5, statSync as statSync4, writeFileSync as writeFileSync8 } from "fs";
5947
+ import { dirname as dirname9, join as join15 } from "path";
5957
5948
  function safeTaskScopeKey(scopeKey) {
5958
5949
  const safe = sanitizeFileName(scopeKey);
5959
5950
  if (!safe || safe === "." || safe === "..") {
@@ -5965,14 +5956,14 @@ function taskScopeKey(opts) {
5965
5956
  return opts.topicId?.trim() || opts.session || "default";
5966
5957
  }
5967
5958
  function getTaskFilePath(userId, scopeKey) {
5968
- return join14(resolveStorageDataDir(), "tasks", safeUserIdComponent2(userId), `${safeTaskScopeKey(scopeKey)}.json`);
5959
+ return join15(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
5969
5960
  }
5970
5961
  function readTasks(userId, scopeKey) {
5971
5962
  const path = getTaskFilePath(userId, scopeKey);
5972
- if (!existsSync11(path))
5963
+ if (!existsSync12(path))
5973
5964
  return [];
5974
5965
  try {
5975
- const parsed = JSON.parse(readFileSync9(path, "utf-8"));
5966
+ const parsed = JSON.parse(readFileSync10(path, "utf-8"));
5976
5967
  return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
5977
5968
  } catch (e) {
5978
5969
  logger.warn({ err: e, path }, "tasks: failed to read task store");
@@ -5985,7 +5976,7 @@ function writeTasks(userId, scopeKey, tasks) {
5985
5976
  const payload = { version: 1, tasks };
5986
5977
  const tmp = `${path}.${process.pid}.tmp`;
5987
5978
  writeFileSync8(tmp, JSON.stringify(payload, null, 2), "utf-8");
5988
- renameSync6(tmp, path);
5979
+ renameSync5(tmp, path);
5989
5980
  }
5990
5981
  function taskFileMtimeNs(userId, scopeKey) {
5991
5982
  try {
@@ -6141,14 +6132,14 @@ function deepMapStrings2(value2, fn) {
6141
6132
  }
6142
6133
 
6143
6134
  // ../../packages/core/src/security/sensitive-path.ts
6144
- import { realpathSync as realpathSync2 } from "fs";
6135
+ import { realpathSync as realpathSync3 } from "fs";
6145
6136
  import { resolve as resolve10 } from "path";
6146
6137
  function isSensitivePath(filePath) {
6147
6138
  const normalized = resolve10(filePath);
6148
6139
  if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
6149
6140
  return true;
6150
6141
  try {
6151
- const real = realpathSync2(normalized);
6142
+ const real = realpathSync3(normalized);
6152
6143
  if (real !== normalized)
6153
6144
  return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
6154
6145
  } catch {}
@@ -6331,7 +6322,7 @@ function deriveBgBashContextCapability(runtimeCapability, userId, topic) {
6331
6322
  var init_context = () => {};
6332
6323
 
6333
6324
  // ../../packages/core/src/platform/background-bash/manager.ts
6334
- import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
6325
+ import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
6335
6326
  import { randomBytes as randomBytes6 } from "crypto";
6336
6327
  function makeBgBashKey(_userId, _topic) {
6337
6328
  return "runtime";
@@ -6358,7 +6349,7 @@ function createBackgroundBashManager(options = {}) {
6358
6349
  const now = options.now ?? Date.now;
6359
6350
  const wait = options.delay ?? delay;
6360
6351
  const portPids = options.portPids ?? defaultPortPids;
6361
- const spawnImpl = options.spawn ?? ((command, args, spawnOptions) => spawn3(command, [...args], spawnOptions));
6352
+ const spawnImpl = options.spawn ?? ((command, args, spawnOptions) => spawn4(command, [...args], spawnOptions));
6362
6353
  function contextKey(userId, topic) {
6363
6354
  return `${userId}\x00${topic}`;
6364
6355
  }
@@ -6809,7 +6800,7 @@ function getMcpServersForQuery(opts) {
6809
6800
  if (!opts.topicId)
6810
6801
  throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
6811
6802
  return getCronMcpServers({
6812
- userId: opts.userId || "default",
6803
+ userId: opts.userId || "local",
6813
6804
  session: opts.session || "cron",
6814
6805
  topicId: opts.topicId,
6815
6806
  queryId: opts.queryId,
@@ -6826,7 +6817,7 @@ function getMcpServersForQuery(opts) {
6826
6817
  }
6827
6818
  if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
6828
6819
  return getDmMcpServers({
6829
- userId: opts.userId || "default",
6820
+ userId: opts.userId || "local",
6830
6821
  agent: opts.agent,
6831
6822
  playwrightPort: opts.playwrightPort,
6832
6823
  playwrightCapability: opts.playwrightCapability
@@ -6834,7 +6825,7 @@ function getMcpServersForQuery(opts) {
6834
6825
  }
6835
6826
  if (opts.sessionType === "manager") {
6836
6827
  return getManagerMcpServers({
6837
- userId: opts.userId || "default",
6828
+ userId: opts.userId || "local",
6838
6829
  session: opts.session,
6839
6830
  topicId: opts.topicId,
6840
6831
  queryId: opts.queryId,
@@ -6851,7 +6842,7 @@ function getMcpServersForQuery(opts) {
6851
6842
  });
6852
6843
  }
6853
6844
  return getForumMcpServers({
6854
- userId: opts.userId || "default",
6845
+ userId: opts.userId || "local",
6855
6846
  session: opts.session || "default",
6856
6847
  topicId: opts.topicId,
6857
6848
  subagentParentTopicId: opts.subagentParentTopicId,
@@ -7209,8 +7200,8 @@ __export(exports_claude_provider, {
7209
7200
  claudeBuiltInTools: () => claudeBuiltInTools,
7210
7201
  buildClaudeDisallowedTools: () => buildClaudeDisallowedTools
7211
7202
  });
7212
- import { spawn as spawn4 } from "child_process";
7213
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
7203
+ import { spawn as spawn5 } from "child_process";
7204
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
7214
7205
  import { query } from "@anthropic-ai/claude-agent-sdk";
7215
7206
  function claudeBuiltInTools(opts) {
7216
7207
  return opts.toolPolicy ? [] : undefined;
@@ -7263,7 +7254,7 @@ function buildClaudePrompt(opts) {
7263
7254
  source: {
7264
7255
  type: "base64",
7265
7256
  media_type: attachment.mimeType,
7266
- data: readFileSync10(attachment.path).toString("base64")
7257
+ data: readFileSync11(attachment.path).toString("base64")
7267
7258
  }
7268
7259
  });
7269
7260
  } catch (err) {
@@ -7286,7 +7277,7 @@ function buildClaudePrompt(opts) {
7286
7277
  parent_tool_use_id: null
7287
7278
  });
7288
7279
  }
7289
- function signalProcessTree(pid, signal) {
7280
+ function signalProcessTree2(pid, signal) {
7290
7281
  try {
7291
7282
  process.kill(-pid, signal);
7292
7283
  return true;
@@ -7301,7 +7292,7 @@ function signalProcessTree(pid, signal) {
7301
7292
  }
7302
7293
  }
7303
7294
  function spawnClaudeCodeProcessWithTreeKill(options) {
7304
- const child = spawn4(options.command, options.args, {
7295
+ const child = spawn5(options.command, options.args, {
7305
7296
  cwd: options.cwd,
7306
7297
  detached: true,
7307
7298
  env: options.env,
@@ -7321,13 +7312,13 @@ function spawnClaudeCodeProcessWithTreeKill(options) {
7321
7312
  const kill = (signal) => {
7322
7313
  if (!child.pid)
7323
7314
  return child.kill(signal);
7324
- const ok = signalProcessTree(child.pid, signal);
7315
+ const ok = signalProcessTree2(child.pid, signal);
7325
7316
  if (signal !== "SIGKILL") {
7326
7317
  clearKillTimer();
7327
7318
  killTimer = setTimeout(() => {
7328
7319
  if (!exited && child.pid) {
7329
7320
  logger.warn({ pid: child.pid }, "Claude process tree still alive after SIGTERM; SIGKILL");
7330
- signalProcessTree(child.pid, "SIGKILL");
7321
+ signalProcessTree2(child.pid, "SIGKILL");
7331
7322
  }
7332
7323
  }, CLAUDE_ABORT_SIGKILL_DELAY_MS);
7333
7324
  killTimer.unref?.();
@@ -7391,7 +7382,7 @@ function isAbortError(err) {
7391
7382
  }
7392
7383
  async function* claudeProvider(opts) {
7393
7384
  const claudeExecutable = hostedClaudeCodeExecutablePath();
7394
- if (claudeExecutable && !existsSync12(claudeExecutable)) {
7385
+ if (claudeExecutable && !existsSync13(claudeExecutable)) {
7395
7386
  yield {
7396
7387
  type: "error",
7397
7388
  content: `Claude CLI not found at ${claudeExecutable}. Install Claude Code or configure the executable path.`
@@ -7686,34 +7677,34 @@ var init_claude_provider = __esm(async () => {
7686
7677
  });
7687
7678
 
7688
7679
  // ../../packages/core/src/version.ts
7689
- var NEGOTIUM_VERSION = "0.1.48";
7680
+ var NEGOTIUM_VERSION = "0.2.0";
7690
7681
 
7691
7682
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
7692
- import { spawn as spawn5 } from "child_process";
7683
+ import { spawn as spawn6 } from "child_process";
7693
7684
  import { randomUUID as randomUUID8 } from "crypto";
7694
7685
  import {
7695
7686
  chmodSync as chmodSync4,
7696
7687
  copyFileSync,
7697
- existsSync as existsSync13,
7688
+ existsSync as existsSync14,
7698
7689
  mkdtempSync,
7699
- readFileSync as readFileSync11,
7700
- renameSync as renameSync7,
7690
+ readFileSync as readFileSync12,
7691
+ renameSync as renameSync6,
7701
7692
  rmSync as rmSync2,
7702
7693
  unlinkSync as unlinkSync11,
7703
7694
  writeFileSync as writeFileSync9
7704
7695
  } from "fs";
7705
7696
  import { createRequire as createRequire2 } from "module";
7706
7697
  import { tmpdir as tmpdir2 } from "os";
7707
- import { dirname as dirname10, join as join15 } from "path";
7698
+ import { dirname as dirname10, join as join16 } from "path";
7708
7699
  function readPackageVersion(packageJsonPath) {
7709
- const parsed = JSON.parse(readFileSync11(packageJsonPath, "utf8"));
7700
+ const parsed = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
7710
7701
  if (typeof parsed.version !== "string" || !parsed.version.trim()) {
7711
7702
  throw new Error(`Codex package has no valid version: ${packageJsonPath}`);
7712
7703
  }
7713
7704
  return parsed.version;
7714
7705
  }
7715
7706
  function codexCliScriptPath() {
7716
- return join15(dirname10(bundledCodexPackagePath), "bin", "codex.js");
7707
+ return join16(dirname10(bundledCodexPackagePath), "bin", "codex.js");
7717
7708
  }
7718
7709
  function parseCodexModelCache(contents, sourcePath) {
7719
7710
  let parsed;
@@ -7728,7 +7719,7 @@ function parseCodexModelCache(contents, sourcePath) {
7728
7719
  return parsed;
7729
7720
  }
7730
7721
  function readCodexModelCache(cachePath) {
7731
- const contents = readFileSync11(cachePath, "utf8");
7722
+ const contents = readFileSync12(cachePath, "utf8");
7732
7723
  return { contents, parsed: parseCodexModelCache(contents, cachePath) };
7733
7724
  }
7734
7725
  function readCompatibleCodexModelCache(cachePath) {
@@ -7740,12 +7731,12 @@ function readCompatibleCodexModelCache(cachePath) {
7740
7731
  return cache;
7741
7732
  }
7742
7733
  function writePrivateFileAtomic(path, contents) {
7743
- if (existsSync13(path) && readFileSync11(path, "utf8") === contents)
7734
+ if (existsSync14(path) && readFileSync12(path, "utf8") === contents)
7744
7735
  return;
7745
7736
  const tempPath = `${path}.${process.pid}.${randomUUID8()}.tmp`;
7746
7737
  try {
7747
7738
  writeFileSync9(tempPath, contents, { encoding: "utf8", mode: 384 });
7748
- renameSync7(tempPath, path);
7739
+ renameSync6(tempPath, path);
7749
7740
  chmodSync4(path, 384);
7750
7741
  } finally {
7751
7742
  try {
@@ -7754,10 +7745,10 @@ function writePrivateFileAtomic(path, contents) {
7754
7745
  }
7755
7746
  }
7756
7747
  function bundledCodexModelCachePath(authFilePath) {
7757
- return join15(dirname10(authFilePath), NEGOTIUM_MODEL_CACHE);
7748
+ return join16(dirname10(authFilePath), NEGOTIUM_MODEL_CACHE);
7758
7749
  }
7759
7750
  async function bootstrapCodexModelCache(codexHome, cachePath) {
7760
- const child = spawn5(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
7751
+ const child = spawn6(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
7761
7752
  env: { ...process.env, CODEX_HOME: codexHome },
7762
7753
  stdio: ["pipe", "pipe", "pipe"]
7763
7754
  });
@@ -7777,7 +7768,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
7777
7768
  } catch {}
7778
7769
  if (error)
7779
7770
  reject(error);
7780
- else if (!existsSync13(cachePath))
7771
+ else if (!existsSync14(cachePath))
7781
7772
  reject(new Error("Codex did not create its model cache"));
7782
7773
  else
7783
7774
  resolve11();
@@ -7840,15 +7831,15 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
7840
7831
  }
7841
7832
  async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
7842
7833
  const sourceHome = dirname10(authFilePath);
7843
- const isolatedHome = mkdtempSync(join15(tmpdir2(), "negotium-codex-models-"));
7844
- const isolatedCachePath = join15(isolatedHome, "models_cache.json");
7834
+ const isolatedHome = mkdtempSync(join16(tmpdir2(), "negotium-codex-models-"));
7835
+ const isolatedCachePath = join16(isolatedHome, "models_cache.json");
7845
7836
  try {
7846
- const isolatedAuthPath = join15(isolatedHome, "auth.json");
7837
+ const isolatedAuthPath = join16(isolatedHome, "auth.json");
7847
7838
  copyFileSync(authFilePath, isolatedAuthPath);
7848
7839
  chmodSync4(isolatedAuthPath, 384);
7849
- const sourceConfigPath = join15(sourceHome, "config.toml");
7850
- if (existsSync13(sourceConfigPath)) {
7851
- const isolatedConfigPath = join15(isolatedHome, "config.toml");
7840
+ const sourceConfigPath = join16(sourceHome, "config.toml");
7841
+ if (existsSync14(sourceConfigPath)) {
7842
+ const isolatedConfigPath = join16(isolatedHome, "config.toml");
7852
7843
  copyFileSync(sourceConfigPath, isolatedConfigPath);
7853
7844
  chmodSync4(isolatedConfigPath, 384);
7854
7845
  }
@@ -7862,22 +7853,22 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
7862
7853
  const codexHome = dirname10(authFilePath);
7863
7854
  const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
7864
7855
  if (configuredCachePath) {
7865
- if (!existsSync13(configuredCachePath)) {
7856
+ if (!existsSync14(configuredCachePath)) {
7866
7857
  throw new Error(`Configured Codex model cache does not exist: ${configuredCachePath}`);
7867
7858
  }
7868
7859
  readCompatibleCodexModelCache(configuredCachePath);
7869
7860
  return configuredCachePath;
7870
7861
  }
7871
7862
  const bundledCachePath = bundledCodexModelCachePath(authFilePath);
7872
- const sharedCachePath = join15(codexHome, "models_cache.json");
7873
- if (existsSync13(sharedCachePath)) {
7863
+ const sharedCachePath = join16(codexHome, "models_cache.json");
7864
+ if (existsSync14(sharedCachePath)) {
7874
7865
  try {
7875
7866
  const shared = readCompatibleCodexModelCache(sharedCachePath);
7876
7867
  writePrivateFileAtomic(bundledCachePath, shared.contents);
7877
7868
  return bundledCachePath;
7878
7869
  } catch {}
7879
7870
  }
7880
- if (existsSync13(bundledCachePath)) {
7871
+ if (existsSync14(bundledCachePath)) {
7881
7872
  try {
7882
7873
  readCompatibleCodexModelCache(bundledCachePath);
7883
7874
  return bundledCachePath;
@@ -7889,7 +7880,7 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
7889
7880
  }
7890
7881
  function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
7891
7882
  const codexHome = dirname10(authFilePath);
7892
- const outputPath = join15(codexHome, NEGOTIUM_MODEL_CATALOG);
7883
+ const outputPath = join16(codexHome, NEGOTIUM_MODEL_CATALOG);
7893
7884
  const parsed = readCodexModelCache(sourcePath).parsed;
7894
7885
  const models = parsed.models.map((model, index) => {
7895
7886
  if (!model || typeof model !== "object" || Array.isArray(model)) {
@@ -8136,7 +8127,7 @@ var init_codex_tree_kill = __esm(() => {
8136
8127
  });
8137
8128
 
8138
8129
  // ../../packages/core/src/agents/tool-format.ts
8139
- import { readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8130
+ import { readFileSync as readFileSync13, statSync as statSync5 } from "fs";
8140
8131
  import { isAbsolute as isAbsolute2, resolve as resolve11 } from "path";
8141
8132
  import { diffLines as computeLineDiff } from "diff";
8142
8133
  function summarizeDisplayText(value2) {
@@ -8465,7 +8456,7 @@ function sourceStartLine(input, before, after, cwd) {
8465
8456
  try {
8466
8457
  if (statSync5(path).size > TOOL_DIFF_SOURCE_MAX_BYTES)
8467
8458
  return 1;
8468
- const source = readFileSync12(path, "utf8").replace(/\r\n?/g, `
8459
+ const source = readFileSync13(path, "utf8").replace(/\r\n?/g, `
8469
8460
  `);
8470
8461
  for (const candidate of [before, after].filter(Boolean)) {
8471
8462
  const index = source.indexOf(candidate);
@@ -8658,8 +8649,8 @@ __export(exports_codex_provider, {
8658
8649
  codexProvider: () => codexProvider
8659
8650
  });
8660
8651
  import { execFileSync as execFileSync6 } from "child_process";
8661
- import { existsSync as existsSync14, readFileSync as readFileSync13, realpathSync as realpathSync3, statSync as statSync6 } from "fs";
8662
- import { dirname as dirname11, isAbsolute as isAbsolute3, join as join16, relative, resolve as resolve12 } from "path";
8652
+ import { existsSync as existsSync15, readFileSync as readFileSync14, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
8653
+ import { dirname as dirname11, isAbsolute as isAbsolute3, join as join17, relative, resolve as resolve12 } from "path";
8663
8654
  import { Codex } from "@openai/codex-sdk";
8664
8655
  function mapEffort(effort) {
8665
8656
  if (!effort)
@@ -8673,12 +8664,12 @@ function codexMcpServerName(name) {
8673
8664
  return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
8674
8665
  }
8675
8666
  function globalCodexMcpServerNames(authFilePath) {
8676
- const configPath = join16(dirname11(authFilePath), "config.toml");
8677
- if (!existsSync14(configPath))
8667
+ const configPath = join17(dirname11(authFilePath), "config.toml");
8668
+ if (!existsSync15(configPath))
8678
8669
  return [];
8679
8670
  try {
8680
8671
  const names = new Set;
8681
- for (const line of readFileSync13(configPath, "utf8").split(`
8672
+ for (const line of readFileSync14(configPath, "utf8").split(`
8682
8673
  `)) {
8683
8674
  const match = line.match(/^\s*\[\s*mcp_servers\.((?:"(?:\\.|[^"])*")|(?:'[^']*')|[^.\]\s]+)(?:[.\]])/);
8684
8675
  const raw = match?.[1];
@@ -8769,19 +8760,19 @@ function summarizeMcpToolCallResult(item) {
8769
8760
  }
8770
8761
  function canonicalPath(path) {
8771
8762
  try {
8772
- return realpathSync3(resolve12(path));
8763
+ return realpathSync4(resolve12(path));
8773
8764
  } catch {
8774
8765
  return resolve12(path);
8775
8766
  }
8776
8767
  }
8777
8768
  function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
8778
- if (!existsSync14(path))
8769
+ if (!existsSync15(path))
8779
8770
  return null;
8780
8771
  try {
8781
8772
  const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
8782
8773
  if (statSync6(path).size > byteLimit)
8783
8774
  return;
8784
- const content = readFileSync13(path);
8775
+ const content = readFileSync14(path);
8785
8776
  if (content.byteLength > byteLimit || content.includes(0))
8786
8777
  return;
8787
8778
  return content.toString("utf8");
@@ -8987,7 +8978,7 @@ async function* codexProvider(opts) {
8987
8978
  const filePreviews = new CodexFilePreviewTracker(opts.cwd);
8988
8979
  const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
8989
8980
  const codexAuthPath = hostedCodexAuthFilePath();
8990
- if (!existsSync14(codexAuthPath)) {
8981
+ if (!existsSync15(codexAuthPath)) {
8991
8982
  yield {
8992
8983
  type: "error",
8993
8984
  content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
@@ -9026,7 +9017,7 @@ async function* codexProvider(opts) {
9026
9017
  model: opts.model ?? "(sdk default)",
9027
9018
  effort: opts.effort ?? "(off)",
9028
9019
  cwd: opts.cwd,
9029
- cwdExists: existsSync14(opts.cwd),
9020
+ cwdExists: existsSync15(opts.cwd),
9030
9021
  resume: Boolean(opts.sessionId),
9031
9022
  mcpServerCount: Object.keys(codexMcpServers).length
9032
9023
  }, "codexProvider: starting turn");
@@ -9467,9 +9458,9 @@ var init_maestro_provider = __esm(async () => {
9467
9458
  });
9468
9459
 
9469
9460
  // ../../packages/core/src/agents/index.ts
9470
- import { existsSync as existsSync15 } from "fs";
9461
+ import { existsSync as existsSync16 } from "fs";
9471
9462
  import { homedir as homedir8 } from "os";
9472
- import { join as join17 } from "path";
9463
+ import { join as join18 } from "path";
9473
9464
  async function* dispatchAgent(opts) {
9474
9465
  switch (opts.agent) {
9475
9466
  case "claude": {
@@ -9503,11 +9494,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
9503
9494
  switch (agent) {
9504
9495
  case "claude": {
9505
9496
  const encodedCwd = encodeClaudeCwd(cwd);
9506
- const path = join17(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
9507
- return !existsSync15(path);
9497
+ const path = join18(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
9498
+ return !existsSync16(path);
9508
9499
  }
9509
9500
  case "codex": {
9510
- const sessionsDir = join17(process.env.CODEX_HOME || join17(homedir8(), ".codex"), "sessions");
9501
+ const sessionsDir = join18(process.env.CODEX_HOME || join18(homedir8(), ".codex"), "sessions");
9511
9502
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
9512
9503
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
9513
9504
  return false;
@@ -9516,7 +9507,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
9516
9507
  }
9517
9508
  case "maestro": {
9518
9509
  const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
9519
- return !existsSync15(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
9510
+ return !existsSync16(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
9520
9511
  }
9521
9512
  default: {
9522
9513
  const _exhaustive = agent;
@@ -9582,10 +9573,10 @@ var init_agents = __esm(async () => {
9582
9573
  });
9583
9574
 
9584
9575
  // ../../packages/core/src/prompts/builders.ts
9585
- import { readFileSync as readFileSync14 } from "fs";
9576
+ import { readFileSync as readFileSync15 } from "fs";
9586
9577
  import { resolve as resolve14 } from "path";
9587
9578
  function loadPrompt(filename, dir = SESSIONS_DIR) {
9588
- const raw = readFileSync14(resolve14(dir, filename), "utf-8");
9579
+ const raw = readFileSync15(resolve14(dir, filename), "utf-8");
9589
9580
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
9590
9581
  }
9591
9582
  function replaceVars(template, vars) {
@@ -9634,7 +9625,7 @@ function visualDesignGuide() {
9634
9625
  return _visualDesignGuide;
9635
9626
  }
9636
9627
  function loadAgentPrompt(filename) {
9637
- const raw = readFileSync14(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
9628
+ const raw = readFileSync15(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
9638
9629
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
9639
9630
  if (!match)
9640
9631
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -10023,9 +10014,9 @@ var init_api_topic_brief = __esm(async () => {
10023
10014
  });
10024
10015
 
10025
10016
  // ../../packages/core/src/storage/wiki.ts
10026
- import { basename as basename3, dirname as dirname12, join as join18 } from "path";
10017
+ import { basename as basename3, dirname as dirname12, join as join19 } from "path";
10027
10018
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
10028
- return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
10019
+ return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join19(workspaceDir, "wiki");
10029
10020
  }
10030
10021
  var init_wiki = __esm(async () => {
10031
10022
  await init_storage_host();
@@ -10033,8 +10024,8 @@ var init_wiki = __esm(async () => {
10033
10024
 
10034
10025
  // ../../packages/core/src/agents/archiver.ts
10035
10026
  import { randomUUID as randomUUID9 } from "crypto";
10036
- import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
10037
- import { join as join19 } from "path";
10027
+ import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync16, statSync as statSync7 } from "fs";
10028
+ import { join as join20 } from "path";
10038
10029
  function resolveMemoryLanguage() {
10039
10030
  const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
10040
10031
  return override && override.length > 0 ? override : resolveOutputLanguage();
@@ -10285,10 +10276,10 @@ function distillOneLine(summaryMd) {
10285
10276
  return "";
10286
10277
  }
10287
10278
  function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
10288
- const dir = join19(storage.getWikiDir(), "summaries");
10279
+ const dir = join20(storage.getWikiDir(), "summaries");
10289
10280
  if (!storage.fileExists(dir))
10290
10281
  return null;
10291
- const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
10282
+ const predicted = join20(dir, wikiSummaryFilename(date, topicTitle, topicId));
10292
10283
  if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
10293
10284
  return predicted;
10294
10285
  let best = null;
@@ -10296,7 +10287,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
10296
10287
  if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
10297
10288
  continue;
10298
10289
  }
10299
- const p = join19(dir, f);
10290
+ const p = join20(dir, f);
10300
10291
  try {
10301
10292
  const m = storage.fileModifiedAt(p);
10302
10293
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -10378,9 +10369,9 @@ var init_archiver = __esm(async () => {
10378
10369
  defaultArchiverRuntime = createArchiverRuntime({
10379
10370
  storage: {
10380
10371
  getWikiDir: getSharedWikiDir,
10381
- fileExists: existsSync16,
10382
- listDirectory: readdirSync4,
10383
- readTextFile: (path) => readFileSync15(path, "utf-8"),
10372
+ fileExists: existsSync17,
10373
+ listDirectory: readdirSync5,
10374
+ readTextFile: (path) => readFileSync16(path, "utf-8"),
10384
10375
  fileSize: (path) => statSync7(path).size,
10385
10376
  fileModifiedAt: (path) => statSync7(path).mtimeMs,
10386
10377
  getGeneralTopicId: (userId) => ensurePersonalGeneral(userId).id,
@@ -10494,13 +10485,13 @@ function formatTopicArchiveTranscriptRecord(row, topicTitle, index) {
10494
10485
 
10495
10486
  // ../../packages/core/src/storage/topic-archive.ts
10496
10487
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
10497
- import { join as join20 } from "path";
10488
+ import { join as join21 } from "path";
10498
10489
  function archiveTopicMessages(topicId, topicTitle, options = {}) {
10499
10490
  const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
10500
10491
  if (rows.length === 0)
10501
10492
  return null;
10502
10493
  const safeTopic = sanitizeTopicName(topicTitle, true);
10503
- const archiveDir = join20(getSharedWikiDir(), "archive");
10494
+ const archiveDir = join21(getSharedWikiDir(), "archive");
10504
10495
  mkdirSync12(archiveDir, { recursive: true });
10505
10496
  const date = new Date().toISOString().slice(0, 10);
10506
10497
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
@@ -10513,7 +10504,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
10513
10504
  let path;
10514
10505
  while (true) {
10515
10506
  filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
10516
- path = join20(archiveDir, filename);
10507
+ path = join21(archiveDir, filename);
10517
10508
  try {
10518
10509
  writeFileSync10(path, body, { flag: "wx" });
10519
10510
  break;
@@ -10558,7 +10549,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
10558
10549
  const entries = readRawConversation(userId, topicTitle);
10559
10550
  if (entries.length === 0)
10560
10551
  return null;
10561
- const archiveDir = join20(getSharedWikiDir(), "archive");
10552
+ const archiveDir = join21(getSharedWikiDir(), "archive");
10562
10553
  mkdirSync12(archiveDir, { recursive: true });
10563
10554
  const safeTopic = sanitizeTopicName(topicTitle, true);
10564
10555
  const date = new Date().toISOString().slice(0, 10);
@@ -10586,7 +10577,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
10586
10577
  let counter = 1;
10587
10578
  while (true) {
10588
10579
  const suffix = counter === 1 ? "" : `_${counter}`;
10589
- const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
10580
+ const path = join21(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
10590
10581
  try {
10591
10582
  writeFileSync10(path, body, { flag: "wx" });
10592
10583
  logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
@@ -10909,7 +10900,7 @@ var init_idle_archiver = __esm(async () => {
10909
10900
  });
10910
10901
 
10911
10902
  // ../../packages/core/src/agents/topic-cleanup.ts
10912
- import { mkdirSync as mkdirSync13, renameSync as renameSync8, unlinkSync as unlinkSync12, writeFileSync as writeFileSync11 } from "fs";
10903
+ import { mkdirSync as mkdirSync13, renameSync as renameSync7, unlinkSync as unlinkSync12, writeFileSync as writeFileSync11 } from "fs";
10913
10904
  import { dirname as dirname13 } from "path";
10914
10905
  function collectSessionIdsByAgent(entries, extraSessions = []) {
10915
10906
  const out = new Map;
@@ -10980,7 +10971,7 @@ function createTopicLogMaintenance(host) {
10980
10971
  writeFileSync11(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
10981
10972
  `)}
10982
10973
  ` : "", { flag: "wx" });
10983
- renameSync8(tempPath, path);
10974
+ renameSync7(tempPath, path);
10984
10975
  } catch (e) {
10985
10976
  try {
10986
10977
  unlinkSync12(tempPath);
@@ -11143,7 +11134,7 @@ var init_usage_alert = __esm(() => {
11143
11134
  import { randomUUID as randomUUID10 } from "crypto";
11144
11135
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "fs";
11145
11136
  import { tmpdir as tmpdir3 } from "os";
11146
- import { join as join21 } from "path";
11137
+ import { join as join22 } from "path";
11147
11138
  function previousCompactedSummary(entries) {
11148
11139
  for (let index = entries.length - 2;index >= 0; index -= 1) {
11149
11140
  const request = entries[index]?.event;
@@ -11273,7 +11264,7 @@ function formatCompactElapsed(startedAt) {
11273
11264
  async function summarizeTopicContext(request) {
11274
11265
  const startedAt = Date.now();
11275
11266
  const sessionIds = [];
11276
- const compactCwd = mkdtempSync2(join21(tmpdir3(), "negotium-compact-"));
11267
+ const compactCwd = mkdtempSync2(join22(tmpdir3(), "negotium-compact-"));
11277
11268
  const abortController = new AbortController;
11278
11269
  const relayAbort = () => abortController.abort(request.signal?.reason);
11279
11270
  if (request.signal?.aborted)
@@ -11300,7 +11291,7 @@ async function summarizeTopicContext(request) {
11300
11291
  let error = "";
11301
11292
  let toolViolation = false;
11302
11293
  let compactionLogCalls = 0;
11303
- const compactionLogPath = join21(compactCwd, "conversation.log");
11294
+ const compactionLogPath = join22(compactCwd, "conversation.log");
11304
11295
  try {
11305
11296
  const compactionMcp = useCompactionLog ? {
11306
11297
  compact_log: {
@@ -11896,14 +11887,14 @@ var init_derive = __esm(async () => {
11896
11887
  });
11897
11888
 
11898
11889
  // ../../packages/core/src/query/session-inbox-path.ts
11899
- import { join as join22 } from "path";
11890
+ import { join as join23 } from "path";
11900
11891
  function sessionInboxPath(userId, topicId) {
11901
11892
  const key = Buffer.from(topicId, "utf8").toString("base64url");
11902
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
11893
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
11903
11894
  }
11904
11895
  function scheduledSessionInboxPath(userId, topicId) {
11905
11896
  const key = Buffer.from(topicId, "utf8").toString("base64url");
11906
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
11897
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
11907
11898
  }
11908
11899
  var TOPIC_ID_FILE_PREFIX = "topic-id-", JSONL_SUFFIX = ".jsonl", SCHEDULE_SUFFIX = ".schedule";
11909
11900
  var init_session_inbox_path = __esm(() => {
@@ -11912,13 +11903,13 @@ var init_session_inbox_path = __esm(() => {
11912
11903
 
11913
11904
  // ../../packages/core/src/query/session-inbox-cleanup.ts
11914
11905
  import { unlinkSync as unlinkSync14 } from "fs";
11915
- import { basename as basename4, join as join23 } from "path";
11906
+ import { basename as basename4, join as join24 } from "path";
11916
11907
  function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
11917
11908
  const live = sessionInboxPath(userId, topicId);
11918
11909
  const scheduled = scheduledSessionInboxPath(userId, topicId);
11919
11910
  const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
11920
11911
  if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename4(legacyTopicTitle) === legacyTopicTitle) {
11921
- const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
11912
+ const legacyBase = join24(SESSION_INBOX_DIR, userId, legacyTopicTitle);
11922
11913
  for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
11923
11914
  candidates.add(`${legacyBase}${suffix}`);
11924
11915
  }
@@ -11943,17 +11934,17 @@ var init_session_inbox_cleanup = __esm(() => {
11943
11934
  });
11944
11935
 
11945
11936
  // ../../packages/core/src/query/state.ts
11946
- import { mkdirSync as mkdirSync15, renameSync as renameSync9, unlinkSync as unlinkSync15, writeFileSync as writeFileSync13 } from "fs";
11947
- import { basename as basename5, join as join24 } from "path";
11937
+ import { mkdirSync as mkdirSync15, renameSync as renameSync8, unlinkSync as unlinkSync15, writeFileSync as writeFileSync13 } from "fs";
11938
+ import { basename as basename5, join as join25 } from "path";
11948
11939
  function createQueryStateStore(options) {
11949
11940
  const sanitize = options.sanitizeTopicId ?? sanitizeId;
11950
- const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
11951
- const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
11941
+ const queryStateDirPath = (userId) => join25(options.usersLogDir, String(userId), "active-queries");
11942
+ const queryStateFile = (userId, topicId) => join25(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
11952
11943
  const legacyQueryStateFile = (userId, topicName) => {
11953
11944
  if (!topicName || topicName === "." || topicName === ".." || basename5(topicName) !== topicName) {
11954
11945
  return null;
11955
11946
  }
11956
- return join24(queryStateDirPath(userId), `${topicName}.json`);
11947
+ return join25(queryStateDirPath(userId), `${topicName}.json`);
11957
11948
  };
11958
11949
  return {
11959
11950
  write(userId, topicId, topicName, task) {
@@ -11965,7 +11956,7 @@ function createQueryStateStore(options) {
11965
11956
  const target = queryStateFile(userId, topicId);
11966
11957
  const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
11967
11958
  writeFileSync13(tmp, JSON.stringify(state));
11968
- renameSync9(tmp, target);
11959
+ renameSync8(tmp, target);
11969
11960
  },
11970
11961
  clear(userId, topicId, legacyTopicName) {
11971
11962
  const paths = [
@@ -12107,35 +12098,35 @@ import {
12107
12098
  closeSync as closeSync3,
12108
12099
  mkdirSync as mkdirSync16,
12109
12100
  openSync as openSync3,
12110
- readdirSync as readdirSync5,
12111
- readFileSync as readFileSync16,
12101
+ readdirSync as readdirSync6,
12102
+ readFileSync as readFileSync17,
12112
12103
  statSync as statSync8,
12113
12104
  unlinkSync as unlinkSync16,
12114
12105
  writeFileSync as writeFileSync14
12115
12106
  } from "fs";
12116
- import { dirname as dirname14, join as join25 } from "path";
12107
+ import { dirname as dirname14, join as join26 } from "path";
12117
12108
  function pendingAskDir(userId) {
12118
12109
  const rawUserId = String(userId);
12119
12110
  const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash4("sha256").update(rawUserId).digest("hex")}`;
12120
- return join25(resolveStorageSessionAsksDir(), safeUserId);
12111
+ return join26(resolveStorageSessionAsksDir(), safeUserId);
12121
12112
  }
12122
12113
  function encodeAskKey(key) {
12123
12114
  return JSON.stringify([key.from, key.to]);
12124
12115
  }
12125
12116
  function pendingAskPath(key) {
12126
12117
  const digest = createHash4("sha256").update(encodeAskKey(key)).digest("hex");
12127
- return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12118
+ return join26(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12128
12119
  }
12129
12120
  function v2PendingAskPath(key) {
12130
12121
  const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
12131
- return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
12122
+ return join26(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
12132
12123
  }
12133
12124
  function legacyPendingAskPath(key) {
12134
12125
  if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
12135
12126
  return null;
12136
12127
  }
12137
12128
  const dir = pendingAskDir(key.userId);
12138
- const candidate = join25(dir, `${key.from}___${key.to}.pending`);
12129
+ const candidate = join26(dir, `${key.from}___${key.to}.pending`);
12139
12130
  return dirname14(candidate) === dir ? candidate : null;
12140
12131
  }
12141
12132
  function parsePendingAskFilename(fileName) {
@@ -12157,7 +12148,7 @@ function parsePendingAskFilename(fileName) {
12157
12148
  }
12158
12149
  function readPendingAskFile(path, fallback) {
12159
12150
  try {
12160
- const raw = readFileSync16(path, "utf8").trim();
12151
+ const raw = readFileSync17(path, "utf8").trim();
12161
12152
  if (!raw)
12162
12153
  return null;
12163
12154
  if (!raw.startsWith("{")) {
@@ -12366,7 +12357,7 @@ function listPendingAsksForCaller(args) {
12366
12357
  const dir = pendingAskDir(args.userId);
12367
12358
  let files;
12368
12359
  try {
12369
- files = readdirSync5(dir);
12360
+ files = readdirSync6(dir);
12370
12361
  } catch {
12371
12362
  return [];
12372
12363
  }
@@ -12376,7 +12367,7 @@ function listPendingAsksForCaller(args) {
12376
12367
  const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
12377
12368
  if (!parsed)
12378
12369
  continue;
12379
- const path = join25(dir, fileName);
12370
+ const path = join26(dir, fileName);
12380
12371
  const record = readPendingAskFile(path, {
12381
12372
  userId: args.userId,
12382
12373
  from: parsed.from,
@@ -12412,13 +12403,13 @@ function deletePendingAsksForTopic(args) {
12412
12403
  const dir = pendingAskDir(args.userId);
12413
12404
  let files;
12414
12405
  try {
12415
- files = readdirSync5(dir);
12406
+ files = readdirSync6(dir);
12416
12407
  } catch {
12417
12408
  return 0;
12418
12409
  }
12419
12410
  let deleted = 0;
12420
12411
  for (const fileName of files) {
12421
- const path = join25(dir, fileName);
12412
+ const path = join26(dir, fileName);
12422
12413
  const parsed = parsePendingAskFilename(fileName);
12423
12414
  const record = readPendingAskFile(path, {
12424
12415
  userId: args.userId,
@@ -13087,7 +13078,7 @@ var init_lifecycle = __esm(async () => {
13087
13078
  // ../../packages/core/src/runtime/attachments.ts
13088
13079
  import { randomUUID as randomUUID13 } from "crypto";
13089
13080
  import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
13090
- import { basename as basename6, join as join26 } from "path";
13081
+ import { basename as basename6, join as join27 } from "path";
13091
13082
  function workspaceCwdFor(topicId) {
13092
13083
  return resolveTopicWorkspaceDir(topicId);
13093
13084
  }
@@ -13100,7 +13091,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
13100
13091
  if (!attachmentIds?.length)
13101
13092
  return [];
13102
13093
  const out = [];
13103
- const destDir = join26(workspaceCwdFor(topicId), "attachments", queryId);
13094
+ const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
13104
13095
  for (const rawId of attachmentIds) {
13105
13096
  if (typeof rawId !== "string")
13106
13097
  continue;
@@ -13117,7 +13108,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
13117
13108
  mkdirSync17(destDir, { recursive: true });
13118
13109
  const index = String(out.length + 1).padStart(2, "0");
13119
13110
  const safeName = safeAttachmentFilename(attachment.filename, fileId);
13120
- const destPath = join26(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
13111
+ const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
13121
13112
  copyFileSync2(sourcePath, destPath);
13122
13113
  out.push({
13123
13114
  id: attachment.id,
@@ -13147,10 +13138,10 @@ function promptWithAttachments(prompt, attachments) {
13147
13138
  return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
13148
13139
  }
13149
13140
  function ingestAttachment(args) {
13150
- const destDir = join26(workspaceCwdFor(args.topicId), "uploads");
13141
+ const destDir = join27(UPLOADS_DIR, args.topicId);
13151
13142
  mkdirSync17(destDir, { recursive: true });
13152
13143
  const safeName = safeAttachmentFilename(args.filename, "upload");
13153
- const destPath = join26(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
13144
+ const destPath = join27(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
13154
13145
  if (args.sourcePath !== undefined) {
13155
13146
  copyFileSync2(args.sourcePath, destPath);
13156
13147
  } else if (args.bytes !== undefined) {
@@ -14136,7 +14127,7 @@ var init_visual_html = __esm(() => {
14136
14127
  });
14137
14128
 
14138
14129
  // ../../packages/core/src/runtime/visuals.ts
14139
- import { realpathSync as realpathSync4 } from "fs";
14130
+ import { realpathSync as realpathSync5 } from "fs";
14140
14131
  import { isAbsolute as isAbsolute4, resolve as resolve15 } from "path";
14141
14132
  function activeVisualHtmlForPrompt(html) {
14142
14133
  if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
@@ -14191,8 +14182,8 @@ function isPathInside(baseDir, filePath) {
14191
14182
  const base = resolve15(baseDir);
14192
14183
  const normalized = resolve15(filePath);
14193
14184
  try {
14194
- const realBase = realpathSync4(base);
14195
- const real = realpathSync4(normalized);
14185
+ const realBase = realpathSync5(base);
14186
+ const real = realpathSync5(normalized);
14196
14187
  return real === realBase || real.startsWith(`${realBase}/`);
14197
14188
  } catch {
14198
14189
  return false;
@@ -14257,7 +14248,7 @@ function resolveVisualMediaInput(topicId, input) {
14257
14248
  }
14258
14249
  let realCandidate;
14259
14250
  try {
14260
- realCandidate = realpathSync4(candidate);
14251
+ realCandidate = realpathSync5(candidate);
14261
14252
  } catch {
14262
14253
  return { error: `file not found: ${rawPath}` };
14263
14254
  }
@@ -14291,7 +14282,7 @@ var init_visuals = __esm(async () => {
14291
14282
  // ../../packages/core/src/storage/token-stats.ts
14292
14283
  import { createHash as createHash6 } from "crypto";
14293
14284
  import { mkdirSync as mkdirSync18 } from "fs";
14294
- import { join as join27 } from "path";
14285
+ import { join as join28 } from "path";
14295
14286
  function emptyBucket() {
14296
14287
  return {
14297
14288
  inputTokens: 0,
@@ -14309,7 +14300,7 @@ function queriesPath(userId) {
14309
14300
  const fileId = tokenStatsFileId(userId);
14310
14301
  const logDir = resolveStorageLogDir();
14311
14302
  mkdirSync18(logDir, { recursive: true });
14312
- return join27(logDir, `token-queries-${fileId}.jsonl`);
14303
+ return join28(logDir, `token-queries-${fileId}.jsonl`);
14313
14304
  }
14314
14305
  function loadRecords(userId) {
14315
14306
  try {
@@ -14380,7 +14371,7 @@ var init_token_stats = __esm(async () => {
14380
14371
 
14381
14372
  // ../../packages/core/src/runtime/turn-event-stream.ts
14382
14373
  import { randomUUID as randomUUID15 } from "crypto";
14383
- import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
14374
+ import { realpathSync as realpathSync6, statSync as statSync9 } from "fs";
14384
14375
  import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
14385
14376
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
14386
14377
  if (getRoomQuery(topicId)?.queryId !== queryId)
@@ -14695,7 +14686,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
14695
14686
  }
14696
14687
  let safePath;
14697
14688
  try {
14698
- safePath = realpathSync5(path);
14689
+ safePath = realpathSync6(path);
14699
14690
  if (!statSync9(safePath).isFile()) {
14700
14691
  logger.warn({ topicId, path }, "peer output path is not a regular file");
14701
14692
  break;
@@ -14962,10 +14953,10 @@ var init_turn_session = __esm(async () => {
14962
14953
  });
14963
14954
 
14964
14955
  // ../../packages/core/src/storage/app-settings.ts
14965
- import { existsSync as existsSync17, mkdirSync as mkdirSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync16 } from "fs";
14966
- import { dirname as dirname15, join as join28 } from "path";
14956
+ import { existsSync as existsSync18, mkdirSync as mkdirSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
14957
+ import { dirname as dirname15, join as join29 } from "path";
14967
14958
  function settingsFile() {
14968
- return join28(resolveStorageDataDir(), "otium-settings.json");
14959
+ return join29(resolveStorageDataDir(), "otium-settings.json");
14969
14960
  }
14970
14961
  function ensureSettingsLoaded() {
14971
14962
  const path = settingsFile();
@@ -14974,8 +14965,8 @@ function ensureSettingsLoaded() {
14974
14965
  loadedSettingsFile = path;
14975
14966
  aiName = DEFAULT_AI_NAME;
14976
14967
  try {
14977
- if (existsSync17(path)) {
14978
- const data = JSON.parse(readFileSync17(path, "utf8"));
14968
+ if (existsSync18(path)) {
14969
+ const data = JSON.parse(readFileSync18(path, "utf8"));
14979
14970
  if (typeof data.aiName === "string" && data.aiName.trim()) {
14980
14971
  aiName = data.aiName.trim();
14981
14972
  }
@@ -15064,8 +15055,8 @@ __export(exports_turn_runner, {
15064
15055
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
15065
15056
  });
15066
15057
  import { randomUUID as randomUUID16 } from "crypto";
15067
- import { existsSync as existsSync18, mkdirSync as mkdirSync20, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
15068
- import { join as join29 } from "path";
15058
+ import { existsSync as existsSync19, mkdirSync as mkdirSync20, readdirSync as readdirSync7, statSync as statSync10 } from "fs";
15059
+ import { join as join30 } from "path";
15069
15060
  function withDefaultPlaywright(configuredMcp, isManager) {
15070
15061
  if (isManager)
15071
15062
  return configuredMcp;
@@ -15108,18 +15099,18 @@ function appendAskReplyMessage(topicId, text2, agentType) {
15108
15099
  return message;
15109
15100
  }
15110
15101
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
15111
- const preferred = join29(directory, preferredFilename);
15112
- if (preferExact && existsSync18(preferred))
15102
+ const preferred = join30(directory, preferredFilename);
15103
+ if (preferExact && existsSync19(preferred))
15113
15104
  return preferred;
15114
15105
  let newestLegacyId = null;
15115
15106
  let newestTitle = null;
15116
15107
  try {
15117
- for (const filename of readdirSync6(directory)) {
15108
+ for (const filename of readdirSync7(directory)) {
15118
15109
  const titleMatch = matchesTitle(filename);
15119
15110
  const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
15120
15111
  if (!titleMatch && !legacyIdMatch)
15121
15112
  continue;
15122
- const path = join29(directory, filename);
15113
+ const path = join30(directory, filename);
15123
15114
  const mtimeMs = statSync10(path).mtimeMs;
15124
15115
  if (titleMatch) {
15125
15116
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
@@ -15133,10 +15124,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
15133
15124
  return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
15134
15125
  }
15135
15126
  function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
15136
- const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
15137
- const hasBriefFile = existsSync18(briefFile) && statSync10(briefFile).isFile();
15138
- const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
15139
- const latestSummaryFile = existsSync18(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15127
+ const briefFile = resolveWikiMirrorPath(join30(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
15128
+ const hasBriefFile = existsSync19(briefFile) && statSync10(briefFile).isFile();
15129
+ const latestSummaryCandidate = resolveWikiMirrorPath(join30(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
15130
+ const latestSummaryFile = existsSync19(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15140
15131
  return { briefFile, hasBriefFile, latestSummaryFile };
15141
15132
  }
15142
15133
  async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
@@ -16288,9 +16279,9 @@ __export(exports_auth_check, {
16288
16279
  checkAgentAuth: () => checkAgentAuth
16289
16280
  });
16290
16281
  import { execFileSync as execFileSync7 } from "child_process";
16291
- import { existsSync as existsSync19 } from "fs";
16282
+ import { existsSync as existsSync20 } from "fs";
16292
16283
  import { homedir as homedir9, platform } from "os";
16293
- import { join as join30 } from "path";
16284
+ import { join as join31 } from "path";
16294
16285
  function hasMaestroCredential(host, key, userId) {
16295
16286
  if (userId && host.getVaultValue(userId, key)?.trim())
16296
16287
  return true;
@@ -16319,7 +16310,7 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost, userId) {
16319
16310
  error: "claude is not logged in (no macOS keychain entry 'Claude Code-credentials'). Run `claude` and complete login first"
16320
16311
  };
16321
16312
  }
16322
- const path = join30(host.homeDirectory(), ".claude", ".credentials.json");
16313
+ const path = join31(host.homeDirectory(), ".claude", ".credentials.json");
16323
16314
  if (host.exists(path))
16324
16315
  return { ok: true };
16325
16316
  return {
@@ -16361,7 +16352,7 @@ var init_auth_check = __esm(async () => {
16361
16352
  await init_vault();
16362
16353
  defaultAgentAuthHost = {
16363
16354
  codexAuthFilePath,
16364
- exists: existsSync19,
16355
+ exists: existsSync20,
16365
16356
  environment: process.env,
16366
16357
  homeDirectory: homedir9,
16367
16358
  operatingSystem: platform,
@@ -18433,15 +18424,401 @@ await __promiseAll([
18433
18424
  init_codex_registry(),
18434
18425
  init_maestro_registry()
18435
18426
  ]);
18427
+ import { spawn as spawn2 } from "child_process";
18428
+ import { existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
18429
+ import { join as join11 } from "path";
18436
18430
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18437
18431
  import { z } from "zod";
18432
+ function signalProcessTree(child, signal) {
18433
+ try {
18434
+ if (child.pid && process.platform !== "win32")
18435
+ process.kill(-child.pid, signal);
18436
+ else
18437
+ child.kill(signal);
18438
+ } catch {
18439
+ try {
18440
+ child.kill(signal);
18441
+ } catch {}
18442
+ }
18443
+ }
18444
+ function spawnCapture(cmd, args, timeoutMs, signal, env) {
18445
+ return new Promise((resolve7, reject) => {
18446
+ const child = spawn2(cmd, args, {
18447
+ env: env ?? process.env,
18448
+ stdio: ["ignore", "pipe", "pipe"],
18449
+ detached: process.platform !== "win32"
18450
+ });
18451
+ let settled = false;
18452
+ let stdout = "";
18453
+ let stderr = "";
18454
+ const forceKill = () => signalProcessTree(child, "SIGKILL");
18455
+ let killTimer;
18456
+ const stop = (reason) => {
18457
+ if (settled)
18458
+ return;
18459
+ settled = true;
18460
+ signalProcessTree(child, "SIGTERM");
18461
+ killTimer = setTimeout(() => {
18462
+ forceKill();
18463
+ killTimer = undefined;
18464
+ }, 1000);
18465
+ killTimer.unref?.();
18466
+ reject(new Error(reason));
18467
+ };
18468
+ const timeout = setTimeout(() => stop("timeout"), timeoutMs);
18469
+ const onAbort = () => stop("aborted");
18470
+ signal.addEventListener("abort", onAbort, { once: true });
18471
+ const cleanup = () => {
18472
+ clearTimeout(timeout);
18473
+ signal.removeEventListener("abort", onAbort);
18474
+ };
18475
+ child.stdout.on("data", (data) => {
18476
+ stdout += data.toString();
18477
+ });
18478
+ child.stderr.on("data", (data) => {
18479
+ stderr += data.toString();
18480
+ });
18481
+ child.on("error", (err) => {
18482
+ if (settled)
18483
+ return;
18484
+ settled = true;
18485
+ cleanup();
18486
+ if (killTimer)
18487
+ clearTimeout(killTimer);
18488
+ reject(err);
18489
+ });
18490
+ child.on("close", (code, exitSignal) => {
18491
+ cleanup();
18492
+ if (settled) {
18493
+ if (killTimer) {
18494
+ clearTimeout(killTimer);
18495
+ killTimer = undefined;
18496
+ }
18497
+ return;
18498
+ }
18499
+ settled = true;
18500
+ if (killTimer)
18501
+ clearTimeout(killTimer);
18502
+ if (code === 0)
18503
+ resolve7({ stdout, stderr });
18504
+ else
18505
+ reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
18506
+ });
18507
+ });
18508
+ }
18509
+ function resultOk(agent, model, latency, detail) {
18510
+ return { agent, model, ok: true, latency, detail };
18511
+ }
18512
+ function resultFail(agent, model, latency, error) {
18513
+ let category = "unknown";
18514
+ const normalized = error.toLowerCase();
18515
+ if (normalized.includes("auth") || normalized.includes("key") || normalized.includes("credential") || normalized.includes("unauthorized")) {
18516
+ category = "auth_error";
18517
+ } else if (normalized.includes("rate") || normalized.includes("limit") || normalized.includes("busy") || normalized.includes("overloaded")) {
18518
+ category = "rate_limit";
18519
+ } else if (normalized.includes("network") || normalized.includes("refused") || normalized.includes("dns") || normalized.includes("timeout")) {
18520
+ category = "network_error";
18521
+ }
18522
+ return { agent, model, ok: false, latency, error, category };
18523
+ }
18524
+ async function checkClaude(model, timeoutMs, signal) {
18525
+ const start = performance.now();
18526
+ try {
18527
+ const args = ["-p", "say OK", "--model", model, "--max-turns", "1", "--output-format", "text"];
18528
+ const { stdout, stderr } = await spawnCapture("claude", args, timeoutMs, signal);
18529
+ const elapsed = Math.round(performance.now() - start);
18530
+ const text = (stdout + stderr).trim().slice(0, 200);
18531
+ if (!text)
18532
+ return resultFail("claude", model, elapsed, "empty response");
18533
+ return resultOk("claude", model, elapsed, text);
18534
+ } catch (err) {
18535
+ return resultFail("claude", model, Math.round(performance.now() - start), errMsg(err));
18536
+ }
18537
+ }
18538
+ async function checkCodex(model, timeoutMs, signal) {
18539
+ const start = performance.now();
18540
+ try {
18541
+ const args = [
18542
+ "exec",
18543
+ "--model",
18544
+ model,
18545
+ "--skip-git-repo-check",
18546
+ "--sandbox",
18547
+ "danger-full-access",
18548
+ "say OK"
18549
+ ];
18550
+ const { stdout, stderr } = await spawnCapture("codex", args, timeoutMs, signal, {
18551
+ ...process.env,
18552
+ CODEX_NO_COLOR: "1"
18553
+ });
18554
+ const elapsed = Math.round(performance.now() - start);
18555
+ const combined = (stdout + stderr).trim();
18556
+ const match = combined.match(/\ncodex\s*\n(.+?)(?:\n(?:tokens|session|user|$))/);
18557
+ return resultOk("codex", model, elapsed, match ? match[1].trim().slice(0, 200) : "OK");
18558
+ } catch (err) {
18559
+ return resultFail("codex", model, Math.round(performance.now() - start), errMsg(err));
18560
+ }
18561
+ }
18562
+ async function checkMaestro(model, timeoutMs, signal) {
18563
+ const start = performance.now();
18564
+ let iterator;
18565
+ try {
18566
+ const { maestroProvider } = await import("maestro-agent-sdk");
18567
+ const generator = maestroProvider({
18568
+ model,
18569
+ prompt: "say OK",
18570
+ cwd: process.cwd(),
18571
+ maxTurns: 1
18572
+ });
18573
+ const deadline = Date.now() + timeoutMs;
18574
+ const activeIterator = generator[Symbol.asyncIterator]();
18575
+ iterator = activeIterator;
18576
+ let text = "";
18577
+ while (true) {
18578
+ const remaining = deadline - Date.now();
18579
+ if (remaining <= 0)
18580
+ throw new Error("timeout");
18581
+ const eventResult = await new Promise((resolve7, reject) => {
18582
+ const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
18583
+ const onAbort = () => reject(new Error("aborted"));
18584
+ signal.addEventListener("abort", onAbort, { once: true });
18585
+ activeIterator.next().then(resolve7, reject).finally(() => {
18586
+ clearTimeout(timeout);
18587
+ signal.removeEventListener("abort", onAbort);
18588
+ });
18589
+ });
18590
+ if (eventResult.done)
18591
+ break;
18592
+ const event = eventResult.value;
18593
+ if (event.type === "text" && event.content)
18594
+ text = event.content.slice(0, 200);
18595
+ if (event.type === "error") {
18596
+ return resultFail("maestro", model, Math.round(performance.now() - start), event.error || "unknown error");
18597
+ }
18598
+ }
18599
+ return resultOk("maestro", model, Math.round(performance.now() - start), text || "(empty response)");
18600
+ } catch (err) {
18601
+ return resultFail("maestro", model, Math.round(performance.now() - start), errMsg(err));
18602
+ } finally {
18603
+ if (iterator?.return) {
18604
+ try {
18605
+ await iterator.return();
18606
+ } catch {}
18607
+ }
18608
+ }
18609
+ }
18610
+ function getDefaultModel(registry) {
18611
+ return resolveDefaultModel(registry.kind, registry.defaultModel);
18612
+ }
18613
+ var AGENTS = [
18614
+ { kind: "claude", registry: claudeRegistry, checker: checkClaude },
18615
+ { kind: "codex", registry: codexRegistry, checker: checkCodex },
18616
+ { kind: "maestro", registry: maestroRegistry, checker: checkMaestro }
18617
+ ];
18618
+ var MAX_CONCURRENT_CHECKS = 2;
18619
+ var activeChecks = 0;
18620
+ var checkWaiters = [];
18438
18621
  var activeCheckAllUsers = new Set;
18622
+ async function withCheckSlot(operation) {
18623
+ if (activeChecks >= MAX_CONCURRENT_CHECKS) {
18624
+ await new Promise((resolve7) => checkWaiters.push(resolve7));
18625
+ }
18626
+ activeChecks += 1;
18627
+ try {
18628
+ return await operation();
18629
+ } finally {
18630
+ activeChecks -= 1;
18631
+ checkWaiters.shift()?.();
18632
+ }
18633
+ }
18634
+ function createAgentHealthMcpServer(context) {
18635
+ const server = new McpServer({ name: "agent-health", version: "1.0.0" });
18636
+ const abortController = new AbortController;
18637
+ const originalClose = server.close.bind(server);
18638
+ server.close = async () => {
18639
+ abortController.abort();
18640
+ await originalClose();
18641
+ };
18642
+ server.tool("check_agent", "\uC2E4\uC81C API \uD638\uCD9C\uB85C \uD2B9\uC815 agent+model \uC870\uD569\uC774 \uC815\uC0C1 \uB3D9\uC791\uD558\uB294\uC9C0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", {
18643
+ agent: z.enum(["claude", "codex", "maestro"]).describe("\uCCB4\uD06C\uD560 agent"),
18644
+ model: z.string().optional().describe("\uCCB4\uD06C\uD560 \uBAA8\uB378 (\uC5C6\uC73C\uBA74 agent\uBCC4 default \uC0AC\uC6A9)"),
18645
+ timeoutMs: z.number().default(30000).describe("\uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08")
18646
+ }, async ({ agent, model, timeoutMs }) => {
18647
+ const entry = AGENTS.find((candidate) => candidate.kind === agent);
18648
+ if (!entry)
18649
+ return mcpOk(`Unknown agent: ${agent}`);
18650
+ const resolvedModel = model || getDefaultModel(entry.registry);
18651
+ const result = await withCheckSlot(() => entry.checker(resolvedModel, timeoutMs, abortController.signal));
18652
+ if (result.ok) {
18653
+ return mcpOk(`OK ${result.agent} / ${result.model}
18654
+ latency: ${result.latency}ms
18655
+ detail: ${result.detail}`);
18656
+ }
18657
+ return mcpOk(`FAIL ${result.agent} / ${result.model} [${result.category}]
18658
+ latency: ${result.latency}ms
18659
+ error: ${result.error}`);
18660
+ });
18661
+ server.tool("check_all", "\uBAA8\uB4E0 agent (claude, codex, maestro)\uB97C \uAE30\uBCF8 \uBAA8\uB378\uB85C \uB3D9\uC2DC\uC5D0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", { timeoutMs: z.number().default(30000).describe("agent\uBCC4 \uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08") }, async ({ timeoutMs }) => {
18662
+ const userKey = context.userId ?? "unscoped";
18663
+ if (activeCheckAllUsers.has(userKey)) {
18664
+ return mcpOk("FAIL check_all is already running for this user");
18665
+ }
18666
+ activeCheckAllUsers.add(userKey);
18667
+ try {
18668
+ const results = await Promise.all(AGENTS.map(async (entry) => withCheckSlot(() => entry.checker(getDefaultModel(entry.registry), timeoutMs, abortController.signal))));
18669
+ const lines = results.map((result) => result.ok ? `OK ${result.agent.padEnd(8)} / ${result.model.padEnd(14)} ${result.latency}ms ${result.detail}` : `FAIL ${result.agent.padEnd(8)} / ${result.model.padEnd(14)} ${result.latency}ms [${result.category}] ${result.error}`);
18670
+ return mcpOk(`Agent/Model Health Check
18671
+ ${lines.join(`
18672
+ `)}
18673
+
18674
+ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents failed"}`);
18675
+ } finally {
18676
+ activeCheckAllUsers.delete(userKey);
18677
+ }
18678
+ });
18679
+ server.tool("list_active_queries", "\uD604\uC7AC \uC0AC\uC6A9\uC790 \uBC94\uC704\uC5D0\uC11C \uC2E4\uD589 \uC911\uC778 \uD1A0\uD53D \uCFFC\uB9AC \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.", {}, async () => {
18680
+ if (!context.userId)
18681
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
18682
+ const stateDir = join11(USERS_LOG_DIR, context.userId, "active-queries");
18683
+ if (!existsSync9(stateDir))
18684
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
18685
+ let files;
18686
+ try {
18687
+ files = readdirSync3(stateDir);
18688
+ } catch {
18689
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
18690
+ }
18691
+ const now = Date.now();
18692
+ const entries = files.flatMap((file) => {
18693
+ if (!file.endsWith(".json"))
18694
+ return [];
18695
+ const state = readJsonFile(join11(stateDir, file));
18696
+ if (!state)
18697
+ return [];
18698
+ const sinceMs = new Date(state.since).getTime();
18699
+ if (now - sinceMs > ACTIVE_QUERY_STALE_MS)
18700
+ return [];
18701
+ return [
18702
+ {
18703
+ topicName: state.topicName ?? file.slice(0, -5),
18704
+ elapsedSec: Math.round((now - sinceMs) / 1000),
18705
+ task: state.task
18706
+ }
18707
+ ];
18708
+ });
18709
+ if (entries.length === 0)
18710
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
18711
+ const lines = entries.map((entry) => `\u2022 ${entry.topicName} | ${entry.elapsedSec}s \uACBD\uACFC${entry.task ? ` "${entry.task}"` : ""}`);
18712
+ return mcpOk(`\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC (${entries.length}\uAC1C)
18713
+ ${lines.join(`
18714
+ `)}`);
18715
+ });
18716
+ return server;
18717
+ }
18439
18718
  // ../../packages/core/src/mcp/factories/compaction-log.ts
18440
18719
  init_mcp_helpers();
18720
+ import { readFileSync as readFileSync8, realpathSync as realpathSync2 } from "fs";
18441
18721
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
18442
18722
  import { z as z2 } from "zod";
18723
+ var DEFAULT_MAX_CALLS = 12;
18443
18724
  var DEFAULT_MAX_TOTAL_BYTES = 512 * 1024;
18444
18725
  var DEFAULT_MAX_CHUNK_BYTES = 64 * 1024;
18726
+ var DEFAULT_LINE_LIMIT = 300;
18727
+ var MAX_LINE_LIMIT = 500;
18728
+ function positiveInt(value2, fallback) {
18729
+ return Number.isFinite(value2) && value2 && value2 > 0 ? Math.floor(value2) : fallback;
18730
+ }
18731
+ function fitUtf8(text, maxBytes) {
18732
+ if (Buffer.byteLength(text, "utf8") <= maxBytes)
18733
+ return text;
18734
+ const marker = `
18735
+ [... oversized line clipped ...]
18736
+ `;
18737
+ const markerBytes = Buffer.byteLength(marker, "utf8");
18738
+ const bytes = Buffer.from(text, "utf8");
18739
+ if (maxBytes <= markerBytes) {
18740
+ return bytes.subarray(0, maxBytes).toString("utf8").replace(/\uFFFD+$/u, "");
18741
+ }
18742
+ const available = Math.max(0, maxBytes - markerBytes);
18743
+ const headBudget = Math.ceil(available * 0.6);
18744
+ const tailBudget = available - headBudget;
18745
+ const head = bytes.subarray(0, headBudget).toString("utf8").replace(/\uFFFD+$/u, "");
18746
+ const tail = bytes.subarray(Math.max(0, bytes.length - tailBudget)).toString("utf8").replace(/^\uFFFD+/u, "");
18747
+ return `${head}${marker}${tail}`;
18748
+ }
18749
+ function createCompactionLogMcpServer(context) {
18750
+ if (!context.filePath)
18751
+ throw new Error("compaction-log MCP requires filePath");
18752
+ const filePath = realpathSync2(context.filePath);
18753
+ const lines = readFileSync8(filePath, "utf8").split(`
18754
+ `);
18755
+ const maxCalls = positiveInt(context.maxCalls, DEFAULT_MAX_CALLS);
18756
+ const maxTotalBytes = positiveInt(context.maxTotalBytes, DEFAULT_MAX_TOTAL_BYTES);
18757
+ const maxChunkBytes = positiveInt(context.maxChunkBytes, DEFAULT_MAX_CHUNK_BYTES);
18758
+ let calls = 0;
18759
+ let totalBytes = 0;
18760
+ const server = new McpServer2({ name: "compaction-log", version: "1.0.0" });
18761
+ server.tool("read_compaction_log", [
18762
+ `Read the immutable conversation snapshot in bounded chunks. It contains ${lines.length} lines.`,
18763
+ "Start at offset 1, then inspect the final chunk using total_lines from the response.",
18764
+ "Read intermediate chunks only when needed. Return a summary instead of calling after the budget is exhausted."
18765
+ ].join(" "), {
18766
+ offset: z2.number().optional().describe("One-based line number. Defaults to 1."),
18767
+ limit: z2.number().optional().describe(`Number of lines. Defaults to ${DEFAULT_LINE_LIMIT}, max ${MAX_LINE_LIMIT}.`)
18768
+ }, async ({ offset: rawOffset, limit: rawLimit }) => {
18769
+ calls += 1;
18770
+ if (calls > maxCalls || totalBytes >= maxTotalBytes) {
18771
+ return mcpError(JSON.stringify({
18772
+ error: "compaction log read budget exhausted; produce the summary now",
18773
+ calls,
18774
+ max_calls: maxCalls,
18775
+ total_lines: lines.length
18776
+ }));
18777
+ }
18778
+ const offset = positiveInt(rawOffset, 1);
18779
+ const limit = Math.min(positiveInt(rawLimit, DEFAULT_LINE_LIMIT), MAX_LINE_LIMIT);
18780
+ const start = Math.max(0, offset - 1);
18781
+ if (start >= lines.length) {
18782
+ return mcpOk(JSON.stringify({
18783
+ content: "(end of compaction log)",
18784
+ offset,
18785
+ returned_lines: 0,
18786
+ total_lines: lines.length,
18787
+ calls
18788
+ }));
18789
+ }
18790
+ const selected = [];
18791
+ let returnedBytes = 0;
18792
+ let index = start;
18793
+ const callBudget = Math.min(maxChunkBytes, maxTotalBytes - totalBytes);
18794
+ while (index < lines.length && selected.length < limit) {
18795
+ const line = fitUtf8(lines[index] ?? "", Math.max(1, callBudget));
18796
+ const separatorBytes = selected.length > 0 ? 1 : 0;
18797
+ const lineBytes = Buffer.byteLength(line, "utf8");
18798
+ if (selected.length > 0 && returnedBytes + separatorBytes + lineBytes > callBudget)
18799
+ break;
18800
+ selected.push(line);
18801
+ returnedBytes += separatorBytes + lineBytes;
18802
+ index += 1;
18803
+ if (returnedBytes >= callBudget)
18804
+ break;
18805
+ }
18806
+ totalBytes += returnedBytes;
18807
+ return mcpOk(JSON.stringify({
18808
+ content: selected.join(`
18809
+ `),
18810
+ offset,
18811
+ returned_lines: selected.length,
18812
+ returned_bytes: returnedBytes,
18813
+ total_lines: lines.length,
18814
+ total_read_bytes: totalBytes,
18815
+ calls,
18816
+ ...index < lines.length ? { next_offset: index + 1 } : {},
18817
+ ...calls >= maxCalls || totalBytes >= maxTotalBytes ? { note: "Read budget exhausted; produce the summary now." } : {}
18818
+ }));
18819
+ });
18820
+ return server;
18821
+ }
18445
18822
  // ../../packages/core/src/mcp/factories/session-comm.ts
18446
18823
  import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
18447
18824
  import { z as z5 } from "zod";
@@ -18940,7 +19317,9 @@ export {
18940
19317
  createTaskMcpServer,
18941
19318
  createSystemHealthMcpServer,
18942
19319
  createSessionTargetCatalog,
18943
- createSessionCommMcpServer
19320
+ createSessionCommMcpServer,
19321
+ createCompactionLogMcpServer,
19322
+ createAgentHealthMcpServer
18944
19323
  };
18945
19324
 
18946
- //# debugId=0271389E4BFF755564756E2164756E21
19325
+ //# debugId=BC238DE297D39F6964756E2164756E21