negotium 0.1.48 → 0.1.49

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.
@@ -583,6 +583,13 @@ function parseJsonlText(raw) {
583
583
  return raw.trim().split(`
584
584
  `).filter(Boolean).map((line) => JSON.parse(line));
585
585
  }
586
+ function readJsonFile(filePath) {
587
+ try {
588
+ return JSON.parse(readFileSync3(filePath, "utf-8"));
589
+ } catch {
590
+ return null;
591
+ }
592
+ }
586
593
  function lockStaleMs() {
587
594
  const raw = Number.parseInt(process.env.NEGOTIUM_JSONL_LOCK_STALE_MS ?? "", 10);
588
595
  return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_LOCK_STALE_MS;
@@ -3498,7 +3505,7 @@ var init_registry = __esm(async () => {
3498
3505
  });
3499
3506
 
3500
3507
  // ../../packages/core/src/agents/fork.ts
3501
- import { existsSync as existsSync9, unlinkSync as unlinkSync8 } from "fs";
3508
+ import { existsSync as existsSync10, unlinkSync as unlinkSync8 } from "fs";
3502
3509
  function createAgentForkHelpers(host) {
3503
3510
  return {
3504
3511
  async forkAgentSession(options) {
@@ -3530,7 +3537,7 @@ var init_fork = __esm(async () => {
3530
3537
  await init_registry();
3531
3538
  defaultForkHelpers = createAgentForkHelpers({
3532
3539
  forkSession: (agent, options) => getRegistryOperations(agent).forkSession(options),
3533
- exists: existsSync9,
3540
+ exists: existsSync10,
3534
3541
  unlink: unlinkSync8,
3535
3542
  warn: (details, message) => logger.warn(details, message)
3536
3543
  });
@@ -3923,7 +3930,7 @@ var init_runtime_process_leases = __esm(async () => {
3923
3930
 
3924
3931
  // ../../packages/core/src/platform/playwright/browser-processes.ts
3925
3932
  import { execFileSync as execFileSync2 } from "child_process";
3926
- import { readdirSync as readdirSync3, unlinkSync as unlinkSync9 } from "fs";
3933
+ import { readdirSync as readdirSync4, unlinkSync as unlinkSync9 } from "fs";
3927
3934
  import { createServer } from "net";
3928
3935
  import { resolve as resolve7, sep } from "path";
3929
3936
  function isPortInUse(port) {
@@ -4048,7 +4055,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
4048
4055
  }
4049
4056
  function cleanSingletonFiles(userDataDir) {
4050
4057
  try {
4051
- const files = readdirSync3(userDataDir);
4058
+ const files = readdirSync4(userDataDir);
4052
4059
  for (const f of files) {
4053
4060
  if (f.startsWith("Singleton")) {
4054
4061
  try {
@@ -4239,7 +4246,7 @@ var init_vault_crypto = __esm(() => {
4239
4246
 
4240
4247
  // ../../packages/core/src/storage/vault.ts
4241
4248
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync9 } from "fs";
4242
- import { join as join11 } from "path";
4249
+ import { join as join12 } from "path";
4243
4250
  function initializeVaultDatabase(database) {
4244
4251
  database.exec("PRAGMA journal_mode = WAL");
4245
4252
  database.exec("PRAGMA busy_timeout = 5000");
@@ -4274,7 +4281,7 @@ function initializeVaultDatabase(database) {
4274
4281
  }
4275
4282
  }
4276
4283
  function openVaultDatabase(dataDir) {
4277
- const path = join11(dataDir, "vault.db");
4284
+ const path = join12(dataDir, "vault.db");
4278
4285
  mkdirSync9(dataDir, { recursive: true });
4279
4286
  const database = new Database(path, { create: true });
4280
4287
  chmodSync2(path, 384);
@@ -4381,7 +4388,7 @@ import { randomBytes as randomBytes4, timingSafeEqual } from "crypto";
4381
4388
  import { chmodSync as chmodSync3 } from "fs";
4382
4389
  import { createServer as createServer2 } from "net";
4383
4390
  import { tmpdir } from "os";
4384
- import { join as join12 } from "path";
4391
+ import { join as join13 } from "path";
4385
4392
  function deepMapStrings(value2, transform) {
4386
4393
  if (typeof value2 === "string")
4387
4394
  return transform(value2);
@@ -4468,7 +4475,7 @@ function authorized(actual, expected) {
4468
4475
  }
4469
4476
  async function createBrowserVaultBroker(userId) {
4470
4477
  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`);
4478
+ const socketPath = join13(process.platform === "win32" ? tmpdir() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes4(8).toString("hex")}.sock`);
4472
4479
  const retainedForms = new Map;
4473
4480
  const leases = new Map;
4474
4481
  const sockets = new Set;
@@ -4661,19 +4668,19 @@ var init_browser_profiles = __esm(async () => {
4661
4668
  });
4662
4669
 
4663
4670
  // ../../packages/core/src/platform/playwright/manager.ts
4664
- import { execFileSync as execFileSync3, spawn as spawn2 } from "child_process";
4671
+ import { execFileSync as execFileSync3, spawn as spawn3 } from "child_process";
4665
4672
  import { createHash as createHash2, randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "crypto";
4666
4673
  import {
4667
4674
  cpSync,
4668
- existsSync as existsSync10,
4675
+ existsSync as existsSync11,
4669
4676
  mkdirSync as mkdirSync10,
4670
- readFileSync as readFileSync8,
4677
+ readFileSync as readFileSync9,
4671
4678
  renameSync as renameSync5,
4672
4679
  rmSync,
4673
4680
  unlinkSync as unlinkSync10,
4674
4681
  writeFileSync as writeFileSync7
4675
4682
  } from "fs";
4676
- import { dirname as dirname8, join as join13, resolve as resolve9 } from "path";
4683
+ import { dirname as dirname8, join as join14, resolve as resolve9 } from "path";
4677
4684
  function makeInstanceKey(userId, topic) {
4678
4685
  return resolvePlaywrightTopicBinding(userId, topic).instanceKey;
4679
4686
  }
@@ -4713,12 +4720,12 @@ function migrateLegacyTopicProfile(ownerId, topic) {
4713
4720
  if (current2 !== "default" || !hasBrowserProfileTopic(topic))
4714
4721
  return current2;
4715
4722
  const legacyDir = resolve9(BROWSER_PROFILES_DIR, sanitizeTopicName(topic));
4716
- if (!existsSync10(legacyDir))
4723
+ if (!existsSync11(legacyDir))
4717
4724
  return current2;
4718
4725
  const profile = legacyBrowserProfileName(topic);
4719
4726
  const profileDir = defaultProfileDir(ownerId, profile);
4720
4727
  mkdirSync10(dirname8(profileDir), { recursive: true });
4721
- if (!existsSync10(profileDir))
4728
+ if (!existsSync11(profileDir))
4722
4729
  renameSync5(legacyDir, profileDir);
4723
4730
  assignTopicBrowserProfile({ topicId: topic, actorUserId: ownerId, profile });
4724
4731
  logger.info({ ownerId, topic, profile }, "Adopted legacy topic browser profile");
@@ -4768,14 +4775,14 @@ function portFileName(instanceKey) {
4768
4775
  function writePortFile(instanceKey, port) {
4769
4776
  try {
4770
4777
  mkdirSync10(managerHost.portsDir, { recursive: true });
4771
- writeFileSync7(join13(managerHost.portsDir, portFileName(instanceKey)), String(port));
4778
+ writeFileSync7(join14(managerHost.portsDir, portFileName(instanceKey)), String(port));
4772
4779
  } catch (e) {
4773
4780
  logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
4774
4781
  }
4775
4782
  }
4776
4783
  function deletePortFile(instanceKey) {
4777
4784
  try {
4778
- unlinkSync10(join13(managerHost.portsDir, portFileName(instanceKey)));
4785
+ unlinkSync10(join14(managerHost.portsDir, portFileName(instanceKey)));
4779
4786
  } catch (e) {
4780
4787
  logger.warn({ err: e, instanceKey }, "Failed to delete playwright port file");
4781
4788
  }
@@ -5029,7 +5036,7 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
5029
5036
  }
5030
5037
  let proc;
5031
5038
  try {
5032
- proc = spawn2(spawnSpec.command, spawnSpec.args, {
5039
+ proc = spawn3(spawnSpec.command, spawnSpec.args, {
5033
5040
  stdio: ["ignore", "ignore", "pipe"],
5034
5041
  detached: false,
5035
5042
  env: childEnv
@@ -5262,11 +5269,11 @@ async function cloneProfileForChild(opts) {
5262
5269
  killInstance(dstKey);
5263
5270
  await delay(500);
5264
5271
  }
5265
- if (!existsSync10(srcDir)) {
5272
+ if (!existsSync11(srcDir)) {
5266
5273
  return { copied: false, srcDir, dstDir, reason: "src-missing" };
5267
5274
  }
5268
5275
  try {
5269
- if (existsSync10(dstDir)) {
5276
+ if (existsSync11(dstDir)) {
5270
5277
  rmSync(dstDir, { recursive: true, force: true });
5271
5278
  }
5272
5279
  mkdirSync10(dirname8(dstDir), { recursive: true });
@@ -5945,8 +5952,8 @@ RULES:
5945
5952
  - Keep the entire summary under 1500 words.`, CHARS_PER_TEXT_TOKEN = 3.5, CHARS_PER_CJK_TOKEN = 0.9;
5946
5953
 
5947
5954
  // ../../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";
5955
+ import { existsSync as existsSync12, mkdirSync as mkdirSync11, readFileSync as readFileSync10, renameSync as renameSync6, statSync as statSync4, writeFileSync as writeFileSync8 } from "fs";
5956
+ import { dirname as dirname9, join as join15 } from "path";
5950
5957
  function safeUserIdComponent2(userId) {
5951
5958
  const str = String(userId);
5952
5959
  if (!str || /[/\\]|\.\./.test(str)) {
@@ -5965,14 +5972,14 @@ function taskScopeKey(opts) {
5965
5972
  return opts.topicId?.trim() || opts.session || "default";
5966
5973
  }
5967
5974
  function getTaskFilePath(userId, scopeKey) {
5968
- return join14(resolveStorageDataDir(), "tasks", safeUserIdComponent2(userId), `${safeTaskScopeKey(scopeKey)}.json`);
5975
+ return join15(resolveStorageDataDir(), "tasks", safeUserIdComponent2(userId), `${safeTaskScopeKey(scopeKey)}.json`);
5969
5976
  }
5970
5977
  function readTasks(userId, scopeKey) {
5971
5978
  const path = getTaskFilePath(userId, scopeKey);
5972
- if (!existsSync11(path))
5979
+ if (!existsSync12(path))
5973
5980
  return [];
5974
5981
  try {
5975
- const parsed = JSON.parse(readFileSync9(path, "utf-8"));
5982
+ const parsed = JSON.parse(readFileSync10(path, "utf-8"));
5976
5983
  return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
5977
5984
  } catch (e) {
5978
5985
  logger.warn({ err: e, path }, "tasks: failed to read task store");
@@ -6141,14 +6148,14 @@ function deepMapStrings2(value2, fn) {
6141
6148
  }
6142
6149
 
6143
6150
  // ../../packages/core/src/security/sensitive-path.ts
6144
- import { realpathSync as realpathSync2 } from "fs";
6151
+ import { realpathSync as realpathSync3 } from "fs";
6145
6152
  import { resolve as resolve10 } from "path";
6146
6153
  function isSensitivePath(filePath) {
6147
6154
  const normalized = resolve10(filePath);
6148
6155
  if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
6149
6156
  return true;
6150
6157
  try {
6151
- const real = realpathSync2(normalized);
6158
+ const real = realpathSync3(normalized);
6152
6159
  if (real !== normalized)
6153
6160
  return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
6154
6161
  } catch {}
@@ -6331,7 +6338,7 @@ function deriveBgBashContextCapability(runtimeCapability, userId, topic) {
6331
6338
  var init_context = () => {};
6332
6339
 
6333
6340
  // ../../packages/core/src/platform/background-bash/manager.ts
6334
- import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
6341
+ import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
6335
6342
  import { randomBytes as randomBytes6 } from "crypto";
6336
6343
  function makeBgBashKey(_userId, _topic) {
6337
6344
  return "runtime";
@@ -6358,7 +6365,7 @@ function createBackgroundBashManager(options = {}) {
6358
6365
  const now = options.now ?? Date.now;
6359
6366
  const wait = options.delay ?? delay;
6360
6367
  const portPids = options.portPids ?? defaultPortPids;
6361
- const spawnImpl = options.spawn ?? ((command, args, spawnOptions) => spawn3(command, [...args], spawnOptions));
6368
+ const spawnImpl = options.spawn ?? ((command, args, spawnOptions) => spawn4(command, [...args], spawnOptions));
6362
6369
  function contextKey(userId, topic) {
6363
6370
  return `${userId}\x00${topic}`;
6364
6371
  }
@@ -7209,8 +7216,8 @@ __export(exports_claude_provider, {
7209
7216
  claudeBuiltInTools: () => claudeBuiltInTools,
7210
7217
  buildClaudeDisallowedTools: () => buildClaudeDisallowedTools
7211
7218
  });
7212
- import { spawn as spawn4 } from "child_process";
7213
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
7219
+ import { spawn as spawn5 } from "child_process";
7220
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
7214
7221
  import { query } from "@anthropic-ai/claude-agent-sdk";
7215
7222
  function claudeBuiltInTools(opts) {
7216
7223
  return opts.toolPolicy ? [] : undefined;
@@ -7263,7 +7270,7 @@ function buildClaudePrompt(opts) {
7263
7270
  source: {
7264
7271
  type: "base64",
7265
7272
  media_type: attachment.mimeType,
7266
- data: readFileSync10(attachment.path).toString("base64")
7273
+ data: readFileSync11(attachment.path).toString("base64")
7267
7274
  }
7268
7275
  });
7269
7276
  } catch (err) {
@@ -7286,7 +7293,7 @@ function buildClaudePrompt(opts) {
7286
7293
  parent_tool_use_id: null
7287
7294
  });
7288
7295
  }
7289
- function signalProcessTree(pid, signal) {
7296
+ function signalProcessTree2(pid, signal) {
7290
7297
  try {
7291
7298
  process.kill(-pid, signal);
7292
7299
  return true;
@@ -7301,7 +7308,7 @@ function signalProcessTree(pid, signal) {
7301
7308
  }
7302
7309
  }
7303
7310
  function spawnClaudeCodeProcessWithTreeKill(options) {
7304
- const child = spawn4(options.command, options.args, {
7311
+ const child = spawn5(options.command, options.args, {
7305
7312
  cwd: options.cwd,
7306
7313
  detached: true,
7307
7314
  env: options.env,
@@ -7321,13 +7328,13 @@ function spawnClaudeCodeProcessWithTreeKill(options) {
7321
7328
  const kill = (signal) => {
7322
7329
  if (!child.pid)
7323
7330
  return child.kill(signal);
7324
- const ok = signalProcessTree(child.pid, signal);
7331
+ const ok = signalProcessTree2(child.pid, signal);
7325
7332
  if (signal !== "SIGKILL") {
7326
7333
  clearKillTimer();
7327
7334
  killTimer = setTimeout(() => {
7328
7335
  if (!exited && child.pid) {
7329
7336
  logger.warn({ pid: child.pid }, "Claude process tree still alive after SIGTERM; SIGKILL");
7330
- signalProcessTree(child.pid, "SIGKILL");
7337
+ signalProcessTree2(child.pid, "SIGKILL");
7331
7338
  }
7332
7339
  }, CLAUDE_ABORT_SIGKILL_DELAY_MS);
7333
7340
  killTimer.unref?.();
@@ -7391,7 +7398,7 @@ function isAbortError(err) {
7391
7398
  }
7392
7399
  async function* claudeProvider(opts) {
7393
7400
  const claudeExecutable = hostedClaudeCodeExecutablePath();
7394
- if (claudeExecutable && !existsSync12(claudeExecutable)) {
7401
+ if (claudeExecutable && !existsSync13(claudeExecutable)) {
7395
7402
  yield {
7396
7403
  type: "error",
7397
7404
  content: `Claude CLI not found at ${claudeExecutable}. Install Claude Code or configure the executable path.`
@@ -7686,17 +7693,17 @@ var init_claude_provider = __esm(async () => {
7686
7693
  });
7687
7694
 
7688
7695
  // ../../packages/core/src/version.ts
7689
- var NEGOTIUM_VERSION = "0.1.48";
7696
+ var NEGOTIUM_VERSION = "0.1.49";
7690
7697
 
7691
7698
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
7692
- import { spawn as spawn5 } from "child_process";
7699
+ import { spawn as spawn6 } from "child_process";
7693
7700
  import { randomUUID as randomUUID8 } from "crypto";
7694
7701
  import {
7695
7702
  chmodSync as chmodSync4,
7696
7703
  copyFileSync,
7697
- existsSync as existsSync13,
7704
+ existsSync as existsSync14,
7698
7705
  mkdtempSync,
7699
- readFileSync as readFileSync11,
7706
+ readFileSync as readFileSync12,
7700
7707
  renameSync as renameSync7,
7701
7708
  rmSync as rmSync2,
7702
7709
  unlinkSync as unlinkSync11,
@@ -7704,16 +7711,16 @@ import {
7704
7711
  } from "fs";
7705
7712
  import { createRequire as createRequire2 } from "module";
7706
7713
  import { tmpdir as tmpdir2 } from "os";
7707
- import { dirname as dirname10, join as join15 } from "path";
7714
+ import { dirname as dirname10, join as join16 } from "path";
7708
7715
  function readPackageVersion(packageJsonPath) {
7709
- const parsed = JSON.parse(readFileSync11(packageJsonPath, "utf8"));
7716
+ const parsed = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
7710
7717
  if (typeof parsed.version !== "string" || !parsed.version.trim()) {
7711
7718
  throw new Error(`Codex package has no valid version: ${packageJsonPath}`);
7712
7719
  }
7713
7720
  return parsed.version;
7714
7721
  }
7715
7722
  function codexCliScriptPath() {
7716
- return join15(dirname10(bundledCodexPackagePath), "bin", "codex.js");
7723
+ return join16(dirname10(bundledCodexPackagePath), "bin", "codex.js");
7717
7724
  }
7718
7725
  function parseCodexModelCache(contents, sourcePath) {
7719
7726
  let parsed;
@@ -7728,7 +7735,7 @@ function parseCodexModelCache(contents, sourcePath) {
7728
7735
  return parsed;
7729
7736
  }
7730
7737
  function readCodexModelCache(cachePath) {
7731
- const contents = readFileSync11(cachePath, "utf8");
7738
+ const contents = readFileSync12(cachePath, "utf8");
7732
7739
  return { contents, parsed: parseCodexModelCache(contents, cachePath) };
7733
7740
  }
7734
7741
  function readCompatibleCodexModelCache(cachePath) {
@@ -7740,7 +7747,7 @@ function readCompatibleCodexModelCache(cachePath) {
7740
7747
  return cache;
7741
7748
  }
7742
7749
  function writePrivateFileAtomic(path, contents) {
7743
- if (existsSync13(path) && readFileSync11(path, "utf8") === contents)
7750
+ if (existsSync14(path) && readFileSync12(path, "utf8") === contents)
7744
7751
  return;
7745
7752
  const tempPath = `${path}.${process.pid}.${randomUUID8()}.tmp`;
7746
7753
  try {
@@ -7754,10 +7761,10 @@ function writePrivateFileAtomic(path, contents) {
7754
7761
  }
7755
7762
  }
7756
7763
  function bundledCodexModelCachePath(authFilePath) {
7757
- return join15(dirname10(authFilePath), NEGOTIUM_MODEL_CACHE);
7764
+ return join16(dirname10(authFilePath), NEGOTIUM_MODEL_CACHE);
7758
7765
  }
7759
7766
  async function bootstrapCodexModelCache(codexHome, cachePath) {
7760
- const child = spawn5(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
7767
+ const child = spawn6(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
7761
7768
  env: { ...process.env, CODEX_HOME: codexHome },
7762
7769
  stdio: ["pipe", "pipe", "pipe"]
7763
7770
  });
@@ -7777,7 +7784,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
7777
7784
  } catch {}
7778
7785
  if (error)
7779
7786
  reject(error);
7780
- else if (!existsSync13(cachePath))
7787
+ else if (!existsSync14(cachePath))
7781
7788
  reject(new Error("Codex did not create its model cache"));
7782
7789
  else
7783
7790
  resolve11();
@@ -7840,15 +7847,15 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
7840
7847
  }
7841
7848
  async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
7842
7849
  const sourceHome = dirname10(authFilePath);
7843
- const isolatedHome = mkdtempSync(join15(tmpdir2(), "negotium-codex-models-"));
7844
- const isolatedCachePath = join15(isolatedHome, "models_cache.json");
7850
+ const isolatedHome = mkdtempSync(join16(tmpdir2(), "negotium-codex-models-"));
7851
+ const isolatedCachePath = join16(isolatedHome, "models_cache.json");
7845
7852
  try {
7846
- const isolatedAuthPath = join15(isolatedHome, "auth.json");
7853
+ const isolatedAuthPath = join16(isolatedHome, "auth.json");
7847
7854
  copyFileSync(authFilePath, isolatedAuthPath);
7848
7855
  chmodSync4(isolatedAuthPath, 384);
7849
- const sourceConfigPath = join15(sourceHome, "config.toml");
7850
- if (existsSync13(sourceConfigPath)) {
7851
- const isolatedConfigPath = join15(isolatedHome, "config.toml");
7856
+ const sourceConfigPath = join16(sourceHome, "config.toml");
7857
+ if (existsSync14(sourceConfigPath)) {
7858
+ const isolatedConfigPath = join16(isolatedHome, "config.toml");
7852
7859
  copyFileSync(sourceConfigPath, isolatedConfigPath);
7853
7860
  chmodSync4(isolatedConfigPath, 384);
7854
7861
  }
@@ -7862,22 +7869,22 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
7862
7869
  const codexHome = dirname10(authFilePath);
7863
7870
  const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
7864
7871
  if (configuredCachePath) {
7865
- if (!existsSync13(configuredCachePath)) {
7872
+ if (!existsSync14(configuredCachePath)) {
7866
7873
  throw new Error(`Configured Codex model cache does not exist: ${configuredCachePath}`);
7867
7874
  }
7868
7875
  readCompatibleCodexModelCache(configuredCachePath);
7869
7876
  return configuredCachePath;
7870
7877
  }
7871
7878
  const bundledCachePath = bundledCodexModelCachePath(authFilePath);
7872
- const sharedCachePath = join15(codexHome, "models_cache.json");
7873
- if (existsSync13(sharedCachePath)) {
7879
+ const sharedCachePath = join16(codexHome, "models_cache.json");
7880
+ if (existsSync14(sharedCachePath)) {
7874
7881
  try {
7875
7882
  const shared = readCompatibleCodexModelCache(sharedCachePath);
7876
7883
  writePrivateFileAtomic(bundledCachePath, shared.contents);
7877
7884
  return bundledCachePath;
7878
7885
  } catch {}
7879
7886
  }
7880
- if (existsSync13(bundledCachePath)) {
7887
+ if (existsSync14(bundledCachePath)) {
7881
7888
  try {
7882
7889
  readCompatibleCodexModelCache(bundledCachePath);
7883
7890
  return bundledCachePath;
@@ -7889,7 +7896,7 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
7889
7896
  }
7890
7897
  function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
7891
7898
  const codexHome = dirname10(authFilePath);
7892
- const outputPath = join15(codexHome, NEGOTIUM_MODEL_CATALOG);
7899
+ const outputPath = join16(codexHome, NEGOTIUM_MODEL_CATALOG);
7893
7900
  const parsed = readCodexModelCache(sourcePath).parsed;
7894
7901
  const models = parsed.models.map((model, index) => {
7895
7902
  if (!model || typeof model !== "object" || Array.isArray(model)) {
@@ -8136,7 +8143,7 @@ var init_codex_tree_kill = __esm(() => {
8136
8143
  });
8137
8144
 
8138
8145
  // ../../packages/core/src/agents/tool-format.ts
8139
- import { readFileSync as readFileSync12, statSync as statSync5 } from "fs";
8146
+ import { readFileSync as readFileSync13, statSync as statSync5 } from "fs";
8140
8147
  import { isAbsolute as isAbsolute2, resolve as resolve11 } from "path";
8141
8148
  import { diffLines as computeLineDiff } from "diff";
8142
8149
  function summarizeDisplayText(value2) {
@@ -8465,7 +8472,7 @@ function sourceStartLine(input, before, after, cwd) {
8465
8472
  try {
8466
8473
  if (statSync5(path).size > TOOL_DIFF_SOURCE_MAX_BYTES)
8467
8474
  return 1;
8468
- const source = readFileSync12(path, "utf8").replace(/\r\n?/g, `
8475
+ const source = readFileSync13(path, "utf8").replace(/\r\n?/g, `
8469
8476
  `);
8470
8477
  for (const candidate of [before, after].filter(Boolean)) {
8471
8478
  const index = source.indexOf(candidate);
@@ -8658,8 +8665,8 @@ __export(exports_codex_provider, {
8658
8665
  codexProvider: () => codexProvider
8659
8666
  });
8660
8667
  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";
8668
+ import { existsSync as existsSync15, readFileSync as readFileSync14, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
8669
+ import { dirname as dirname11, isAbsolute as isAbsolute3, join as join17, relative, resolve as resolve12 } from "path";
8663
8670
  import { Codex } from "@openai/codex-sdk";
8664
8671
  function mapEffort(effort) {
8665
8672
  if (!effort)
@@ -8673,12 +8680,12 @@ function codexMcpServerName(name) {
8673
8680
  return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
8674
8681
  }
8675
8682
  function globalCodexMcpServerNames(authFilePath) {
8676
- const configPath = join16(dirname11(authFilePath), "config.toml");
8677
- if (!existsSync14(configPath))
8683
+ const configPath = join17(dirname11(authFilePath), "config.toml");
8684
+ if (!existsSync15(configPath))
8678
8685
  return [];
8679
8686
  try {
8680
8687
  const names = new Set;
8681
- for (const line of readFileSync13(configPath, "utf8").split(`
8688
+ for (const line of readFileSync14(configPath, "utf8").split(`
8682
8689
  `)) {
8683
8690
  const match = line.match(/^\s*\[\s*mcp_servers\.((?:"(?:\\.|[^"])*")|(?:'[^']*')|[^.\]\s]+)(?:[.\]])/);
8684
8691
  const raw = match?.[1];
@@ -8769,19 +8776,19 @@ function summarizeMcpToolCallResult(item) {
8769
8776
  }
8770
8777
  function canonicalPath(path) {
8771
8778
  try {
8772
- return realpathSync3(resolve12(path));
8779
+ return realpathSync4(resolve12(path));
8773
8780
  } catch {
8774
8781
  return resolve12(path);
8775
8782
  }
8776
8783
  }
8777
8784
  function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
8778
- if (!existsSync14(path))
8785
+ if (!existsSync15(path))
8779
8786
  return null;
8780
8787
  try {
8781
8788
  const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
8782
8789
  if (statSync6(path).size > byteLimit)
8783
8790
  return;
8784
- const content = readFileSync13(path);
8791
+ const content = readFileSync14(path);
8785
8792
  if (content.byteLength > byteLimit || content.includes(0))
8786
8793
  return;
8787
8794
  return content.toString("utf8");
@@ -8987,7 +8994,7 @@ async function* codexProvider(opts) {
8987
8994
  const filePreviews = new CodexFilePreviewTracker(opts.cwd);
8988
8995
  const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
8989
8996
  const codexAuthPath = hostedCodexAuthFilePath();
8990
- if (!existsSync14(codexAuthPath)) {
8997
+ if (!existsSync15(codexAuthPath)) {
8991
8998
  yield {
8992
8999
  type: "error",
8993
9000
  content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
@@ -9026,7 +9033,7 @@ async function* codexProvider(opts) {
9026
9033
  model: opts.model ?? "(sdk default)",
9027
9034
  effort: opts.effort ?? "(off)",
9028
9035
  cwd: opts.cwd,
9029
- cwdExists: existsSync14(opts.cwd),
9036
+ cwdExists: existsSync15(opts.cwd),
9030
9037
  resume: Boolean(opts.sessionId),
9031
9038
  mcpServerCount: Object.keys(codexMcpServers).length
9032
9039
  }, "codexProvider: starting turn");
@@ -9467,9 +9474,9 @@ var init_maestro_provider = __esm(async () => {
9467
9474
  });
9468
9475
 
9469
9476
  // ../../packages/core/src/agents/index.ts
9470
- import { existsSync as existsSync15 } from "fs";
9477
+ import { existsSync as existsSync16 } from "fs";
9471
9478
  import { homedir as homedir8 } from "os";
9472
- import { join as join17 } from "path";
9479
+ import { join as join18 } from "path";
9473
9480
  async function* dispatchAgent(opts) {
9474
9481
  switch (opts.agent) {
9475
9482
  case "claude": {
@@ -9503,11 +9510,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
9503
9510
  switch (agent) {
9504
9511
  case "claude": {
9505
9512
  const encodedCwd = encodeClaudeCwd(cwd);
9506
- const path = join17(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
9507
- return !existsSync15(path);
9513
+ const path = join18(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
9514
+ return !existsSync16(path);
9508
9515
  }
9509
9516
  case "codex": {
9510
- const sessionsDir = join17(process.env.CODEX_HOME || join17(homedir8(), ".codex"), "sessions");
9517
+ const sessionsDir = join18(process.env.CODEX_HOME || join18(homedir8(), ".codex"), "sessions");
9511
9518
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
9512
9519
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
9513
9520
  return false;
@@ -9516,7 +9523,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
9516
9523
  }
9517
9524
  case "maestro": {
9518
9525
  const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
9519
- return !existsSync15(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
9526
+ return !existsSync16(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
9520
9527
  }
9521
9528
  default: {
9522
9529
  const _exhaustive = agent;
@@ -9582,10 +9589,10 @@ var init_agents = __esm(async () => {
9582
9589
  });
9583
9590
 
9584
9591
  // ../../packages/core/src/prompts/builders.ts
9585
- import { readFileSync as readFileSync14 } from "fs";
9592
+ import { readFileSync as readFileSync15 } from "fs";
9586
9593
  import { resolve as resolve14 } from "path";
9587
9594
  function loadPrompt(filename, dir = SESSIONS_DIR) {
9588
- const raw = readFileSync14(resolve14(dir, filename), "utf-8");
9595
+ const raw = readFileSync15(resolve14(dir, filename), "utf-8");
9589
9596
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
9590
9597
  }
9591
9598
  function replaceVars(template, vars) {
@@ -9634,7 +9641,7 @@ function visualDesignGuide() {
9634
9641
  return _visualDesignGuide;
9635
9642
  }
9636
9643
  function loadAgentPrompt(filename) {
9637
- const raw = readFileSync14(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
9644
+ const raw = readFileSync15(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
9638
9645
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
9639
9646
  if (!match)
9640
9647
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -10023,9 +10030,9 @@ var init_api_topic_brief = __esm(async () => {
10023
10030
  });
10024
10031
 
10025
10032
  // ../../packages/core/src/storage/wiki.ts
10026
- import { basename as basename3, dirname as dirname12, join as join18 } from "path";
10033
+ import { basename as basename3, dirname as dirname12, join as join19 } from "path";
10027
10034
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
10028
- return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
10035
+ return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join19(workspaceDir, "wiki");
10029
10036
  }
10030
10037
  var init_wiki = __esm(async () => {
10031
10038
  await init_storage_host();
@@ -10033,8 +10040,8 @@ var init_wiki = __esm(async () => {
10033
10040
 
10034
10041
  // ../../packages/core/src/agents/archiver.ts
10035
10042
  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";
10043
+ import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync16, statSync as statSync7 } from "fs";
10044
+ import { join as join20 } from "path";
10038
10045
  function resolveMemoryLanguage() {
10039
10046
  const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
10040
10047
  return override && override.length > 0 ? override : resolveOutputLanguage();
@@ -10285,10 +10292,10 @@ function distillOneLine(summaryMd) {
10285
10292
  return "";
10286
10293
  }
10287
10294
  function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
10288
- const dir = join19(storage.getWikiDir(), "summaries");
10295
+ const dir = join20(storage.getWikiDir(), "summaries");
10289
10296
  if (!storage.fileExists(dir))
10290
10297
  return null;
10291
- const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
10298
+ const predicted = join20(dir, wikiSummaryFilename(date, topicTitle, topicId));
10292
10299
  if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
10293
10300
  return predicted;
10294
10301
  let best = null;
@@ -10296,7 +10303,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
10296
10303
  if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
10297
10304
  continue;
10298
10305
  }
10299
- const p = join19(dir, f);
10306
+ const p = join20(dir, f);
10300
10307
  try {
10301
10308
  const m = storage.fileModifiedAt(p);
10302
10309
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -10378,9 +10385,9 @@ var init_archiver = __esm(async () => {
10378
10385
  defaultArchiverRuntime = createArchiverRuntime({
10379
10386
  storage: {
10380
10387
  getWikiDir: getSharedWikiDir,
10381
- fileExists: existsSync16,
10382
- listDirectory: readdirSync4,
10383
- readTextFile: (path) => readFileSync15(path, "utf-8"),
10388
+ fileExists: existsSync17,
10389
+ listDirectory: readdirSync5,
10390
+ readTextFile: (path) => readFileSync16(path, "utf-8"),
10384
10391
  fileSize: (path) => statSync7(path).size,
10385
10392
  fileModifiedAt: (path) => statSync7(path).mtimeMs,
10386
10393
  getGeneralTopicId: (userId) => ensurePersonalGeneral(userId).id,
@@ -10494,13 +10501,13 @@ function formatTopicArchiveTranscriptRecord(row, topicTitle, index) {
10494
10501
 
10495
10502
  // ../../packages/core/src/storage/topic-archive.ts
10496
10503
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
10497
- import { join as join20 } from "path";
10504
+ import { join as join21 } from "path";
10498
10505
  function archiveTopicMessages(topicId, topicTitle, options = {}) {
10499
10506
  const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
10500
10507
  if (rows.length === 0)
10501
10508
  return null;
10502
10509
  const safeTopic = sanitizeTopicName(topicTitle, true);
10503
- const archiveDir = join20(getSharedWikiDir(), "archive");
10510
+ const archiveDir = join21(getSharedWikiDir(), "archive");
10504
10511
  mkdirSync12(archiveDir, { recursive: true });
10505
10512
  const date = new Date().toISOString().slice(0, 10);
10506
10513
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
@@ -10513,7 +10520,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
10513
10520
  let path;
10514
10521
  while (true) {
10515
10522
  filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
10516
- path = join20(archiveDir, filename);
10523
+ path = join21(archiveDir, filename);
10517
10524
  try {
10518
10525
  writeFileSync10(path, body, { flag: "wx" });
10519
10526
  break;
@@ -10558,7 +10565,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
10558
10565
  const entries = readRawConversation(userId, topicTitle);
10559
10566
  if (entries.length === 0)
10560
10567
  return null;
10561
- const archiveDir = join20(getSharedWikiDir(), "archive");
10568
+ const archiveDir = join21(getSharedWikiDir(), "archive");
10562
10569
  mkdirSync12(archiveDir, { recursive: true });
10563
10570
  const safeTopic = sanitizeTopicName(topicTitle, true);
10564
10571
  const date = new Date().toISOString().slice(0, 10);
@@ -10586,7 +10593,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
10586
10593
  let counter = 1;
10587
10594
  while (true) {
10588
10595
  const suffix = counter === 1 ? "" : `_${counter}`;
10589
- const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
10596
+ const path = join21(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
10590
10597
  try {
10591
10598
  writeFileSync10(path, body, { flag: "wx" });
10592
10599
  logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
@@ -11143,7 +11150,7 @@ var init_usage_alert = __esm(() => {
11143
11150
  import { randomUUID as randomUUID10 } from "crypto";
11144
11151
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "fs";
11145
11152
  import { tmpdir as tmpdir3 } from "os";
11146
- import { join as join21 } from "path";
11153
+ import { join as join22 } from "path";
11147
11154
  function previousCompactedSummary(entries) {
11148
11155
  for (let index = entries.length - 2;index >= 0; index -= 1) {
11149
11156
  const request = entries[index]?.event;
@@ -11273,7 +11280,7 @@ function formatCompactElapsed(startedAt) {
11273
11280
  async function summarizeTopicContext(request) {
11274
11281
  const startedAt = Date.now();
11275
11282
  const sessionIds = [];
11276
- const compactCwd = mkdtempSync2(join21(tmpdir3(), "negotium-compact-"));
11283
+ const compactCwd = mkdtempSync2(join22(tmpdir3(), "negotium-compact-"));
11277
11284
  const abortController = new AbortController;
11278
11285
  const relayAbort = () => abortController.abort(request.signal?.reason);
11279
11286
  if (request.signal?.aborted)
@@ -11300,7 +11307,7 @@ async function summarizeTopicContext(request) {
11300
11307
  let error = "";
11301
11308
  let toolViolation = false;
11302
11309
  let compactionLogCalls = 0;
11303
- const compactionLogPath = join21(compactCwd, "conversation.log");
11310
+ const compactionLogPath = join22(compactCwd, "conversation.log");
11304
11311
  try {
11305
11312
  const compactionMcp = useCompactionLog ? {
11306
11313
  compact_log: {
@@ -11896,14 +11903,14 @@ var init_derive = __esm(async () => {
11896
11903
  });
11897
11904
 
11898
11905
  // ../../packages/core/src/query/session-inbox-path.ts
11899
- import { join as join22 } from "path";
11906
+ import { join as join23 } from "path";
11900
11907
  function sessionInboxPath(userId, topicId) {
11901
11908
  const key = Buffer.from(topicId, "utf8").toString("base64url");
11902
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
11909
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
11903
11910
  }
11904
11911
  function scheduledSessionInboxPath(userId, topicId) {
11905
11912
  const key = Buffer.from(topicId, "utf8").toString("base64url");
11906
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
11913
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
11907
11914
  }
11908
11915
  var TOPIC_ID_FILE_PREFIX = "topic-id-", JSONL_SUFFIX = ".jsonl", SCHEDULE_SUFFIX = ".schedule";
11909
11916
  var init_session_inbox_path = __esm(() => {
@@ -11912,13 +11919,13 @@ var init_session_inbox_path = __esm(() => {
11912
11919
 
11913
11920
  // ../../packages/core/src/query/session-inbox-cleanup.ts
11914
11921
  import { unlinkSync as unlinkSync14 } from "fs";
11915
- import { basename as basename4, join as join23 } from "path";
11922
+ import { basename as basename4, join as join24 } from "path";
11916
11923
  function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
11917
11924
  const live = sessionInboxPath(userId, topicId);
11918
11925
  const scheduled = scheduledSessionInboxPath(userId, topicId);
11919
11926
  const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
11920
11927
  if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename4(legacyTopicTitle) === legacyTopicTitle) {
11921
- const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
11928
+ const legacyBase = join24(SESSION_INBOX_DIR, userId, legacyTopicTitle);
11922
11929
  for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
11923
11930
  candidates.add(`${legacyBase}${suffix}`);
11924
11931
  }
@@ -11944,16 +11951,16 @@ var init_session_inbox_cleanup = __esm(() => {
11944
11951
 
11945
11952
  // ../../packages/core/src/query/state.ts
11946
11953
  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";
11954
+ import { basename as basename5, join as join25 } from "path";
11948
11955
  function createQueryStateStore(options) {
11949
11956
  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`);
11957
+ const queryStateDirPath = (userId) => join25(options.usersLogDir, String(userId), "active-queries");
11958
+ const queryStateFile = (userId, topicId) => join25(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
11952
11959
  const legacyQueryStateFile = (userId, topicName) => {
11953
11960
  if (!topicName || topicName === "." || topicName === ".." || basename5(topicName) !== topicName) {
11954
11961
  return null;
11955
11962
  }
11956
- return join24(queryStateDirPath(userId), `${topicName}.json`);
11963
+ return join25(queryStateDirPath(userId), `${topicName}.json`);
11957
11964
  };
11958
11965
  return {
11959
11966
  write(userId, topicId, topicName, task) {
@@ -12107,35 +12114,35 @@ import {
12107
12114
  closeSync as closeSync3,
12108
12115
  mkdirSync as mkdirSync16,
12109
12116
  openSync as openSync3,
12110
- readdirSync as readdirSync5,
12111
- readFileSync as readFileSync16,
12117
+ readdirSync as readdirSync6,
12118
+ readFileSync as readFileSync17,
12112
12119
  statSync as statSync8,
12113
12120
  unlinkSync as unlinkSync16,
12114
12121
  writeFileSync as writeFileSync14
12115
12122
  } from "fs";
12116
- import { dirname as dirname14, join as join25 } from "path";
12123
+ import { dirname as dirname14, join as join26 } from "path";
12117
12124
  function pendingAskDir(userId) {
12118
12125
  const rawUserId = String(userId);
12119
12126
  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);
12127
+ return join26(resolveStorageSessionAsksDir(), safeUserId);
12121
12128
  }
12122
12129
  function encodeAskKey(key) {
12123
12130
  return JSON.stringify([key.from, key.to]);
12124
12131
  }
12125
12132
  function pendingAskPath(key) {
12126
12133
  const digest = createHash4("sha256").update(encodeAskKey(key)).digest("hex");
12127
- return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12134
+ return join26(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12128
12135
  }
12129
12136
  function v2PendingAskPath(key) {
12130
12137
  const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
12131
- return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
12138
+ return join26(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
12132
12139
  }
12133
12140
  function legacyPendingAskPath(key) {
12134
12141
  if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
12135
12142
  return null;
12136
12143
  }
12137
12144
  const dir = pendingAskDir(key.userId);
12138
- const candidate = join25(dir, `${key.from}___${key.to}.pending`);
12145
+ const candidate = join26(dir, `${key.from}___${key.to}.pending`);
12139
12146
  return dirname14(candidate) === dir ? candidate : null;
12140
12147
  }
12141
12148
  function parsePendingAskFilename(fileName) {
@@ -12157,7 +12164,7 @@ function parsePendingAskFilename(fileName) {
12157
12164
  }
12158
12165
  function readPendingAskFile(path, fallback) {
12159
12166
  try {
12160
- const raw = readFileSync16(path, "utf8").trim();
12167
+ const raw = readFileSync17(path, "utf8").trim();
12161
12168
  if (!raw)
12162
12169
  return null;
12163
12170
  if (!raw.startsWith("{")) {
@@ -12366,7 +12373,7 @@ function listPendingAsksForCaller(args) {
12366
12373
  const dir = pendingAskDir(args.userId);
12367
12374
  let files;
12368
12375
  try {
12369
- files = readdirSync5(dir);
12376
+ files = readdirSync6(dir);
12370
12377
  } catch {
12371
12378
  return [];
12372
12379
  }
@@ -12376,7 +12383,7 @@ function listPendingAsksForCaller(args) {
12376
12383
  const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
12377
12384
  if (!parsed)
12378
12385
  continue;
12379
- const path = join25(dir, fileName);
12386
+ const path = join26(dir, fileName);
12380
12387
  const record = readPendingAskFile(path, {
12381
12388
  userId: args.userId,
12382
12389
  from: parsed.from,
@@ -12412,13 +12419,13 @@ function deletePendingAsksForTopic(args) {
12412
12419
  const dir = pendingAskDir(args.userId);
12413
12420
  let files;
12414
12421
  try {
12415
- files = readdirSync5(dir);
12422
+ files = readdirSync6(dir);
12416
12423
  } catch {
12417
12424
  return 0;
12418
12425
  }
12419
12426
  let deleted = 0;
12420
12427
  for (const fileName of files) {
12421
- const path = join25(dir, fileName);
12428
+ const path = join26(dir, fileName);
12422
12429
  const parsed = parsePendingAskFilename(fileName);
12423
12430
  const record = readPendingAskFile(path, {
12424
12431
  userId: args.userId,
@@ -13087,7 +13094,7 @@ var init_lifecycle = __esm(async () => {
13087
13094
  // ../../packages/core/src/runtime/attachments.ts
13088
13095
  import { randomUUID as randomUUID13 } from "crypto";
13089
13096
  import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
13090
- import { basename as basename6, join as join26 } from "path";
13097
+ import { basename as basename6, join as join27 } from "path";
13091
13098
  function workspaceCwdFor(topicId) {
13092
13099
  return resolveTopicWorkspaceDir(topicId);
13093
13100
  }
@@ -13100,7 +13107,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
13100
13107
  if (!attachmentIds?.length)
13101
13108
  return [];
13102
13109
  const out = [];
13103
- const destDir = join26(workspaceCwdFor(topicId), "attachments", queryId);
13110
+ const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
13104
13111
  for (const rawId of attachmentIds) {
13105
13112
  if (typeof rawId !== "string")
13106
13113
  continue;
@@ -13117,7 +13124,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
13117
13124
  mkdirSync17(destDir, { recursive: true });
13118
13125
  const index = String(out.length + 1).padStart(2, "0");
13119
13126
  const safeName = safeAttachmentFilename(attachment.filename, fileId);
13120
- const destPath = join26(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
13127
+ const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
13121
13128
  copyFileSync2(sourcePath, destPath);
13122
13129
  out.push({
13123
13130
  id: attachment.id,
@@ -13147,10 +13154,10 @@ function promptWithAttachments(prompt, attachments) {
13147
13154
  return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
13148
13155
  }
13149
13156
  function ingestAttachment(args) {
13150
- const destDir = join26(workspaceCwdFor(args.topicId), "uploads");
13157
+ const destDir = join27(workspaceCwdFor(args.topicId), "uploads");
13151
13158
  mkdirSync17(destDir, { recursive: true });
13152
13159
  const safeName = safeAttachmentFilename(args.filename, "upload");
13153
- const destPath = join26(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
13160
+ const destPath = join27(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
13154
13161
  if (args.sourcePath !== undefined) {
13155
13162
  copyFileSync2(args.sourcePath, destPath);
13156
13163
  } else if (args.bytes !== undefined) {
@@ -14136,7 +14143,7 @@ var init_visual_html = __esm(() => {
14136
14143
  });
14137
14144
 
14138
14145
  // ../../packages/core/src/runtime/visuals.ts
14139
- import { realpathSync as realpathSync4 } from "fs";
14146
+ import { realpathSync as realpathSync5 } from "fs";
14140
14147
  import { isAbsolute as isAbsolute4, resolve as resolve15 } from "path";
14141
14148
  function activeVisualHtmlForPrompt(html) {
14142
14149
  if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
@@ -14191,8 +14198,8 @@ function isPathInside(baseDir, filePath) {
14191
14198
  const base = resolve15(baseDir);
14192
14199
  const normalized = resolve15(filePath);
14193
14200
  try {
14194
- const realBase = realpathSync4(base);
14195
- const real = realpathSync4(normalized);
14201
+ const realBase = realpathSync5(base);
14202
+ const real = realpathSync5(normalized);
14196
14203
  return real === realBase || real.startsWith(`${realBase}/`);
14197
14204
  } catch {
14198
14205
  return false;
@@ -14257,7 +14264,7 @@ function resolveVisualMediaInput(topicId, input) {
14257
14264
  }
14258
14265
  let realCandidate;
14259
14266
  try {
14260
- realCandidate = realpathSync4(candidate);
14267
+ realCandidate = realpathSync5(candidate);
14261
14268
  } catch {
14262
14269
  return { error: `file not found: ${rawPath}` };
14263
14270
  }
@@ -14291,7 +14298,7 @@ var init_visuals = __esm(async () => {
14291
14298
  // ../../packages/core/src/storage/token-stats.ts
14292
14299
  import { createHash as createHash6 } from "crypto";
14293
14300
  import { mkdirSync as mkdirSync18 } from "fs";
14294
- import { join as join27 } from "path";
14301
+ import { join as join28 } from "path";
14295
14302
  function emptyBucket() {
14296
14303
  return {
14297
14304
  inputTokens: 0,
@@ -14309,7 +14316,7 @@ function queriesPath(userId) {
14309
14316
  const fileId = tokenStatsFileId(userId);
14310
14317
  const logDir = resolveStorageLogDir();
14311
14318
  mkdirSync18(logDir, { recursive: true });
14312
- return join27(logDir, `token-queries-${fileId}.jsonl`);
14319
+ return join28(logDir, `token-queries-${fileId}.jsonl`);
14313
14320
  }
14314
14321
  function loadRecords(userId) {
14315
14322
  try {
@@ -14380,7 +14387,7 @@ var init_token_stats = __esm(async () => {
14380
14387
 
14381
14388
  // ../../packages/core/src/runtime/turn-event-stream.ts
14382
14389
  import { randomUUID as randomUUID15 } from "crypto";
14383
- import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
14390
+ import { realpathSync as realpathSync6, statSync as statSync9 } from "fs";
14384
14391
  import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
14385
14392
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
14386
14393
  if (getRoomQuery(topicId)?.queryId !== queryId)
@@ -14695,7 +14702,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
14695
14702
  }
14696
14703
  let safePath;
14697
14704
  try {
14698
- safePath = realpathSync5(path);
14705
+ safePath = realpathSync6(path);
14699
14706
  if (!statSync9(safePath).isFile()) {
14700
14707
  logger.warn({ topicId, path }, "peer output path is not a regular file");
14701
14708
  break;
@@ -14962,10 +14969,10 @@ var init_turn_session = __esm(async () => {
14962
14969
  });
14963
14970
 
14964
14971
  // ../../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";
14972
+ import { existsSync as existsSync18, mkdirSync as mkdirSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
14973
+ import { dirname as dirname15, join as join29 } from "path";
14967
14974
  function settingsFile() {
14968
- return join28(resolveStorageDataDir(), "otium-settings.json");
14975
+ return join29(resolveStorageDataDir(), "otium-settings.json");
14969
14976
  }
14970
14977
  function ensureSettingsLoaded() {
14971
14978
  const path = settingsFile();
@@ -14974,8 +14981,8 @@ function ensureSettingsLoaded() {
14974
14981
  loadedSettingsFile = path;
14975
14982
  aiName = DEFAULT_AI_NAME;
14976
14983
  try {
14977
- if (existsSync17(path)) {
14978
- const data = JSON.parse(readFileSync17(path, "utf8"));
14984
+ if (existsSync18(path)) {
14985
+ const data = JSON.parse(readFileSync18(path, "utf8"));
14979
14986
  if (typeof data.aiName === "string" && data.aiName.trim()) {
14980
14987
  aiName = data.aiName.trim();
14981
14988
  }
@@ -15064,8 +15071,8 @@ __export(exports_turn_runner, {
15064
15071
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
15065
15072
  });
15066
15073
  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";
15074
+ import { existsSync as existsSync19, mkdirSync as mkdirSync20, readdirSync as readdirSync7, statSync as statSync10 } from "fs";
15075
+ import { join as join30 } from "path";
15069
15076
  function withDefaultPlaywright(configuredMcp, isManager) {
15070
15077
  if (isManager)
15071
15078
  return configuredMcp;
@@ -15108,18 +15115,18 @@ function appendAskReplyMessage(topicId, text2, agentType) {
15108
15115
  return message;
15109
15116
  }
15110
15117
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
15111
- const preferred = join29(directory, preferredFilename);
15112
- if (preferExact && existsSync18(preferred))
15118
+ const preferred = join30(directory, preferredFilename);
15119
+ if (preferExact && existsSync19(preferred))
15113
15120
  return preferred;
15114
15121
  let newestLegacyId = null;
15115
15122
  let newestTitle = null;
15116
15123
  try {
15117
- for (const filename of readdirSync6(directory)) {
15124
+ for (const filename of readdirSync7(directory)) {
15118
15125
  const titleMatch = matchesTitle(filename);
15119
15126
  const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
15120
15127
  if (!titleMatch && !legacyIdMatch)
15121
15128
  continue;
15122
- const path = join29(directory, filename);
15129
+ const path = join30(directory, filename);
15123
15130
  const mtimeMs = statSync10(path).mtimeMs;
15124
15131
  if (titleMatch) {
15125
15132
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
@@ -15133,10 +15140,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
15133
15140
  return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
15134
15141
  }
15135
15142
  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;
15143
+ const briefFile = resolveWikiMirrorPath(join30(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
15144
+ const hasBriefFile = existsSync19(briefFile) && statSync10(briefFile).isFile();
15145
+ const latestSummaryCandidate = resolveWikiMirrorPath(join30(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
15146
+ const latestSummaryFile = existsSync19(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15140
15147
  return { briefFile, hasBriefFile, latestSummaryFile };
15141
15148
  }
15142
15149
  async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
@@ -16288,9 +16295,9 @@ __export(exports_auth_check, {
16288
16295
  checkAgentAuth: () => checkAgentAuth
16289
16296
  });
16290
16297
  import { execFileSync as execFileSync7 } from "child_process";
16291
- import { existsSync as existsSync19 } from "fs";
16298
+ import { existsSync as existsSync20 } from "fs";
16292
16299
  import { homedir as homedir9, platform } from "os";
16293
- import { join as join30 } from "path";
16300
+ import { join as join31 } from "path";
16294
16301
  function hasMaestroCredential(host, key, userId) {
16295
16302
  if (userId && host.getVaultValue(userId, key)?.trim())
16296
16303
  return true;
@@ -16319,7 +16326,7 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost, userId) {
16319
16326
  error: "claude is not logged in (no macOS keychain entry 'Claude Code-credentials'). Run `claude` and complete login first"
16320
16327
  };
16321
16328
  }
16322
- const path = join30(host.homeDirectory(), ".claude", ".credentials.json");
16329
+ const path = join31(host.homeDirectory(), ".claude", ".credentials.json");
16323
16330
  if (host.exists(path))
16324
16331
  return { ok: true };
16325
16332
  return {
@@ -16361,7 +16368,7 @@ var init_auth_check = __esm(async () => {
16361
16368
  await init_vault();
16362
16369
  defaultAgentAuthHost = {
16363
16370
  codexAuthFilePath,
16364
- exists: existsSync19,
16371
+ exists: existsSync20,
16365
16372
  environment: process.env,
16366
16373
  homeDirectory: homedir9,
16367
16374
  operatingSystem: platform,
@@ -18433,15 +18440,401 @@ await __promiseAll([
18433
18440
  init_codex_registry(),
18434
18441
  init_maestro_registry()
18435
18442
  ]);
18443
+ import { spawn as spawn2 } from "child_process";
18444
+ import { existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
18445
+ import { join as join11 } from "path";
18436
18446
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18437
18447
  import { z } from "zod";
18448
+ function signalProcessTree(child, signal) {
18449
+ try {
18450
+ if (child.pid && process.platform !== "win32")
18451
+ process.kill(-child.pid, signal);
18452
+ else
18453
+ child.kill(signal);
18454
+ } catch {
18455
+ try {
18456
+ child.kill(signal);
18457
+ } catch {}
18458
+ }
18459
+ }
18460
+ function spawnCapture(cmd, args, timeoutMs, signal, env) {
18461
+ return new Promise((resolve7, reject) => {
18462
+ const child = spawn2(cmd, args, {
18463
+ env: env ?? process.env,
18464
+ stdio: ["ignore", "pipe", "pipe"],
18465
+ detached: process.platform !== "win32"
18466
+ });
18467
+ let settled = false;
18468
+ let stdout = "";
18469
+ let stderr = "";
18470
+ const forceKill = () => signalProcessTree(child, "SIGKILL");
18471
+ let killTimer;
18472
+ const stop = (reason) => {
18473
+ if (settled)
18474
+ return;
18475
+ settled = true;
18476
+ signalProcessTree(child, "SIGTERM");
18477
+ killTimer = setTimeout(() => {
18478
+ forceKill();
18479
+ killTimer = undefined;
18480
+ }, 1000);
18481
+ killTimer.unref?.();
18482
+ reject(new Error(reason));
18483
+ };
18484
+ const timeout = setTimeout(() => stop("timeout"), timeoutMs);
18485
+ const onAbort = () => stop("aborted");
18486
+ signal.addEventListener("abort", onAbort, { once: true });
18487
+ const cleanup = () => {
18488
+ clearTimeout(timeout);
18489
+ signal.removeEventListener("abort", onAbort);
18490
+ };
18491
+ child.stdout.on("data", (data) => {
18492
+ stdout += data.toString();
18493
+ });
18494
+ child.stderr.on("data", (data) => {
18495
+ stderr += data.toString();
18496
+ });
18497
+ child.on("error", (err) => {
18498
+ if (settled)
18499
+ return;
18500
+ settled = true;
18501
+ cleanup();
18502
+ if (killTimer)
18503
+ clearTimeout(killTimer);
18504
+ reject(err);
18505
+ });
18506
+ child.on("close", (code, exitSignal) => {
18507
+ cleanup();
18508
+ if (settled) {
18509
+ if (killTimer) {
18510
+ clearTimeout(killTimer);
18511
+ killTimer = undefined;
18512
+ }
18513
+ return;
18514
+ }
18515
+ settled = true;
18516
+ if (killTimer)
18517
+ clearTimeout(killTimer);
18518
+ if (code === 0)
18519
+ resolve7({ stdout, stderr });
18520
+ else
18521
+ reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
18522
+ });
18523
+ });
18524
+ }
18525
+ function resultOk(agent, model, latency, detail) {
18526
+ return { agent, model, ok: true, latency, detail };
18527
+ }
18528
+ function resultFail(agent, model, latency, error) {
18529
+ let category = "unknown";
18530
+ const normalized = error.toLowerCase();
18531
+ if (normalized.includes("auth") || normalized.includes("key") || normalized.includes("credential") || normalized.includes("unauthorized")) {
18532
+ category = "auth_error";
18533
+ } else if (normalized.includes("rate") || normalized.includes("limit") || normalized.includes("busy") || normalized.includes("overloaded")) {
18534
+ category = "rate_limit";
18535
+ } else if (normalized.includes("network") || normalized.includes("refused") || normalized.includes("dns") || normalized.includes("timeout")) {
18536
+ category = "network_error";
18537
+ }
18538
+ return { agent, model, ok: false, latency, error, category };
18539
+ }
18540
+ async function checkClaude(model, timeoutMs, signal) {
18541
+ const start = performance.now();
18542
+ try {
18543
+ const args = ["-p", "say OK", "--model", model, "--max-turns", "1", "--output-format", "text"];
18544
+ const { stdout, stderr } = await spawnCapture("claude", args, timeoutMs, signal);
18545
+ const elapsed = Math.round(performance.now() - start);
18546
+ const text = (stdout + stderr).trim().slice(0, 200);
18547
+ if (!text)
18548
+ return resultFail("claude", model, elapsed, "empty response");
18549
+ return resultOk("claude", model, elapsed, text);
18550
+ } catch (err) {
18551
+ return resultFail("claude", model, Math.round(performance.now() - start), errMsg(err));
18552
+ }
18553
+ }
18554
+ async function checkCodex(model, timeoutMs, signal) {
18555
+ const start = performance.now();
18556
+ try {
18557
+ const args = [
18558
+ "exec",
18559
+ "--model",
18560
+ model,
18561
+ "--skip-git-repo-check",
18562
+ "--sandbox",
18563
+ "danger-full-access",
18564
+ "say OK"
18565
+ ];
18566
+ const { stdout, stderr } = await spawnCapture("codex", args, timeoutMs, signal, {
18567
+ ...process.env,
18568
+ CODEX_NO_COLOR: "1"
18569
+ });
18570
+ const elapsed = Math.round(performance.now() - start);
18571
+ const combined = (stdout + stderr).trim();
18572
+ const match = combined.match(/\ncodex\s*\n(.+?)(?:\n(?:tokens|session|user|$))/);
18573
+ return resultOk("codex", model, elapsed, match ? match[1].trim().slice(0, 200) : "OK");
18574
+ } catch (err) {
18575
+ return resultFail("codex", model, Math.round(performance.now() - start), errMsg(err));
18576
+ }
18577
+ }
18578
+ async function checkMaestro(model, timeoutMs, signal) {
18579
+ const start = performance.now();
18580
+ let iterator;
18581
+ try {
18582
+ const { maestroProvider } = await import("maestro-agent-sdk");
18583
+ const generator = maestroProvider({
18584
+ model,
18585
+ prompt: "say OK",
18586
+ cwd: process.cwd(),
18587
+ maxTurns: 1
18588
+ });
18589
+ const deadline = Date.now() + timeoutMs;
18590
+ const activeIterator = generator[Symbol.asyncIterator]();
18591
+ iterator = activeIterator;
18592
+ let text = "";
18593
+ while (true) {
18594
+ const remaining = deadline - Date.now();
18595
+ if (remaining <= 0)
18596
+ throw new Error("timeout");
18597
+ const eventResult = await new Promise((resolve7, reject) => {
18598
+ const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
18599
+ const onAbort = () => reject(new Error("aborted"));
18600
+ signal.addEventListener("abort", onAbort, { once: true });
18601
+ activeIterator.next().then(resolve7, reject).finally(() => {
18602
+ clearTimeout(timeout);
18603
+ signal.removeEventListener("abort", onAbort);
18604
+ });
18605
+ });
18606
+ if (eventResult.done)
18607
+ break;
18608
+ const event = eventResult.value;
18609
+ if (event.type === "text" && event.content)
18610
+ text = event.content.slice(0, 200);
18611
+ if (event.type === "error") {
18612
+ return resultFail("maestro", model, Math.round(performance.now() - start), event.error || "unknown error");
18613
+ }
18614
+ }
18615
+ return resultOk("maestro", model, Math.round(performance.now() - start), text || "(empty response)");
18616
+ } catch (err) {
18617
+ return resultFail("maestro", model, Math.round(performance.now() - start), errMsg(err));
18618
+ } finally {
18619
+ if (iterator?.return) {
18620
+ try {
18621
+ await iterator.return();
18622
+ } catch {}
18623
+ }
18624
+ }
18625
+ }
18626
+ function getDefaultModel(registry) {
18627
+ return resolveDefaultModel(registry.kind, registry.defaultModel);
18628
+ }
18629
+ var AGENTS = [
18630
+ { kind: "claude", registry: claudeRegistry, checker: checkClaude },
18631
+ { kind: "codex", registry: codexRegistry, checker: checkCodex },
18632
+ { kind: "maestro", registry: maestroRegistry, checker: checkMaestro }
18633
+ ];
18634
+ var MAX_CONCURRENT_CHECKS = 2;
18635
+ var activeChecks = 0;
18636
+ var checkWaiters = [];
18438
18637
  var activeCheckAllUsers = new Set;
18638
+ async function withCheckSlot(operation) {
18639
+ if (activeChecks >= MAX_CONCURRENT_CHECKS) {
18640
+ await new Promise((resolve7) => checkWaiters.push(resolve7));
18641
+ }
18642
+ activeChecks += 1;
18643
+ try {
18644
+ return await operation();
18645
+ } finally {
18646
+ activeChecks -= 1;
18647
+ checkWaiters.shift()?.();
18648
+ }
18649
+ }
18650
+ function createAgentHealthMcpServer(context) {
18651
+ const server = new McpServer({ name: "agent-health", version: "1.0.0" });
18652
+ const abortController = new AbortController;
18653
+ const originalClose = server.close.bind(server);
18654
+ server.close = async () => {
18655
+ abortController.abort();
18656
+ await originalClose();
18657
+ };
18658
+ 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.", {
18659
+ agent: z.enum(["claude", "codex", "maestro"]).describe("\uCCB4\uD06C\uD560 agent"),
18660
+ model: z.string().optional().describe("\uCCB4\uD06C\uD560 \uBAA8\uB378 (\uC5C6\uC73C\uBA74 agent\uBCC4 default \uC0AC\uC6A9)"),
18661
+ timeoutMs: z.number().default(30000).describe("\uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08")
18662
+ }, async ({ agent, model, timeoutMs }) => {
18663
+ const entry = AGENTS.find((candidate) => candidate.kind === agent);
18664
+ if (!entry)
18665
+ return mcpOk(`Unknown agent: ${agent}`);
18666
+ const resolvedModel = model || getDefaultModel(entry.registry);
18667
+ const result = await withCheckSlot(() => entry.checker(resolvedModel, timeoutMs, abortController.signal));
18668
+ if (result.ok) {
18669
+ return mcpOk(`OK ${result.agent} / ${result.model}
18670
+ latency: ${result.latency}ms
18671
+ detail: ${result.detail}`);
18672
+ }
18673
+ return mcpOk(`FAIL ${result.agent} / ${result.model} [${result.category}]
18674
+ latency: ${result.latency}ms
18675
+ error: ${result.error}`);
18676
+ });
18677
+ 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 }) => {
18678
+ const userKey = context.userId ?? "unscoped";
18679
+ if (activeCheckAllUsers.has(userKey)) {
18680
+ return mcpOk("FAIL check_all is already running for this user");
18681
+ }
18682
+ activeCheckAllUsers.add(userKey);
18683
+ try {
18684
+ const results = await Promise.all(AGENTS.map(async (entry) => withCheckSlot(() => entry.checker(getDefaultModel(entry.registry), timeoutMs, abortController.signal))));
18685
+ 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}`);
18686
+ return mcpOk(`Agent/Model Health Check
18687
+ ${lines.join(`
18688
+ `)}
18689
+
18690
+ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents failed"}`);
18691
+ } finally {
18692
+ activeCheckAllUsers.delete(userKey);
18693
+ }
18694
+ });
18695
+ 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 () => {
18696
+ if (!context.userId)
18697
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
18698
+ const stateDir = join11(USERS_LOG_DIR, context.userId, "active-queries");
18699
+ if (!existsSync9(stateDir))
18700
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
18701
+ let files;
18702
+ try {
18703
+ files = readdirSync3(stateDir);
18704
+ } catch {
18705
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
18706
+ }
18707
+ const now = Date.now();
18708
+ const entries = files.flatMap((file) => {
18709
+ if (!file.endsWith(".json"))
18710
+ return [];
18711
+ const state = readJsonFile(join11(stateDir, file));
18712
+ if (!state)
18713
+ return [];
18714
+ const sinceMs = new Date(state.since).getTime();
18715
+ if (now - sinceMs > ACTIVE_QUERY_STALE_MS)
18716
+ return [];
18717
+ return [
18718
+ {
18719
+ topicName: state.topicName ?? file.slice(0, -5),
18720
+ elapsedSec: Math.round((now - sinceMs) / 1000),
18721
+ task: state.task
18722
+ }
18723
+ ];
18724
+ });
18725
+ if (entries.length === 0)
18726
+ return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
18727
+ const lines = entries.map((entry) => `\u2022 ${entry.topicName} | ${entry.elapsedSec}s \uACBD\uACFC${entry.task ? ` "${entry.task}"` : ""}`);
18728
+ return mcpOk(`\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC (${entries.length}\uAC1C)
18729
+ ${lines.join(`
18730
+ `)}`);
18731
+ });
18732
+ return server;
18733
+ }
18439
18734
  // ../../packages/core/src/mcp/factories/compaction-log.ts
18440
18735
  init_mcp_helpers();
18736
+ import { readFileSync as readFileSync8, realpathSync as realpathSync2 } from "fs";
18441
18737
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
18442
18738
  import { z as z2 } from "zod";
18739
+ var DEFAULT_MAX_CALLS = 12;
18443
18740
  var DEFAULT_MAX_TOTAL_BYTES = 512 * 1024;
18444
18741
  var DEFAULT_MAX_CHUNK_BYTES = 64 * 1024;
18742
+ var DEFAULT_LINE_LIMIT = 300;
18743
+ var MAX_LINE_LIMIT = 500;
18744
+ function positiveInt(value2, fallback) {
18745
+ return Number.isFinite(value2) && value2 && value2 > 0 ? Math.floor(value2) : fallback;
18746
+ }
18747
+ function fitUtf8(text, maxBytes) {
18748
+ if (Buffer.byteLength(text, "utf8") <= maxBytes)
18749
+ return text;
18750
+ const marker = `
18751
+ [... oversized line clipped ...]
18752
+ `;
18753
+ const markerBytes = Buffer.byteLength(marker, "utf8");
18754
+ const bytes = Buffer.from(text, "utf8");
18755
+ if (maxBytes <= markerBytes) {
18756
+ return bytes.subarray(0, maxBytes).toString("utf8").replace(/\uFFFD+$/u, "");
18757
+ }
18758
+ const available = Math.max(0, maxBytes - markerBytes);
18759
+ const headBudget = Math.ceil(available * 0.6);
18760
+ const tailBudget = available - headBudget;
18761
+ const head = bytes.subarray(0, headBudget).toString("utf8").replace(/\uFFFD+$/u, "");
18762
+ const tail = bytes.subarray(Math.max(0, bytes.length - tailBudget)).toString("utf8").replace(/^\uFFFD+/u, "");
18763
+ return `${head}${marker}${tail}`;
18764
+ }
18765
+ function createCompactionLogMcpServer(context) {
18766
+ if (!context.filePath)
18767
+ throw new Error("compaction-log MCP requires filePath");
18768
+ const filePath = realpathSync2(context.filePath);
18769
+ const lines = readFileSync8(filePath, "utf8").split(`
18770
+ `);
18771
+ const maxCalls = positiveInt(context.maxCalls, DEFAULT_MAX_CALLS);
18772
+ const maxTotalBytes = positiveInt(context.maxTotalBytes, DEFAULT_MAX_TOTAL_BYTES);
18773
+ const maxChunkBytes = positiveInt(context.maxChunkBytes, DEFAULT_MAX_CHUNK_BYTES);
18774
+ let calls = 0;
18775
+ let totalBytes = 0;
18776
+ const server = new McpServer2({ name: "compaction-log", version: "1.0.0" });
18777
+ server.tool("read_compaction_log", [
18778
+ `Read the immutable conversation snapshot in bounded chunks. It contains ${lines.length} lines.`,
18779
+ "Start at offset 1, then inspect the final chunk using total_lines from the response.",
18780
+ "Read intermediate chunks only when needed. Return a summary instead of calling after the budget is exhausted."
18781
+ ].join(" "), {
18782
+ offset: z2.number().optional().describe("One-based line number. Defaults to 1."),
18783
+ limit: z2.number().optional().describe(`Number of lines. Defaults to ${DEFAULT_LINE_LIMIT}, max ${MAX_LINE_LIMIT}.`)
18784
+ }, async ({ offset: rawOffset, limit: rawLimit }) => {
18785
+ calls += 1;
18786
+ if (calls > maxCalls || totalBytes >= maxTotalBytes) {
18787
+ return mcpError(JSON.stringify({
18788
+ error: "compaction log read budget exhausted; produce the summary now",
18789
+ calls,
18790
+ max_calls: maxCalls,
18791
+ total_lines: lines.length
18792
+ }));
18793
+ }
18794
+ const offset = positiveInt(rawOffset, 1);
18795
+ const limit = Math.min(positiveInt(rawLimit, DEFAULT_LINE_LIMIT), MAX_LINE_LIMIT);
18796
+ const start = Math.max(0, offset - 1);
18797
+ if (start >= lines.length) {
18798
+ return mcpOk(JSON.stringify({
18799
+ content: "(end of compaction log)",
18800
+ offset,
18801
+ returned_lines: 0,
18802
+ total_lines: lines.length,
18803
+ calls
18804
+ }));
18805
+ }
18806
+ const selected = [];
18807
+ let returnedBytes = 0;
18808
+ let index = start;
18809
+ const callBudget = Math.min(maxChunkBytes, maxTotalBytes - totalBytes);
18810
+ while (index < lines.length && selected.length < limit) {
18811
+ const line = fitUtf8(lines[index] ?? "", Math.max(1, callBudget));
18812
+ const separatorBytes = selected.length > 0 ? 1 : 0;
18813
+ const lineBytes = Buffer.byteLength(line, "utf8");
18814
+ if (selected.length > 0 && returnedBytes + separatorBytes + lineBytes > callBudget)
18815
+ break;
18816
+ selected.push(line);
18817
+ returnedBytes += separatorBytes + lineBytes;
18818
+ index += 1;
18819
+ if (returnedBytes >= callBudget)
18820
+ break;
18821
+ }
18822
+ totalBytes += returnedBytes;
18823
+ return mcpOk(JSON.stringify({
18824
+ content: selected.join(`
18825
+ `),
18826
+ offset,
18827
+ returned_lines: selected.length,
18828
+ returned_bytes: returnedBytes,
18829
+ total_lines: lines.length,
18830
+ total_read_bytes: totalBytes,
18831
+ calls,
18832
+ ...index < lines.length ? { next_offset: index + 1 } : {},
18833
+ ...calls >= maxCalls || totalBytes >= maxTotalBytes ? { note: "Read budget exhausted; produce the summary now." } : {}
18834
+ }));
18835
+ });
18836
+ return server;
18837
+ }
18445
18838
  // ../../packages/core/src/mcp/factories/session-comm.ts
18446
18839
  import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
18447
18840
  import { z as z5 } from "zod";
@@ -18940,7 +19333,9 @@ export {
18940
19333
  createTaskMcpServer,
18941
19334
  createSystemHealthMcpServer,
18942
19335
  createSessionTargetCatalog,
18943
- createSessionCommMcpServer
19336
+ createSessionCommMcpServer,
19337
+ createCompactionLogMcpServer,
19338
+ createAgentHealthMcpServer
18944
19339
  };
18945
19340
 
18946
- //# debugId=0271389E4BFF755564756E2164756E21
19341
+ //# debugId=BC568829D61D883264756E2164756E21