negotium 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -1869,7 +1869,7 @@ var exports_version = {};
1869
1869
  __export(exports_version, {
1870
1870
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
1871
1871
  });
1872
- var NEGOTIUM_VERSION = "0.4.0";
1872
+ var NEGOTIUM_VERSION = "0.4.2";
1873
1873
 
1874
1874
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1875
1875
  import { spawn } from "child_process";
@@ -2654,7 +2654,8 @@ var init_mcp_catalog_policy = __esm(() => {
2654
2654
  "system-health": { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
2655
2655
  "background-bash": { scopes: ["forum"], forumRequired: true },
2656
2656
  "agent-health": { scopes: ["forum", "manager", "cron"], forumRequired: true },
2657
- vault: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true }
2657
+ vault: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
2658
+ "cua-rs": { scopes: ["dm", "forum", "fork"], forumRequired: false }
2658
2659
  };
2659
2660
  });
2660
2661
 
@@ -2666,6 +2667,9 @@ function browserOwnerCapability(capability, owner) {
2666
2667
  var init_capability = () => {};
2667
2668
 
2668
2669
  // ../../packages/core/src/platform/mcp-config.ts
2670
+ import { accessSync as accessSync2, constants as fsConstants } from "fs";
2671
+ import { homedir as homedir6 } from "os";
2672
+ import { join as join8 } from "path";
2669
2673
  function buildStdioMcpServer(agent, serverFile, serverArgs, env) {
2670
2674
  if (agent === "codex") {
2671
2675
  return {
@@ -2781,6 +2785,27 @@ function backgroundBashTransport(agent, port, userId, topic) {
2781
2785
  }
2782
2786
  return { type: "sse", url: `http://127.0.0.1:${port}/sse`, headers };
2783
2787
  }
2788
+ function cuaRsArgs() {
2789
+ const raw = envText("NEGOTIUM_CUA_RS_ALLOW_HID")?.trim().toLowerCase();
2790
+ const on = raw === "1" || raw === "true" || raw === "yes";
2791
+ return on ? ["--allow-hid"] : [];
2792
+ }
2793
+ function resolveCuaRsBinary(platform2 = process.platform) {
2794
+ if (platform2 !== "darwin")
2795
+ return null;
2796
+ const candidates = [
2797
+ envText("NEGOTIUM_CUA_RS_BIN"),
2798
+ join8(homedir6(), ".local", "bin", "cua-rs"),
2799
+ "/usr/local/bin/cua-rs"
2800
+ ].filter((p) => Boolean(p));
2801
+ for (const candidate of candidates) {
2802
+ try {
2803
+ accessSync2(candidate, fsConstants.X_OK);
2804
+ return candidate;
2805
+ } catch {}
2806
+ }
2807
+ return null;
2808
+ }
2784
2809
  function refreshForumCatalogViews() {
2785
2810
  const { all, required, optional } = classifyForumMcpServers(MCP_CATALOG);
2786
2811
  allForumMcpServerNames.splice(0, allForumMcpServerNames.length, ...all);
@@ -3240,6 +3265,15 @@ var init_mcp_config = __esm(() => {
3240
3265
  args.push("--list-only=true");
3241
3266
  return buildBuiltinMcpServer("vault", { ...ctx, userId: vaultUserId ?? userId }, () => buildStdioMcpServer(agent, VAULT_SERVER, args));
3242
3267
  }
3268
+ },
3269
+ "cua-rs": {
3270
+ ...commonRuntimeMcpPolicy("cua-rs"),
3271
+ build() {
3272
+ const bin = resolveCuaRsBinary();
3273
+ if (!bin)
3274
+ return null;
3275
+ return { command: bin, args: cuaRsArgs() };
3276
+ }
3243
3277
  }
3244
3278
  };
3245
3279
  allForumMcpServerNames = [];
@@ -3311,14 +3345,14 @@ var init_execution_host = __esm(async () => {
3311
3345
  // ../../packages/core/src/agents/rollout/codex.ts
3312
3346
  import { randomBytes as randomBytes4 } from "crypto";
3313
3347
  import { existsSync as existsSync6, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync2, unlinkSync as unlinkSync5 } from "fs";
3314
- import { basename, dirname as dirname6, join as join8, resolve as resolve5 } from "path";
3348
+ import { basename, dirname as dirname6, join as join9, resolve as resolve5 } from "path";
3315
3349
  function codexSessionsDir() {
3316
- return join8(hostedCodexHomePath(), "sessions");
3350
+ return join9(hostedCodexHomePath(), "sessions");
3317
3351
  }
3318
3352
  function loadCodexShell() {
3319
3353
  if (_shellCache)
3320
3354
  return _shellCache;
3321
- const raw = readFileSync5(join8(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
3355
+ const raw = readFileSync5(join9(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
3322
3356
  const lines = parseJsonlText(raw);
3323
3357
  if (lines.length < 5) {
3324
3358
  throw new Error(`loadCodexShell: expected >=5 entries in codex-shell.jsonl, got ${lines.length}`);
@@ -3385,8 +3419,8 @@ function codexRolloutPath(threadId, fallback) {
3385
3419
  const hh = String(createdAt.getHours()).padStart(2, "0");
3386
3420
  const min = String(createdAt.getMinutes()).padStart(2, "0");
3387
3421
  const ss = String(createdAt.getSeconds()).padStart(2, "0");
3388
- const dir = join8(codexSessionsDir(), yyyy, mm, dd);
3389
- return join8(dir, `rollout-${yyyy}-${mm}-${dd}T${hh}-${min}-${ss}-${threadId}.jsonl`);
3422
+ const dir = join9(codexSessionsDir(), yyyy, mm, dd);
3423
+ return join9(dir, `rollout-${yyyy}-${mm}-${dd}T${hh}-${min}-${ss}-${threadId}.jsonl`);
3390
3424
  }
3391
3425
  function canonicalFilePath(path) {
3392
3426
  const absolute = resolve5(path);
@@ -3394,7 +3428,7 @@ function canonicalFilePath(path) {
3394
3428
  return realpathSync2(absolute);
3395
3429
  } catch {
3396
3430
  try {
3397
- return join8(realpathSync2(dirname6(absolute)), basename(absolute));
3431
+ return join9(realpathSync2(dirname6(absolute)), basename(absolute));
3398
3432
  } catch {
3399
3433
  return absolute;
3400
3434
  }
@@ -3624,19 +3658,19 @@ function latestCodexRolloutPath(threadId) {
3624
3658
  try {
3625
3659
  if (buckets) {
3626
3660
  for (const bucket of buckets) {
3627
- const dir = join8(sessionsDir, bucket);
3661
+ const dir = join9(sessionsDir, bucket);
3628
3662
  if (!existsSync6(dir))
3629
3663
  continue;
3630
3664
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
3631
3665
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
3632
- candidates.push(join8(dir, rel));
3666
+ candidates.push(join9(dir, rel));
3633
3667
  }
3634
3668
  }
3635
3669
  }
3636
3670
  if (candidates.length === 0) {
3637
3671
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
3638
3672
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
3639
- candidates.push(join8(sessionsDir, rel));
3673
+ candidates.push(join9(sessionsDir, rel));
3640
3674
  }
3641
3675
  }
3642
3676
  return candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
@@ -3757,13 +3791,13 @@ function sweepPriorRolloutsForThread(threadId) {
3757
3791
  return;
3758
3792
  }
3759
3793
  for (const bucket of buckets) {
3760
- const dir = join8(sessionsDir, bucket);
3794
+ const dir = join9(sessionsDir, bucket);
3761
3795
  if (!existsSync6(dir))
3762
3796
  continue;
3763
3797
  try {
3764
3798
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
3765
3799
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
3766
- const fullPath = join8(dir, rel);
3800
+ const fullPath = join9(dir, rel);
3767
3801
  try {
3768
3802
  unlinkSync5(fullPath);
3769
3803
  } catch (e) {
@@ -3781,7 +3815,7 @@ function sweepPriorRolloutsFullTree(threadId, sessionsDir) {
3781
3815
  try {
3782
3816
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
3783
3817
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
3784
- const fullPath = join8(sessionsDir, rel);
3818
+ const fullPath = join9(sessionsDir, rel);
3785
3819
  try {
3786
3820
  unlinkSync5(fullPath);
3787
3821
  } catch (e) {
@@ -3959,7 +3993,7 @@ var init_codex_app_server = __esm(async () => {
3959
3993
 
3960
3994
  // ../../packages/core/src/agents/codex-registry.ts
3961
3995
  import { existsSync as existsSync7, unlinkSync as unlinkSync6 } from "fs";
3962
- import { join as join9 } from "path";
3996
+ import { join as join10 } from "path";
3963
3997
  var VALID_EFFORTS2, codexRegistry, codexRegistryOperations;
3964
3998
  var init_codex_registry = __esm(async () => {
3965
3999
  await init_codex_app_server();
@@ -4002,7 +4036,7 @@ var init_codex_registry = __esm(async () => {
4002
4036
  async cleanupRollouts({ sessionIds }) {
4003
4037
  if (sessionIds.length === 0)
4004
4038
  return;
4005
- const sessionsDir = join9(hostedCodexHomePath(), "sessions");
4039
+ const sessionsDir = join10(hostedCodexHomePath(), "sessions");
4006
4040
  if (!existsSync7(sessionsDir))
4007
4041
  return;
4008
4042
  const failures = [];
@@ -4010,7 +4044,7 @@ var init_codex_registry = __esm(async () => {
4010
4044
  try {
4011
4045
  const glob = new Bun.Glob(`**/rollout-*-${tid}.jsonl`);
4012
4046
  for await (const rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
4013
- const path = join9(sessionsDir, rel);
4047
+ const path = join10(sessionsDir, rel);
4014
4048
  try {
4015
4049
  unlinkSync6(path);
4016
4050
  } catch (e) {
@@ -4035,16 +4069,16 @@ var init_codex_registry = __esm(async () => {
4035
4069
  // ../../packages/core/src/agents/maestro-registry.ts
4036
4070
  import { randomUUID as randomUUID3 } from "crypto";
4037
4071
  import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4038
- import { homedir as homedir6 } from "os";
4039
- import { join as join10, resolve as resolve6 } from "path";
4072
+ import { homedir as homedir7 } from "os";
4073
+ import { join as join11, resolve as resolve6 } from "path";
4040
4074
  function maestroSessionsDir() {
4041
- return join10(process.env.MAESTRO_DATA_DIR ? resolve6(process.env.MAESTRO_DATA_DIR) : join10(homedir6(), ".maestro"), "sessions");
4075
+ return join11(process.env.MAESTRO_DATA_DIR ? resolve6(process.env.MAESTRO_DATA_DIR) : join11(homedir7(), ".maestro"), "sessions");
4042
4076
  }
4043
4077
  function maestroSessionPath(sessionId) {
4044
- return join10(maestroSessionsDir(), `${sessionId}.jsonl`);
4078
+ return join11(maestroSessionsDir(), `${sessionId}.jsonl`);
4045
4079
  }
4046
4080
  function maestroActiveSessionPath(sessionId) {
4047
- return join10(maestroSessionsDir(), `${sessionId}.active.jsonl`);
4081
+ return join11(maestroSessionsDir(), `${sessionId}.active.jsonl`);
4048
4082
  }
4049
4083
  function existingCreatedAt(path) {
4050
4084
  if (!existsSync8(path))
@@ -4283,7 +4317,7 @@ __export(exports_tasks, {
4283
4317
  TASK_STATUS_VALUES: () => TASK_STATUS_VALUES
4284
4318
  });
4285
4319
  import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as renameSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
4286
- import { dirname as dirname7, join as join11 } from "path";
4320
+ import { dirname as dirname7, join as join12 } from "path";
4287
4321
  function safeTaskScopeKey(scopeKey) {
4288
4322
  const safe = sanitizeFileName(scopeKey);
4289
4323
  if (!safe || safe === "." || safe === "..") {
@@ -4295,7 +4329,7 @@ function taskScopeKey(opts) {
4295
4329
  return opts.topicId?.trim() || opts.session || "default";
4296
4330
  }
4297
4331
  function getTaskFilePath(userId, scopeKey) {
4298
- return join11(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
4332
+ return join12(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
4299
4333
  }
4300
4334
  function readTasks(userId, scopeKey) {
4301
4335
  const path = getTaskFilePath(userId, scopeKey);
@@ -4479,16 +4513,16 @@ import {
4479
4513
  unlinkSync as unlinkSync8,
4480
4514
  writeFileSync as writeFileSync7
4481
4515
  } from "fs";
4482
- import { dirname as dirname8, join as join12 } from "path";
4516
+ import { dirname as dirname8, join as join13 } from "path";
4483
4517
  function conversationDir(_userId) {
4484
- return join12(resolveStorageDataDir(), "conversations");
4518
+ return join13(resolveStorageDataDir(), "conversations");
4485
4519
  }
4486
4520
  function topicFilename(topicName) {
4487
4521
  const t = sanitizeTopicName(topicName, true);
4488
4522
  return `${t}.jsonl`;
4489
4523
  }
4490
4524
  function getConversationPath(userId, topicName) {
4491
- return join12(conversationDir(userId), topicFilename(topicName));
4525
+ return join13(conversationDir(userId), topicFilename(topicName));
4492
4526
  }
4493
4527
  function getActiveConversationPath(userId, topicName) {
4494
4528
  const rawPath = getConversationPath(userId, topicName);
@@ -5245,7 +5279,7 @@ import { existsSync as existsSync13 } from "fs";
5245
5279
  import { chmod, mkdtemp, rm, writeFile } from "fs/promises";
5246
5280
  import { createServer } from "net";
5247
5281
  import { tmpdir as tmpdir2 } from "os";
5248
- import { dirname as dirname9, join as join13, resolve as resolve7 } from "path";
5282
+ import { dirname as dirname9, join as join14, resolve as resolve7 } from "path";
5249
5283
  import { fileURLToPath as fileURLToPath2 } from "url";
5250
5284
  function evaluateCodexVaultPreToolUse(input, userId, operations) {
5251
5285
  if (operations.referencesSensitiveStorage(input.tool_input)) {
@@ -5283,9 +5317,11 @@ function hookClientPath() {
5283
5317
  return packaged;
5284
5318
  throw new Error("Codex Vault hook client is missing from this installation");
5285
5319
  }
5286
- function privateCodexWrapper(codexScript) {
5320
+ function privateCodexWrapper(codexScript, socketPath, token) {
5287
5321
  return [
5288
5322
  "#!/bin/sh",
5323
+ `export ${HOOK_SOCKET_ENV}=${shellQuote(socketPath)}`,
5324
+ `export ${HOOK_TOKEN_ENV}=${shellQuote(token)}`,
5289
5325
  'if [ "$1" = "exec" ]; then',
5290
5326
  " shift",
5291
5327
  ` exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} exec --dangerously-bypass-hook-trust "$@"`,
@@ -5296,10 +5332,10 @@ function privateCodexWrapper(codexScript) {
5296
5332
  `);
5297
5333
  }
5298
5334
  async function createCodexVaultHookBridge(userId) {
5299
- const root = await mkdtemp(join13(tmpdir2(), "negotium-codex-vault-"));
5335
+ const root = await mkdtemp(join14(tmpdir2(), "negotium-codex-vault-"));
5300
5336
  await chmod(root, 448);
5301
- const socketPath = join13(root, "hook.sock");
5302
- const wrapperPath = join13(root, "codex-with-hooks");
5337
+ const socketPath = join14(root, "hook.sock");
5338
+ const wrapperPath = join14(root, "codex-with-hooks");
5303
5339
  const token = randomBytes5(32).toString("hex");
5304
5340
  const connections = new Set;
5305
5341
  const server = createServer((socket) => {
@@ -5348,15 +5384,13 @@ async function createCodexVaultHookBridge(userId) {
5348
5384
  });
5349
5385
  });
5350
5386
  await chmod(socketPath, 384);
5351
- await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath()), { mode: 448 });
5352
- const command = [
5353
- shellQuote(process.execPath),
5354
- shellQuote(hookClientPath()),
5355
- shellQuote(socketPath),
5356
- shellQuote(token)
5357
- ].join(" ");
5387
+ await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath(), socketPath, token), {
5388
+ mode: 448
5389
+ });
5390
+ const command = [shellQuote(process.execPath), shellQuote(hookClientPath())].join(" ");
5358
5391
  return {
5359
5392
  codexPathOverride: wrapperPath,
5393
+ environment: { [HOOK_SOCKET_ENV]: socketPath, [HOOK_TOKEN_ENV]: token },
5360
5394
  hooks: {
5361
5395
  PreToolUse: [
5362
5396
  {
@@ -5385,7 +5419,7 @@ async function createCodexVaultHookBridge(userId) {
5385
5419
  throw error;
5386
5420
  }
5387
5421
  }
5388
- var MAX_HOOK_REQUEST_BYTES, SENSITIVE_STORAGE_DENIAL = "Runtime secret storage access is not permitted";
5422
+ var MAX_HOOK_REQUEST_BYTES, SENSITIVE_STORAGE_DENIAL = "Runtime secret storage access is not permitted", HOOK_SOCKET_ENV = "NEGOTIUM_CODEX_VAULT_HOOK_SOCKET", HOOK_TOKEN_ENV = "NEGOTIUM_CODEX_VAULT_HOOK_TOKEN";
5389
5423
  var init_codex_vault_hook_bridge = __esm(async () => {
5390
5424
  init_codex_native_multi_agent();
5391
5425
  await init_execution_host();
@@ -5918,8 +5952,8 @@ __export(exports_codex_provider, {
5918
5952
  });
5919
5953
  import { execFileSync as execFileSync5 } from "child_process";
5920
5954
  import { existsSync as existsSync14, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync5 } from "fs";
5921
- import { homedir as homedir7 } from "os";
5922
- import { dirname as dirname10, isAbsolute as isAbsolute2, join as join14, relative, resolve as resolve9 } from "path";
5955
+ import { homedir as homedir8 } from "os";
5956
+ import { dirname as dirname10, isAbsolute as isAbsolute2, join as join15, relative, resolve as resolve9 } from "path";
5923
5957
  import { Codex } from "@openai/codex-sdk";
5924
5958
  function sameCodexUsage(usage, total) {
5925
5959
  return usage.input_tokens === total.inputTokens && usage.output_tokens === total.outputTokens && (usage.cached_input_tokens ?? 0) === total.cachedInputTokens && (usage.cache_write_input_tokens ?? 0) === total.cacheWriteInputTokens;
@@ -5947,7 +5981,7 @@ function codexMcpServerName(name) {
5947
5981
  return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
5948
5982
  }
5949
5983
  function globalCodexMcpServerNames(authFilePath) {
5950
- const configPath = join14(dirname10(authFilePath), "config.toml");
5984
+ const configPath = join15(dirname10(authFilePath), "config.toml");
5951
5985
  if (!existsSync14(configPath))
5952
5986
  return [];
5953
5987
  try {
@@ -6305,7 +6339,7 @@ async function* codexProvider(opts) {
6305
6339
  mcpServerCount: Object.keys(codexMcpServers).length
6306
6340
  }, "codexProvider: starting turn");
6307
6341
  const hostedCodexHome = hostedCodexHomePath();
6308
- const inheritedCodexHome = process.env.CODEX_HOME || join14(homedir7(), ".codex");
6342
+ const inheritedCodexHome = process.env.CODEX_HOME || join15(homedir8(), ".codex");
6309
6343
  const codexEnvironment = scopedBrowserCapability || resolve9(hostedCodexHome) !== resolve9(inheritedCodexHome) ? {
6310
6344
  ...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
6311
6345
  CODEX_HOME: hostedCodexHome,
@@ -6499,7 +6533,10 @@ async function* codexProvider(opts) {
6499
6533
  yield fileEvent;
6500
6534
  }
6501
6535
  } else if (item.type === "error") {
6502
- yield { type: "error", content: String(item.message ?? "") };
6536
+ const message = String(item.message ?? "");
6537
+ if (message !== CODEX_HOOK_TRUST_BYPASS_NOTICE) {
6538
+ yield { type: "error", content: message };
6539
+ }
6503
6540
  }
6504
6541
  break;
6505
6542
  }
@@ -6590,7 +6627,7 @@ async function* codexProvider(opts) {
6590
6627
  await vaultHook.close();
6591
6628
  }
6592
6629
  }
6593
- var CODEX_MCP_SERVER_NAME_OVERRIDES, CODEX_DIFF_FILE_LIMIT, CODEX_DIFF_BASELINE_FILE_LIMIT = 200, CODEX_DIFF_BASELINE_BYTE_LIMIT, CODEX_STARTUP_TIMEOUT_MS = 90000;
6630
+ var CODEX_HOOK_TRUST_BYPASS_NOTICE = "`--dangerously-bypass-hook-trust` is enabled. Enabled hooks may run without review for this invocation.", CODEX_MCP_SERVER_NAME_OVERRIDES, CODEX_DIFF_FILE_LIMIT, CODEX_DIFF_BASELINE_FILE_LIMIT = 200, CODEX_DIFF_BASELINE_BYTE_LIMIT, CODEX_STARTUP_TIMEOUT_MS = 90000;
6594
6631
  var init_codex_provider = __esm(async () => {
6595
6632
  init_codex_native_multi_agent();
6596
6633
  init_codex_tree_kill();
@@ -6756,8 +6793,8 @@ var init_maestro_provider = __esm(async () => {
6756
6793
 
6757
6794
  // ../../packages/core/src/agents/index.ts
6758
6795
  import { existsSync as existsSync15 } from "fs";
6759
- import { homedir as homedir8 } from "os";
6760
- import { join as join15 } from "path";
6796
+ import { homedir as homedir9 } from "os";
6797
+ import { join as join16 } from "path";
6761
6798
  async function* dispatchAgent(opts) {
6762
6799
  switch (opts.agent) {
6763
6800
  case "claude": {
@@ -6791,11 +6828,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
6791
6828
  switch (agent) {
6792
6829
  case "claude": {
6793
6830
  const encodedCwd = encodeClaudeCwd(cwd);
6794
- const path = join15(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
6831
+ const path = join16(homedir9(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
6795
6832
  return !existsSync15(path);
6796
6833
  }
6797
6834
  case "codex": {
6798
- const sessionsDir = join15(hostedCodexHomePath(), "sessions");
6835
+ const sessionsDir = join16(hostedCodexHomePath(), "sessions");
6799
6836
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
6800
6837
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
6801
6838
  return false;
@@ -10124,13 +10161,13 @@ var init_browser_processes = __esm(async () => {
10124
10161
  });
10125
10162
 
10126
10163
  // ../../packages/core/src/platform/playwright/headed-launch.ts
10127
- import { accessSync as accessSync2, constants as constants2 } from "fs";
10164
+ import { accessSync as accessSync3, constants as constants2 } from "fs";
10128
10165
  import { delimiter, isAbsolute as isAbsolute3, resolve as resolve12 } from "path";
10129
10166
  function findExecutableOnPath(command, environment = process.env) {
10130
10167
  const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve12(directory, command));
10131
10168
  for (const candidate of candidates) {
10132
10169
  try {
10133
- accessSync2(candidate, constants2.X_OK);
10170
+ accessSync3(candidate, constants2.X_OK);
10134
10171
  return candidate;
10135
10172
  } catch {}
10136
10173
  }
@@ -10252,7 +10289,7 @@ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual2 } from
10252
10289
  import { chmodSync as chmodSync4 } from "fs";
10253
10290
  import { createServer as createServer3 } from "net";
10254
10291
  import { tmpdir as tmpdir3 } from "os";
10255
- import { join as join16 } from "path";
10292
+ import { join as join17 } from "path";
10256
10293
  function deepMapStrings2(value, transform) {
10257
10294
  if (typeof value === "string")
10258
10295
  return transform(value);
@@ -10339,7 +10376,7 @@ function authorized(actual, expected) {
10339
10376
  }
10340
10377
  async function createBrowserVaultBroker(userId) {
10341
10378
  const token = randomBytes6(32).toString("hex");
10342
- const socketPath = join16(process.platform === "win32" ? tmpdir3() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes6(8).toString("hex")}.sock`);
10379
+ const socketPath = join17(process.platform === "win32" ? tmpdir3() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes6(8).toString("hex")}.sock`);
10343
10380
  const retainedForms = new Map;
10344
10381
  const leases = new Map;
10345
10382
  const sockets = new Set;
@@ -10605,7 +10642,7 @@ import {
10605
10642
  unlinkSync as unlinkSync11,
10606
10643
  writeFileSync as writeFileSync8
10607
10644
  } from "fs";
10608
- import { dirname as dirname11, join as join17, resolve as resolve13 } from "path";
10645
+ import { dirname as dirname11, join as join18, resolve as resolve13 } from "path";
10609
10646
  function removeDefaultProfileDataDir(userDataDir) {
10610
10647
  const root = resolve13(BROWSER_PROFILES_DIR);
10611
10648
  const target = resolve13(userDataDir);
@@ -10695,14 +10732,14 @@ function portFileName(instanceKey) {
10695
10732
  function writePortFile(instanceKey, port) {
10696
10733
  try {
10697
10734
  mkdirSync10(managerHost.portsDir, { recursive: true });
10698
- writeFileSync8(join17(managerHost.portsDir, portFileName(instanceKey)), String(port));
10735
+ writeFileSync8(join18(managerHost.portsDir, portFileName(instanceKey)), String(port));
10699
10736
  } catch (e) {
10700
10737
  logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
10701
10738
  }
10702
10739
  }
10703
10740
  function deletePortFile(instanceKey) {
10704
10741
  try {
10705
- unlinkSync11(join17(managerHost.portsDir, portFileName(instanceKey)));
10742
+ unlinkSync11(join18(managerHost.portsDir, portFileName(instanceKey)));
10706
10743
  } catch (e) {
10707
10744
  if (e.code === "ENOENT")
10708
10745
  return;
@@ -12248,32 +12285,32 @@ __export(exports_wiki, {
12248
12285
  getSharedWikiDir: () => getSharedWikiDir
12249
12286
  });
12250
12287
  import { existsSync as existsSync17, readdirSync as readdirSync3, statSync as statSync6 } from "fs";
12251
- import { basename as basename2, dirname as dirname12, join as join18 } from "path";
12288
+ import { basename as basename2, dirname as dirname12, join as join19 } from "path";
12252
12289
  function getWikiDir(_userId, workspaceDir = resolveStorageWorkspaceDir()) {
12253
- return join18(workspaceDir, "wiki");
12290
+ return join19(workspaceDir, "wiki");
12254
12291
  }
12255
12292
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
12256
- return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
12293
+ return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join19(workspaceDir, "wiki");
12257
12294
  }
12258
12295
  function findLatestSummaryFile(wikiDir, safeTopic) {
12259
- const summariesDir = join18(wikiDir, "summaries");
12296
+ const summariesDir = join19(wikiDir, "summaries");
12260
12297
  if (!existsSync17(summariesDir))
12261
12298
  return null;
12262
12299
  const files = readdirSync3(summariesDir).filter((f) => f.endsWith(".md") && f.match(new RegExp(`^\\d{4}-\\d{2}-\\d{2}-${safeTopic}(\\.md|~\\d+\\.md)$`)) && !f.endsWith("-sent-files.md")).sort((left, right) => {
12263
- const mtimeDelta = statSync6(join18(summariesDir, right)).mtimeMs - statSync6(join18(summariesDir, left)).mtimeMs;
12300
+ const mtimeDelta = statSync6(join19(summariesDir, right)).mtimeMs - statSync6(join19(summariesDir, left)).mtimeMs;
12264
12301
  return mtimeDelta || right.localeCompare(left, undefined, { numeric: true });
12265
12302
  });
12266
- return files.length > 0 ? join18(summariesDir, files[0]) : null;
12303
+ return files.length > 0 ? join19(summariesDir, files[0]) : null;
12267
12304
  }
12268
12305
  function getTopicMemoryFilePaths(_userId, topicName, forkOrigin, workspaceDir = resolveStorageWorkspaceDir()) {
12269
12306
  const wikiDir = getSharedWikiDir(workspaceDir);
12270
12307
  const resolveBrief = (name) => {
12271
12308
  const safe = sanitizeTopicName(name, true);
12272
- const brief = join18(wikiDir, "topic", `${safe}.md`);
12309
+ const brief = join19(wikiDir, "topic", `${safe}.md`);
12273
12310
  const latestSummary = findLatestSummaryFile(wikiDir, safe);
12274
12311
  return { brief, latestSummary };
12275
12312
  };
12276
- const archiveDir = join18(wikiDir, "archive");
12313
+ const archiveDir = join19(wikiDir, "archive");
12277
12314
  const archiveExistsFor = (name) => {
12278
12315
  const safe = sanitizeTopicName(name);
12279
12316
  return existsSync17(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
@@ -12290,7 +12327,7 @@ function getTopicMemoryFilePaths(_userId, topicName, forkOrigin, workspaceDir =
12290
12327
  };
12291
12328
  }
12292
12329
  return {
12293
- memoryDir: join18(wikiDir, "topic"),
12330
+ memoryDir: join19(wikiDir, "topic"),
12294
12331
  memoryFiles: [],
12295
12332
  ...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
12296
12333
  ...hasArchive ? { hasArchive: true } : {}
@@ -12303,7 +12340,7 @@ var init_wiki = __esm(async () => {
12303
12340
  // ../../packages/core/src/agents/archiver.ts
12304
12341
  import { randomUUID as randomUUID10 } from "crypto";
12305
12342
  import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
12306
- import { join as join19 } from "path";
12343
+ import { join as join20 } from "path";
12307
12344
  function resolveMemoryLanguage() {
12308
12345
  const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
12309
12346
  return override && override.length > 0 ? override : resolveOutputLanguage();
@@ -12554,10 +12591,10 @@ function distillOneLine(summaryMd) {
12554
12591
  return "";
12555
12592
  }
12556
12593
  function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
12557
- const dir = join19(storage.getWikiDir(), "summaries");
12594
+ const dir = join20(storage.getWikiDir(), "summaries");
12558
12595
  if (!storage.fileExists(dir))
12559
12596
  return null;
12560
- const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
12597
+ const predicted = join20(dir, wikiSummaryFilename(date, topicTitle, topicId));
12561
12598
  if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
12562
12599
  return predicted;
12563
12600
  let best = null;
@@ -12565,7 +12602,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
12565
12602
  if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
12566
12603
  continue;
12567
12604
  }
12568
- const p = join19(dir, f);
12605
+ const p = join20(dir, f);
12569
12606
  try {
12570
12607
  const m = storage.fileModifiedAt(p);
12571
12608
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -12781,13 +12818,13 @@ __export(exports_topic_archive, {
12781
12818
  archiveConversationEvents: () => archiveConversationEvents
12782
12819
  });
12783
12820
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
12784
- import { join as join20 } from "path";
12821
+ import { join as join21 } from "path";
12785
12822
  function archiveTopicMessages(topicId, topicTitle, options = {}) {
12786
12823
  const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
12787
12824
  if (rows.length === 0)
12788
12825
  return null;
12789
12826
  const safeTopic = sanitizeTopicName(topicTitle, true);
12790
- const archiveDir = join20(getSharedWikiDir(), "archive");
12827
+ const archiveDir = join21(getSharedWikiDir(), "archive");
12791
12828
  mkdirSync11(archiveDir, { recursive: true });
12792
12829
  const date = new Date().toISOString().slice(0, 10);
12793
12830
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
@@ -12800,7 +12837,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
12800
12837
  let path;
12801
12838
  while (true) {
12802
12839
  filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
12803
- path = join20(archiveDir, filename);
12840
+ path = join21(archiveDir, filename);
12804
12841
  try {
12805
12842
  writeFileSync9(path, body, { flag: "wx" });
12806
12843
  break;
@@ -12845,7 +12882,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
12845
12882
  const entries = readRawConversation(userId, topicTitle);
12846
12883
  if (entries.length === 0)
12847
12884
  return null;
12848
- const archiveDir = join20(getSharedWikiDir(), "archive");
12885
+ const archiveDir = join21(getSharedWikiDir(), "archive");
12849
12886
  mkdirSync11(archiveDir, { recursive: true });
12850
12887
  const safeTopic = sanitizeTopicName(topicTitle, true);
12851
12888
  const date = new Date().toISOString().slice(0, 10);
@@ -12873,7 +12910,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
12873
12910
  let counter = 1;
12874
12911
  while (true) {
12875
12912
  const suffix = counter === 1 ? "" : `_${counter}`;
12876
- const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
12913
+ const path = join21(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
12877
12914
  try {
12878
12915
  writeFileSync9(path, body, { flag: "wx" });
12879
12916
  logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
@@ -13884,7 +13921,7 @@ var init_runtime_turn_requests = __esm(async () => {
13884
13921
  import { randomUUID as randomUUID11 } from "crypto";
13885
13922
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
13886
13923
  import { tmpdir as tmpdir4 } from "os";
13887
- import { join as join21 } from "path";
13924
+ import { join as join22 } from "path";
13888
13925
  async function waitForMemoryArchive(settled, timeoutMs) {
13889
13926
  let timer;
13890
13927
  try {
@@ -14137,7 +14174,7 @@ function formatCompactElapsed(startedAt) {
14137
14174
  async function summarizeTopicContext(request) {
14138
14175
  const startedAt = Date.now();
14139
14176
  const sessionIds = [];
14140
- const compactCwd = mkdtempSync2(join21(tmpdir4(), "negotium-compact-"));
14177
+ const compactCwd = mkdtempSync2(join22(tmpdir4(), "negotium-compact-"));
14141
14178
  const abortController = new AbortController;
14142
14179
  const relayAbort = () => abortController.abort(request.signal?.reason);
14143
14180
  if (request.signal?.aborted)
@@ -14164,7 +14201,7 @@ async function summarizeTopicContext(request) {
14164
14201
  let error = "";
14165
14202
  let toolViolation = false;
14166
14203
  let compactionLogCalls = 0;
14167
- const compactionLogPath = join21(compactCwd, "conversation.log");
14204
+ const compactionLogPath = join22(compactCwd, "conversation.log");
14168
14205
  try {
14169
14206
  const compactionMcp = useCompactionLog ? {
14170
14207
  compact_log: {
@@ -15485,14 +15522,14 @@ var init_self_config = __esm(async () => {
15485
15522
  });
15486
15523
 
15487
15524
  // ../../packages/core/src/query/session-inbox-path.ts
15488
- import { join as join22 } from "path";
15525
+ import { join as join23 } from "path";
15489
15526
  function sessionInboxPath(userId, topicId) {
15490
15527
  const key = Buffer.from(topicId, "utf8").toString("base64url");
15491
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
15528
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
15492
15529
  }
15493
15530
  function scheduledSessionInboxPath(userId, topicId) {
15494
15531
  const key = Buffer.from(topicId, "utf8").toString("base64url");
15495
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
15532
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
15496
15533
  }
15497
15534
  function decodeTopicIdFileName(fileName, suffix) {
15498
15535
  if (!fileName.startsWith(TOPIC_ID_FILE_PREFIX) || !fileName.endsWith(suffix))
@@ -15520,13 +15557,13 @@ var init_session_inbox_path = __esm(() => {
15520
15557
 
15521
15558
  // ../../packages/core/src/query/session-inbox-cleanup.ts
15522
15559
  import { unlinkSync as unlinkSync14 } from "fs";
15523
- import { basename as basename3, join as join23 } from "path";
15560
+ import { basename as basename3, join as join24 } from "path";
15524
15561
  function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
15525
15562
  const live = sessionInboxPath(userId, topicId);
15526
15563
  const scheduled = scheduledSessionInboxPath(userId, topicId);
15527
15564
  const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
15528
15565
  if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename3(legacyTopicTitle) === legacyTopicTitle) {
15529
- const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
15566
+ const legacyBase = join24(SESSION_INBOX_DIR, userId, legacyTopicTitle);
15530
15567
  for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
15531
15568
  candidates.add(`${legacyBase}${suffix}`);
15532
15569
  }
@@ -15552,16 +15589,16 @@ var init_session_inbox_cleanup = __esm(() => {
15552
15589
 
15553
15590
  // ../../packages/core/src/query/state.ts
15554
15591
  import { mkdirSync as mkdirSync14, renameSync as renameSync7, unlinkSync as unlinkSync15, writeFileSync as writeFileSync12 } from "fs";
15555
- import { basename as basename4, join as join24 } from "path";
15592
+ import { basename as basename4, join as join25 } from "path";
15556
15593
  function createQueryStateStore(options) {
15557
15594
  const sanitize = options.sanitizeTopicId ?? sanitizeId;
15558
- const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
15559
- const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
15595
+ const queryStateDirPath = (userId) => join25(options.usersLogDir, String(userId), "active-queries");
15596
+ const queryStateFile = (userId, topicId) => join25(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
15560
15597
  const legacyQueryStateFile = (userId, topicName) => {
15561
15598
  if (!topicName || topicName === "." || topicName === ".." || basename4(topicName) !== topicName) {
15562
15599
  return null;
15563
15600
  }
15564
- return join24(queryStateDirPath(userId), `${topicName}.json`);
15601
+ return join25(queryStateDirPath(userId), `${topicName}.json`);
15565
15602
  };
15566
15603
  return {
15567
15604
  write(userId, topicId, topicName, task) {
@@ -15722,29 +15759,29 @@ import {
15722
15759
  unlinkSync as unlinkSync16,
15723
15760
  writeFileSync as writeFileSync13
15724
15761
  } from "fs";
15725
- import { dirname as dirname14, join as join25 } from "path";
15762
+ import { dirname as dirname14, join as join26 } from "path";
15726
15763
  function pendingAskDir(userId) {
15727
15764
  const rawUserId = String(userId);
15728
15765
  const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash6("sha256").update(rawUserId).digest("hex")}`;
15729
- return join25(resolveStorageSessionAsksDir(), safeUserId);
15766
+ return join26(resolveStorageSessionAsksDir(), safeUserId);
15730
15767
  }
15731
15768
  function encodeAskKey(key) {
15732
15769
  return JSON.stringify([key.from, key.to]);
15733
15770
  }
15734
15771
  function pendingAskPath(key) {
15735
15772
  const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
15736
- return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
15773
+ return join26(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
15737
15774
  }
15738
15775
  function v2PendingAskPath(key) {
15739
15776
  const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
15740
- return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
15777
+ return join26(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
15741
15778
  }
15742
15779
  function legacyPendingAskPath(key) {
15743
15780
  if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
15744
15781
  return null;
15745
15782
  }
15746
15783
  const dir = pendingAskDir(key.userId);
15747
- const candidate = join25(dir, `${key.from}___${key.to}.pending`);
15784
+ const candidate = join26(dir, `${key.from}___${key.to}.pending`);
15748
15785
  return dirname14(candidate) === dir ? candidate : null;
15749
15786
  }
15750
15787
  function parsePendingAskFilename(fileName) {
@@ -15985,7 +16022,7 @@ function listPendingAsksForCaller(args) {
15985
16022
  const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
15986
16023
  if (!parsed)
15987
16024
  continue;
15988
- const path = join25(dir, fileName);
16025
+ const path = join26(dir, fileName);
15989
16026
  const record = readPendingAskFile(path, {
15990
16027
  userId: args.userId,
15991
16028
  from: parsed.from,
@@ -16027,7 +16064,7 @@ function deletePendingAsksForTopic(args) {
16027
16064
  }
16028
16065
  let deleted = 0;
16029
16066
  for (const fileName of files) {
16030
- const path = join25(dir, fileName);
16067
+ const path = join26(dir, fileName);
16031
16068
  const parsed = parsePendingAskFilename(fileName);
16032
16069
  const record = readPendingAskFile(path, {
16033
16070
  userId: args.userId,
@@ -16445,7 +16482,7 @@ __export(exports_token_stats, {
16445
16482
  });
16446
16483
  import { createHash as createHash7 } from "crypto";
16447
16484
  import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
16448
- import { join as join26 } from "path";
16485
+ import { join as join27 } from "path";
16449
16486
  function emptyBucket() {
16450
16487
  return {
16451
16488
  inputTokens: 0,
@@ -16464,7 +16501,7 @@ function queriesPath(userId) {
16464
16501
  const fileId = tokenStatsFileId(userId);
16465
16502
  const logDir = resolveStorageLogDir();
16466
16503
  mkdirSync16(logDir, { recursive: true });
16467
- return join26(logDir, `token-queries-${fileId}.jsonl`);
16504
+ return join27(logDir, `token-queries-${fileId}.jsonl`);
16468
16505
  }
16469
16506
  function loadRecords(userId) {
16470
16507
  try {
@@ -16893,7 +16930,7 @@ var init_lifecycle = __esm(async () => {
16893
16930
  // ../../packages/core/src/runtime/attachments.ts
16894
16931
  import { randomUUID as randomUUID14 } from "crypto";
16895
16932
  import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
16896
- import { basename as basename5, join as join27 } from "path";
16933
+ import { basename as basename5, join as join28 } from "path";
16897
16934
  function workspaceCwdFor(topicId) {
16898
16935
  return resolveTopicWorkspaceDir(topicId);
16899
16936
  }
@@ -16906,7 +16943,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
16906
16943
  if (!attachmentIds?.length)
16907
16944
  return [];
16908
16945
  const out = [];
16909
- const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
16946
+ const destDir = join28(workspaceCwdFor(topicId), "attachments", queryId);
16910
16947
  for (const rawId of attachmentIds) {
16911
16948
  if (typeof rawId !== "string")
16912
16949
  continue;
@@ -16923,7 +16960,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
16923
16960
  mkdirSync17(destDir, { recursive: true });
16924
16961
  const index = String(out.length + 1).padStart(2, "0");
16925
16962
  const safeName = safeAttachmentFilename(attachment.filename, fileId);
16926
- const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
16963
+ const destPath = join28(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
16927
16964
  copyFileSync2(sourcePath, destPath);
16928
16965
  out.push({
16929
16966
  id: attachment.id,
@@ -16953,10 +16990,10 @@ function promptWithAttachments(prompt, attachments) {
16953
16990
  return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
16954
16991
  }
16955
16992
  function ingestAttachment(args) {
16956
- const destDir = join27(UPLOADS_DIR, args.topicId);
16993
+ const destDir = join28(UPLOADS_DIR, args.topicId);
16957
16994
  mkdirSync17(destDir, { recursive: true });
16958
16995
  const safeName = safeAttachmentFilename(args.filename, "upload");
16959
- const destPath = join27(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
16996
+ const destPath = join28(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
16960
16997
  if (args.sourcePath !== undefined) {
16961
16998
  copyFileSync2(args.sourcePath, destPath);
16962
16999
  } else if (args.bytes !== undefined) {
@@ -18267,9 +18304,9 @@ __export(exports_app_settings, {
18267
18304
  DEFAULT_AI_NAME: () => DEFAULT_AI_NAME
18268
18305
  });
18269
18306
  import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "fs";
18270
- import { dirname as dirname15, join as join28 } from "path";
18307
+ import { dirname as dirname15, join as join29 } from "path";
18271
18308
  function settingsFile() {
18272
- return join28(resolveStorageDataDir(), "otium-settings.json");
18309
+ return join29(resolveStorageDataDir(), "otium-settings.json");
18273
18310
  }
18274
18311
  function getGlobalAiName() {
18275
18312
  const path = settingsFile();
@@ -18373,7 +18410,7 @@ __export(exports_turn_runner, {
18373
18410
  });
18374
18411
  import { randomUUID as randomUUID16 } from "crypto";
18375
18412
  import { existsSync as existsSync20, mkdirSync as mkdirSync19, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
18376
- import { join as join29 } from "path";
18413
+ import { join as join30 } from "path";
18377
18414
  function withDefaultPlaywright(configuredMcp, isManager) {
18378
18415
  if (isManager)
18379
18416
  return configuredMcp;
@@ -18428,7 +18465,7 @@ function appendAskReplyMessage(topicId, text2, agentType) {
18428
18465
  return message;
18429
18466
  }
18430
18467
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
18431
- const preferred = join29(directory, preferredFilename);
18468
+ const preferred = join30(directory, preferredFilename);
18432
18469
  if (preferExact && existsSync20(preferred))
18433
18470
  return preferred;
18434
18471
  let newestLegacyId = null;
@@ -18439,7 +18476,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
18439
18476
  const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
18440
18477
  if (!titleMatch && !legacyIdMatch)
18441
18478
  continue;
18442
- const path = join29(directory, filename);
18479
+ const path = join30(directory, filename);
18443
18480
  const mtimeMs = statSync10(path).mtimeMs;
18444
18481
  if (titleMatch) {
18445
18482
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
@@ -18453,9 +18490,9 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
18453
18490
  return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
18454
18491
  }
18455
18492
  function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
18456
- const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
18493
+ const briefFile = resolveWikiMirrorPath(join30(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
18457
18494
  const hasBriefFile = existsSync20(briefFile) && statSync10(briefFile).isFile();
18458
- const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
18495
+ const latestSummaryCandidate = resolveWikiMirrorPath(join30(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
18459
18496
  const latestSummaryFile = existsSync20(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
18460
18497
  return { briefFile, hasBriefFile, latestSummaryFile };
18461
18498
  }
@@ -21205,7 +21242,7 @@ var init_vault_command = __esm(async () => {
21205
21242
  import { execFile } from "child_process";
21206
21243
  import { existsSync as existsSync21, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
21207
21244
  import { readFile } from "fs/promises";
21208
- import { extname, join as join30 } from "path";
21245
+ import { extname, join as join31 } from "path";
21209
21246
  import { promisify } from "util";
21210
21247
  async function extractText(filePath) {
21211
21248
  const ext = extname(filePath).toLowerCase();
@@ -21322,7 +21359,7 @@ async function extractFromAudio(filePath, opts = {}) {
21322
21359
  const tmpDir = `${filePath}_whisper_tmp`;
21323
21360
  try {
21324
21361
  mkdirSync21(tmpDir, { recursive: true });
21325
- const mp3Path = join30(tmpDir, "audio.mp3");
21362
+ const mp3Path = join31(tmpDir, "audio.mp3");
21326
21363
  await execFileAsync(opts.ffmpegBin ?? FFMPEG_BIN2, [
21327
21364
  "-y",
21328
21365
  "-i",
@@ -21349,7 +21386,7 @@ async function extractFromAudio(filePath, opts = {}) {
21349
21386
  "--output-format",
21350
21387
  "txt"
21351
21388
  ], { timeout: 120000 });
21352
- const txtPath = join30(tmpDir, "audio.txt");
21389
+ const txtPath = join31(tmpDir, "audio.txt");
21353
21390
  const text2 = existsSync21(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
21354
21391
  if (!text2) {
21355
21392
  return { text: null, method: "whisper", error: "Whisper produced no text" };
@@ -21522,7 +21559,7 @@ var init_lifecycle2 = __esm(() => {
21522
21559
 
21523
21560
  // ../../packages/core/src/platform/log-rotation.ts
21524
21561
  import { existsSync as existsSync22, renameSync as renameSync8, statSync as statSync11 } from "fs";
21525
- import { join as join31 } from "path";
21562
+ import { join as join32 } from "path";
21526
21563
  function rotateOversizedLog(logPath, maxBytes = DEFAULT_DAEMON_LOG_ROTATE_BYTES) {
21527
21564
  try {
21528
21565
  if (!existsSync22(logPath))
@@ -21531,9 +21568,9 @@ function rotateOversizedLog(logPath, maxBytes = DEFAULT_DAEMON_LOG_ROTATE_BYTES)
21531
21568
  if (size < maxBytes)
21532
21569
  return;
21533
21570
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
21534
- const dir = join31(logPath, "..");
21571
+ const dir = join32(logPath, "..");
21535
21572
  const base = logPath.slice(dir.length + 1);
21536
- renameSync8(logPath, join31(dir, `${base}.${stamp}`));
21573
+ renameSync8(logPath, join32(dir, `${base}.${stamp}`));
21537
21574
  } catch {}
21538
21575
  }
21539
21576
  var DEFAULT_DAEMON_LOG_ROTATE_BYTES;
@@ -21726,7 +21763,7 @@ import {
21726
21763
  rmSync as rmSync8,
21727
21764
  statSync as statSync12
21728
21765
  } from "fs";
21729
- import { join as join32 } from "path";
21766
+ import { join as join33 } from "path";
21730
21767
  function setBashrsCompletionSink(sink) {
21731
21768
  completionSink = sink ?? defaultSink;
21732
21769
  }
@@ -21753,15 +21790,15 @@ function readTail(filePath) {
21753
21790
  }
21754
21791
  }
21755
21792
  function buildMessage(dir, result) {
21756
- const stdout = readTail(join32(dir, "stdout.log"));
21757
- const stderr = readTail(join32(dir, "stderr.log"));
21793
+ const stdout = readTail(join33(dir, "stdout.log"));
21794
+ const stderr = readTail(join33(dir, "stderr.log"));
21758
21795
  const parts = [];
21759
21796
  if (stdout.text.trim() || stdout.truncated) {
21760
- parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join32(dir, "stdout.log")})` : ""}:
21797
+ parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join33(dir, "stdout.log")})` : ""}:
21761
21798
  ${stdout.text.trim()}`);
21762
21799
  }
21763
21800
  if (stderr.text.trim() || stderr.truncated) {
21764
- parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join32(dir, "stderr.log")})` : ""}:
21801
+ parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join33(dir, "stderr.log")})` : ""}:
21765
21802
  ${stderr.text.trim()}`);
21766
21803
  }
21767
21804
  const header = watchHeader(result) ?? `[background_bash ${result.bash_id} finished]`;
@@ -21795,7 +21832,7 @@ function parseOwner(owner) {
21795
21832
  return { userId, topicId };
21796
21833
  }
21797
21834
  function injectedMarker(dir) {
21798
- return join32(dir, "result.json.injected");
21835
+ return join33(dir, "result.json.injected");
21799
21836
  }
21800
21837
  async function flushBashrsCompletions() {
21801
21838
  let entries;
@@ -21806,8 +21843,8 @@ async function flushBashrsCompletions() {
21806
21843
  }
21807
21844
  const now = Date.now();
21808
21845
  for (const bashId of entries) {
21809
- const dir = join32(BASHRS_SPILL_ROOT, bashId);
21810
- const resultPath = join32(dir, "result.json");
21846
+ const dir = join33(BASHRS_SPILL_ROOT, bashId);
21847
+ const resultPath = join33(dir, "result.json");
21811
21848
  const marker = injectedMarker(dir);
21812
21849
  try {
21813
21850
  const markerStat = statSync12(marker);
@@ -22105,7 +22142,7 @@ var init_file_ops = __esm(() => {
22105
22142
  // ../../packages/core/src/runtime/inbox.ts
22106
22143
  import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
22107
22144
  import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync17 } from "fs";
22108
- import { join as join33 } from "path";
22145
+ import { join as join34 } from "path";
22109
22146
  async function createAskForkPlan(options) {
22110
22147
  const snapshot = structuredClone(options.entries);
22111
22148
  const prepareSession = async () => options.synthesize(structuredClone(snapshot));
@@ -22245,7 +22282,7 @@ async function flushSessionInbox() {
22245
22282
  return;
22246
22283
  }
22247
22284
  for (const uid of userDirs) {
22248
- const userInboxDir = join33(SESSION_INBOX_DIR, uid);
22285
+ const userInboxDir = join34(SESSION_INBOX_DIR, uid);
22249
22286
  let entries;
22250
22287
  try {
22251
22288
  entries = readdirSync8(userInboxDir);
@@ -22253,7 +22290,7 @@ async function flushSessionInbox() {
22253
22290
  continue;
22254
22291
  }
22255
22292
  for (const entry of entries) {
22256
- const entryPath = join33(userInboxDir, entry);
22293
+ const entryPath = join34(userInboxDir, entry);
22257
22294
  let isDir = false;
22258
22295
  try {
22259
22296
  isDir = statSync13(entryPath).isDirectory();
@@ -22343,7 +22380,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
22343
22380
  return;
22344
22381
  }
22345
22382
  for (const userId of userDirs) {
22346
- const userInboxDir = join33(SESSION_INBOX_DIR, userId);
22383
+ const userInboxDir = join34(SESSION_INBOX_DIR, userId);
22347
22384
  let files;
22348
22385
  try {
22349
22386
  files = readdirSync8(userInboxDir);
@@ -22355,7 +22392,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
22355
22392
  const topicId = topicIdFromScheduledSessionInboxFileName(file);
22356
22393
  if (!topicId)
22357
22394
  continue;
22358
- const schedulePath = join33(userInboxDir, file);
22395
+ const schedulePath = join34(userInboxDir, file);
22359
22396
  const hasProcessingClaim = files.includes(`${file}.processing`);
22360
22397
  if (!hasProcessingClaim && !scheduledFileNeedsClaim(schedulePath, nowMs))
22361
22398
  continue;
@@ -23192,7 +23229,7 @@ var init_src = __esm(async () => {
23192
23229
 
23193
23230
  // ../../packages/core/src/storage/conversation-migration.ts
23194
23231
  import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync23, readFileSync as readFileSync20 } from "fs";
23195
- import { dirname as dirname16, join as join34 } from "path";
23232
+ import { dirname as dirname16, join as join35 } from "path";
23196
23233
  function readEntries(path) {
23197
23234
  const entries = [];
23198
23235
  for (const [index, line] of readFileSync20(path, "utf8").split(`
@@ -23259,7 +23296,7 @@ function migrateLegacyCompactedConversations() {
23259
23296
  result.skipped++;
23260
23297
  continue;
23261
23298
  }
23262
- const backupPath = join34(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
23299
+ const backupPath = join35(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
23263
23300
  const sourcePath = existsSync24(backupPath) ? backupPath : rawPath;
23264
23301
  const legacyEntries = readEntries(sourcePath);
23265
23302
  const compactIndex = compactionEntryIndex(legacyEntries);
@@ -23319,7 +23356,7 @@ __export(exports_decisions, {
23319
23356
  DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
23320
23357
  });
23321
23358
  import { existsSync as existsSync25, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
23322
- import { dirname as dirname17, join as join35 } from "path";
23359
+ import { dirname as dirname17, join as join36 } from "path";
23323
23360
  function safeDecisionScopeKey(scopeKey) {
23324
23361
  const safe = sanitizeFileName(scopeKey);
23325
23362
  if (!safe || safe === "." || safe === "..") {
@@ -23331,10 +23368,10 @@ function decisionScopeKey(opts) {
23331
23368
  return opts.topicId?.trim() || opts.session || "default";
23332
23369
  }
23333
23370
  function getDecisionFilePath(userId, scopeKey) {
23334
- return join35(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
23371
+ return join36(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
23335
23372
  }
23336
23373
  function getDecisionGraphSvgPath(userId, scopeKey) {
23337
- return join35(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
23374
+ return join36(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
23338
23375
  }
23339
23376
  function writeDecisionGraphSvg(userId, scopeKey, svg) {
23340
23377
  const path = getDecisionGraphSvgPath(userId, scopeKey);
@@ -24594,7 +24631,7 @@ __export(exports_agent_health, {
24594
24631
  });
24595
24632
  import { spawn as spawn6 } from "child_process";
24596
24633
  import { existsSync as existsSync27, readdirSync as readdirSync9 } from "fs";
24597
- import { join as join36 } from "path";
24634
+ import { join as join37 } from "path";
24598
24635
  import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
24599
24636
  import { z as z10 } from "zod";
24600
24637
  function signalProcessTree2(child, signal) {
@@ -24838,7 +24875,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
24838
24875
  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 () => {
24839
24876
  if (!context.userId)
24840
24877
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
24841
- const stateDir = join36(USERS_LOG_DIR, context.userId, "active-queries");
24878
+ const stateDir = join37(USERS_LOG_DIR, context.userId, "active-queries");
24842
24879
  if (!existsSync27(stateDir))
24843
24880
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
24844
24881
  let files;
@@ -24851,7 +24888,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
24851
24888
  const entries = files.flatMap((file) => {
24852
24889
  if (!file.endsWith(".json"))
24853
24890
  return [];
24854
- const state = readJsonFile(join36(stateDir, file));
24891
+ const state = readJsonFile(join37(stateDir, file));
24855
24892
  if (!state)
24856
24893
  return [];
24857
24894
  const sinceMs = new Date(state.since).getTime();
@@ -25084,7 +25121,7 @@ var exports_default_host = {};
25084
25121
  __export(exports_default_host, {
25085
25122
  createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
25086
25123
  });
25087
- import { basename as basename6, join as join37 } from "path";
25124
+ import { basename as basename6, join as join38 } from "path";
25088
25125
  function ok2(text2) {
25089
25126
  return { content: [{ type: "text", text: text2 }] };
25090
25127
  }
@@ -25131,10 +25168,10 @@ function remoteTarget(context, to) {
25131
25168
  return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
25132
25169
  }
25133
25170
  function activeQuery(context, topicId, title) {
25134
- const dir = join37(USERS_LOG_DIR, context.userId, "active-queries");
25135
- const candidates = [join37(dir, `${sanitizeId(topicId)}.json`)];
25171
+ const dir = join38(USERS_LOG_DIR, context.userId, "active-queries");
25172
+ const candidates = [join38(dir, `${sanitizeId(topicId)}.json`)];
25136
25173
  if (title && basename6(title) === title && title !== "." && title !== "..") {
25137
- candidates.push(join37(dir, `${title}.json`));
25174
+ candidates.push(join38(dir, `${title}.json`));
25138
25175
  }
25139
25176
  for (const path of candidates) {
25140
25177
  const state = readJsonFile(path);
@@ -25683,7 +25720,7 @@ import {
25683
25720
  unlinkSync as unlinkSync19,
25684
25721
  writeFileSync as writeFileSync19
25685
25722
  } from "fs";
25686
- import { basename as basename7, dirname as dirname18, join as join38, relative as relative2, resolve as resolve18 } from "path";
25723
+ import { basename as basename7, dirname as dirname18, join as join39, relative as relative2, resolve as resolve18 } from "path";
25687
25724
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
25688
25725
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
25689
25726
  import {
@@ -26133,13 +26170,13 @@ function wikiQuery(args) {
26133
26170
  try {
26134
26171
  for (const entry of readdirSync10(dir, { withFileTypes: true })) {
26135
26172
  if (entry.isDirectory()) {
26136
- const sub = join38(dir, entry.name);
26173
+ const sub = join39(dir, entry.name);
26137
26174
  if (label === "articles" || label === "skills") {
26138
26175
  try {
26139
26176
  for (const f of readdirSync10(sub, { withFileTypes: true })) {
26140
26177
  if (!f.isFile() || !f.name.endsWith(".md"))
26141
26178
  continue;
26142
- const fp = join38(sub, f.name);
26179
+ const fp = join39(sub, f.name);
26143
26180
  try {
26144
26181
  const text2 = readFileSync24(fp, "utf-8");
26145
26182
  const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
@@ -26160,7 +26197,7 @@ function wikiQuery(args) {
26160
26197
  if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
26161
26198
  continue;
26162
26199
  }
26163
- const fp = join38(dir, entry.name);
26200
+ const fp = join39(dir, entry.name);
26164
26201
  try {
26165
26202
  const text2 = readFileSync24(fp, "utf-8");
26166
26203
  const key = entry.name.replace(/\.md$/i, "");
@@ -26881,21 +26918,21 @@ function collectIndexableDocuments() {
26881
26918
  }
26882
26919
  for (const entry of entries) {
26883
26920
  if (entry.isFile() && entry.name.endsWith(".md")) {
26884
- add(kind, entry.name.replace(/\.md$/i, ""), join38(root, entry.name));
26921
+ add(kind, entry.name.replace(/\.md$/i, ""), join39(root, entry.name));
26885
26922
  continue;
26886
26923
  }
26887
26924
  if (!entry.isDirectory() || !allowSubdirectories)
26888
26925
  continue;
26889
26926
  let nested;
26890
26927
  try {
26891
- nested = readdirSync10(join38(root, entry.name), { withFileTypes: true });
26928
+ nested = readdirSync10(join39(root, entry.name), { withFileTypes: true });
26892
26929
  } catch {
26893
26930
  continue;
26894
26931
  }
26895
26932
  for (const file of nested) {
26896
26933
  if (!file.isFile() || !file.name.endsWith(".md"))
26897
26934
  continue;
26898
- add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join38(root, entry.name, file.name));
26935
+ add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join39(root, entry.name, file.name));
26899
26936
  }
26900
26937
  }
26901
26938
  }
@@ -27404,7 +27441,7 @@ import {
27404
27441
  unlinkSync as unlinkSync20,
27405
27442
  writeFileSync as writeFileSync20
27406
27443
  } from "fs";
27407
- import { basename as basename8, join as join39 } from "path";
27444
+ import { basename as basename8, join as join40 } from "path";
27408
27445
  function startLogRotation() {
27409
27446
  if (rotationTimer)
27410
27447
  return;
@@ -27418,7 +27455,7 @@ function writeLog(entry) {
27418
27455
  mkdirSync26(logDir, { recursive: true });
27419
27456
  const safeSession = sanitizeTopicName(entry.session);
27420
27457
  const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
27421
- const file = join39(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
27458
+ const file = join40(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
27422
27459
  try {
27423
27460
  const stat = statSync16(file);
27424
27461
  if (stat.size >= MAX_FILE_SIZE) {
@@ -27438,7 +27475,7 @@ function rotateOldLogs() {
27438
27475
  try {
27439
27476
  const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
27440
27477
  try {
27441
- const stat = statSync16(join39(logDir, f));
27478
+ const stat = statSync16(join40(logDir, f));
27442
27479
  return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
27443
27480
  } catch {
27444
27481
  return null;
@@ -27454,7 +27491,7 @@ function rotateOldLogs() {
27454
27491
  if (freed >= toFree)
27455
27492
  break;
27456
27493
  try {
27457
- unlinkSync20(join39(logDir, file.name));
27494
+ unlinkSync20(join40(logDir, file.name));
27458
27495
  freed += file.size;
27459
27496
  logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
27460
27497
  } catch (e) {
@@ -27466,9 +27503,9 @@ function rotateOldLogs() {
27466
27503
  }
27467
27504
  }
27468
27505
  function writeSentFileLog(entry) {
27469
- const dir = join39(resolveStorageUsersLogDir(), String(entry.userId));
27506
+ const dir = join40(resolveStorageUsersLogDir(), String(entry.userId));
27470
27507
  mkdirSync26(dir, { recursive: true });
27471
- const file = join39(dir, "sent-files.jsonl");
27508
+ const file = join40(dir, "sent-files.jsonl");
27472
27509
  try {
27473
27510
  appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
27474
27511
  } catch (e) {
@@ -27479,7 +27516,7 @@ function entryMatchesTopic(e, topicName) {
27479
27516
  return e.topicName === topicName;
27480
27517
  }
27481
27518
  function readSentFilesForTopic(userId, topicName) {
27482
- const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27519
+ const file = join40(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27483
27520
  if (!existsSync30(file))
27484
27521
  return [];
27485
27522
  try {
@@ -27490,7 +27527,7 @@ function readSentFilesForTopic(userId, topicName) {
27490
27527
  }
27491
27528
  }
27492
27529
  function removeSentFilesForTopic(userId, topicName) {
27493
- const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27530
+ const file = join40(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27494
27531
  if (!existsSync30(file))
27495
27532
  return;
27496
27533
  try {
@@ -28977,11 +29014,11 @@ var init_src2 = __esm(async () => {
28977
29014
  });
28978
29015
 
28979
29016
  // ../../packages/mcp-host/src/paths.ts
28980
- import { homedir as homedir9 } from "os";
29017
+ import { homedir as homedir10 } from "os";
28981
29018
  import { resolve as resolve21 } from "path";
28982
29019
  function stateDir() {
28983
29020
  const env = process.env.NEGOTIUM_STATE_DIR?.trim();
28984
- return env ? resolve21(env) : resolve21(homedir9(), ".negotium");
29021
+ return env ? resolve21(env) : resolve21(homedir10(), ".negotium");
28985
29022
  }
28986
29023
  function defaultPortsDir() {
28987
29024
  return resolve21(stateDir(), "run", "mcp-ports");
@@ -29134,7 +29171,7 @@ import {
29134
29171
  writeFileSync as writeFileSync22
29135
29172
  } from "fs";
29136
29173
  import { connect, createServer as createServer4 } from "net";
29137
- import { join as join40 } from "path";
29174
+ import { join as join41 } from "path";
29138
29175
  function regKey(key, instanceKey) {
29139
29176
  return `${key}\x00${instanceKey}`;
29140
29177
  }
@@ -29436,7 +29473,7 @@ class McpHost {
29436
29473
  if (file.includes(".tmp-"))
29437
29474
  continue;
29438
29475
  try {
29439
- const port = Number.parseInt(readFileSync26(join40(this.portsDir, file), "utf8").trim(), 10);
29476
+ const port = Number.parseInt(readFileSync26(join41(this.portsDir, file), "utf8").trim(), 10);
29440
29477
  if (!Number.isNaN(port))
29441
29478
  claims.set(file, port);
29442
29479
  } catch {}
@@ -29446,7 +29483,7 @@ class McpHost {
29446
29483
  writePortFile(fileName, port) {
29447
29484
  try {
29448
29485
  mkdirSync28(this.portsDir, { recursive: true });
29449
- const file = join40(this.portsDir, fileName);
29486
+ const file = join41(this.portsDir, fileName);
29450
29487
  const tmp = `${file}.tmp-${process.pid}`;
29451
29488
  writeFileSync22(tmp, String(port));
29452
29489
  renameSync15(tmp, file);
@@ -29456,7 +29493,7 @@ class McpHost {
29456
29493
  }
29457
29494
  deletePortFile(fileName) {
29458
29495
  try {
29459
- unlinkSync21(join40(this.portsDir, fileName));
29496
+ unlinkSync21(join41(this.portsDir, fileName));
29460
29497
  } catch {}
29461
29498
  }
29462
29499
  cleanupRunning(rkey, inst) {
@@ -29568,7 +29605,7 @@ import {
29568
29605
  statSync as statSync18,
29569
29606
  writeFileSync as writeFileSync23
29570
29607
  } from "fs";
29571
- import { basename as basename10, extname as extname4, join as join41 } from "path";
29608
+ import { basename as basename10, extname as extname4, join as join42 } from "path";
29572
29609
  function safeExtension(filename) {
29573
29610
  const extension = extname4(basename10(filename));
29574
29611
  return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
@@ -29586,14 +29623,14 @@ function contentDisposition(filename) {
29586
29623
 
29587
29624
  class NodeFileStore {
29588
29625
  uploadDir;
29589
- constructor(uploadDir = join41(DATA_DIR, "uploads")) {
29626
+ constructor(uploadDir = join42(DATA_DIR, "uploads")) {
29590
29627
  this.uploadDir = uploadDir;
29591
29628
  }
29592
29629
  #ensureDir() {
29593
29630
  mkdirSync29(this.uploadDir, { recursive: true });
29594
29631
  }
29595
29632
  #metadataPath(fileId) {
29596
- return join41(this.uploadDir, `${fileId}.meta.json`);
29633
+ return join42(this.uploadDir, `${fileId}.meta.json`);
29597
29634
  }
29598
29635
  #metadata(fileId) {
29599
29636
  if (!FILE_ID_RE.test(fileId))
@@ -29622,7 +29659,7 @@ class NodeFileStore {
29622
29659
  hooks = {
29623
29660
  resolveAttachmentByFileId: (fileId) => {
29624
29661
  const metadata = this.#metadata(fileId);
29625
- if (!metadata || !existsSync32(join41(this.uploadDir, metadata.savedName)))
29662
+ if (!metadata || !existsSync32(join42(this.uploadDir, metadata.savedName)))
29626
29663
  return null;
29627
29664
  return this.#attachment(fileId, metadata);
29628
29665
  },
@@ -29630,7 +29667,7 @@ class NodeFileStore {
29630
29667
  const metadata = this.#metadata(fileId);
29631
29668
  if (!metadata)
29632
29669
  return null;
29633
- const path = join41(this.uploadDir, metadata.savedName);
29670
+ const path = join42(this.uploadDir, metadata.savedName);
29634
29671
  return existsSync32(path) ? path : null;
29635
29672
  },
29636
29673
  storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
@@ -29645,11 +29682,11 @@ class NodeFileStore {
29645
29682
  const mimeType = file.type || MIME_BY_EXT2[extension] || "application/octet-stream";
29646
29683
  const existing = this.#metadata(fileId);
29647
29684
  if (existing) {
29648
- const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId && existsSync32(join41(this.uploadDir, existing.savedName));
29685
+ const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId && existsSync32(join42(this.uploadDir, existing.savedName));
29649
29686
  return matches ? this.#attachment(fileId, existing) : null;
29650
29687
  }
29651
29688
  const savedName = `${fileId}${extension}`;
29652
- const savedPath = join41(this.uploadDir, savedName);
29689
+ const savedPath = join42(this.uploadDir, savedName);
29653
29690
  try {
29654
29691
  const sizeBytes = await Bun.write(savedPath, file);
29655
29692
  if (sizeBytes !== file.size)
@@ -29674,14 +29711,14 @@ class NodeFileStore {
29674
29711
  }
29675
29712
  allows(fileId, access) {
29676
29713
  const metadata = this.#metadata(fileId);
29677
- return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync32(join41(this.uploadDir, metadata.savedName)));
29714
+ return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync32(join42(this.uploadDir, metadata.savedName)));
29678
29715
  }
29679
29716
  store(absPath, access = {}) {
29680
29717
  this.#ensureDir();
29681
29718
  const fileId = randomUUID24();
29682
29719
  const extension = safeExtension(absPath);
29683
29720
  const savedName = `${fileId}${extension}`;
29684
- const savedPath = join41(this.uploadDir, savedName);
29721
+ const savedPath = join42(this.uploadDir, savedName);
29685
29722
  try {
29686
29723
  const stats = statSync18(absPath);
29687
29724
  if (!stats.isFile() || stats.size > MAX_NODE_UPLOAD_BYTES)
@@ -29713,7 +29750,7 @@ class NodeFileStore {
29713
29750
  const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
29714
29751
  if (!allowed)
29715
29752
  return null;
29716
- const path = join41(this.uploadDir, metadata.savedName);
29753
+ const path = join42(this.uploadDir, metadata.savedName);
29717
29754
  if (!existsSync32(path))
29718
29755
  return null;
29719
29756
  return new Response(Bun.file(path), {
@@ -29732,7 +29769,7 @@ class NodeFileStore {
29732
29769
  const metadata = this.#metadata(fileId);
29733
29770
  if (metadata?.topicId !== topicId)
29734
29771
  continue;
29735
- rmSync9(join41(this.uploadDir, metadata.savedName), { force: true });
29772
+ rmSync9(join42(this.uploadDir, metadata.savedName), { force: true });
29736
29773
  rmSync9(this.#metadataPath(fileId), { force: true });
29737
29774
  }
29738
29775
  }
@@ -33855,8 +33892,8 @@ var init_context_usage = __esm(async () => {
33855
33892
  // ../../adapters/terminal/src/path-suggest.ts
33856
33893
  import { execFile as execFile3 } from "child_process";
33857
33894
  import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
33858
- import { homedir as homedir10 } from "os";
33859
- import { basename as basename11, dirname as dirname21, join as join42, sep as sep3 } from "path";
33895
+ import { homedir as homedir11 } from "os";
33896
+ import { basename as basename11, dirname as dirname21, join as join43, sep as sep3 } from "path";
33860
33897
  import { promisify as promisify3 } from "util";
33861
33898
  function indexRecursivePaths(files) {
33862
33899
  const paths = [];
@@ -33950,25 +33987,25 @@ function activeAtToken(lineText, col) {
33950
33987
  return { start: col - frag.length - 1, frag };
33951
33988
  }
33952
33989
  function resolveFragment(frag) {
33953
- const home = homedir10();
33990
+ const home = homedir11();
33954
33991
  let path;
33955
33992
  if (frag === "" || frag === "~")
33956
33993
  path = home;
33957
33994
  else if (frag === "~/")
33958
33995
  path = home;
33959
33996
  else if (frag.startsWith("~/"))
33960
- path = join42(home, frag.slice(2));
33997
+ path = join43(home, frag.slice(2));
33961
33998
  else if (frag.startsWith("/"))
33962
33999
  path = frag;
33963
34000
  else
33964
- path = join42(home, frag);
34001
+ path = join43(home, frag);
33965
34002
  if (frag.endsWith("/") || frag === "" || frag === "~") {
33966
34003
  return { dir: path, prefix: "" };
33967
34004
  }
33968
34005
  return { dir: dirname21(path), prefix: basename11(path) };
33969
34006
  }
33970
34007
  function toToken(fullPath, isDir) {
33971
- const home = homedir10();
34008
+ const home = homedir11();
33972
34009
  let shown = fullPath;
33973
34010
  if (fullPath === home)
33974
34011
  shown = "~";
@@ -33990,7 +34027,7 @@ function rankAndSlice(dir, candidates) {
33990
34027
  });
33991
34028
  return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
33992
34029
  label: `${relPath}${isDir ? "/" : ""}`,
33993
- value: toToken(join42(dir, relPath), isDir),
34030
+ value: toToken(join43(dir, relPath), isDir),
33994
34031
  isDir
33995
34032
  }));
33996
34033
  }
@@ -34069,7 +34106,7 @@ function pathSuggestions(lineText, col) {
34069
34106
  let isDir = entry.isDirectory();
34070
34107
  if (!isDir && entry.isSymbolicLink()) {
34071
34108
  try {
34072
- isDir = statSync19(join42(dir, entry.name)).isDirectory();
34109
+ isDir = statSync19(join43(dir, entry.name)).isDirectory();
34073
34110
  } catch {}
34074
34111
  }
34075
34112
  return {
@@ -34092,14 +34129,14 @@ function pathSuggestions(lineText, col) {
34092
34129
  };
34093
34130
  }
34094
34131
  function fragmentToAbsolutePath(frag) {
34095
- const home = homedir10();
34132
+ const home = homedir11();
34096
34133
  if (frag === "~" || frag === "~/")
34097
34134
  return home;
34098
34135
  if (frag.startsWith("~/"))
34099
- return join42(home, frag.slice(2));
34136
+ return join43(home, frag.slice(2));
34100
34137
  if (frag.startsWith("/"))
34101
34138
  return frag;
34102
- return join42(home, frag);
34139
+ return join43(home, frag);
34103
34140
  }
34104
34141
  function fragmentResolves(frag) {
34105
34142
  try {
@@ -40605,7 +40642,7 @@ var init_commands2 = __esm(async () => {
40605
40642
  // ../../adapters/telegram/src/mapping-store.ts
40606
40643
  import { Database as Database2 } from "bun:sqlite";
40607
40644
  import { mkdirSync as mkdirSync32 } from "fs";
40608
- import { dirname as dirname22, join as join43 } from "path";
40645
+ import { dirname as dirname22, join as join44 } from "path";
40609
40646
  function outboxRowToEntry(row) {
40610
40647
  return {
40611
40648
  id: row.id,
@@ -40671,7 +40708,7 @@ function migrateOutboxSchema(db4) {
40671
40708
  db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
40672
40709
  }
40673
40710
  function openMappingStore(path) {
40674
- const dbPath = path ?? join43(DATA_DIR, "adapter-telegram.db");
40711
+ const dbPath = path ?? join44(DATA_DIR, "adapter-telegram.db");
40675
40712
  if (dbPath !== ":memory:")
40676
40713
  mkdirSync32(dirname22(dbPath), { recursive: true });
40677
40714
  const db4 = new Database2(dbPath);
@@ -42571,9 +42608,9 @@ function firstCell() {
42571
42608
  return cell;
42572
42609
  return null;
42573
42610
  }
42574
- function attachOtiumCentralCell(join44) {
42575
- cells.set(join44.cellId, {
42576
- join: join44,
42611
+ function attachOtiumCentralCell(join45) {
42612
+ cells.set(join45.cellId, {
42613
+ join: join45,
42577
42614
  nodesCache: null,
42578
42615
  verifyCache: new Map,
42579
42616
  tokenCache: new Map
@@ -42585,10 +42622,10 @@ function detachOtiumCentralCell(cellId) {
42585
42622
  function attachedOtiumCells() {
42586
42623
  return [...cells.values()].map((cell) => cell.join);
42587
42624
  }
42588
- function configureOtiumCentral(join44) {
42625
+ function configureOtiumCentral(join45) {
42589
42626
  cells.clear();
42590
- if (join44)
42591
- attachOtiumCentralCell(join44);
42627
+ if (join45)
42628
+ attachOtiumCentralCell(join45);
42592
42629
  }
42593
42630
  function isOtiumCentralConfigured() {
42594
42631
  return cells.size > 0;
@@ -43152,17 +43189,17 @@ function withJoinCredentialLock(operation) {
43152
43189
  function joinsEqual(left, right) {
43153
43190
  return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
43154
43191
  }
43155
- function normalizedJoin(join44) {
43192
+ function normalizedJoin(join45) {
43156
43193
  return normalizeJoin({
43157
- v: join44.v,
43158
- central: join44.central,
43159
- relay: join44.relay,
43160
- cellId: join44.cellId,
43161
- secret: join44.secret
43194
+ v: join45.v,
43195
+ central: join45.central,
43196
+ relay: join45.relay,
43197
+ cellId: join45.cellId,
43198
+ secret: join45.secret
43162
43199
  });
43163
43200
  }
43164
- function joinCredentialDigest(join44) {
43165
- return createHash11("sha256").update(JSON.stringify(normalizedJoin(join44))).digest("base64url");
43201
+ function joinCredentialDigest(join45) {
43202
+ return createHash11("sha256").update(JSON.stringify(normalizedJoin(join45))).digest("base64url");
43166
43203
  }
43167
43204
  function readPersistedJoins(path = joinFilePath()) {
43168
43205
  if (!existsSync39(path))
@@ -43181,9 +43218,9 @@ function readPersistedJoins(path = joinFilePath()) {
43181
43218
  return normalizeJoin(entry);
43182
43219
  });
43183
43220
  }
43184
- function isJoinPersisted(join44) {
43221
+ function isJoinPersisted(join45) {
43185
43222
  try {
43186
- const normalized = normalizedJoin(join44);
43223
+ const normalized = normalizedJoin(join45);
43187
43224
  return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
43188
43225
  } catch {
43189
43226
  return false;
@@ -43227,10 +43264,10 @@ function writeJoins(joins, allowOverwrite) {
43227
43264
  }
43228
43265
  return path;
43229
43266
  }
43230
- function saveJoinWhileLocked(join44, options = {}) {
43267
+ function saveJoinWhileLocked(join45, options = {}) {
43231
43268
  const path = joinFilePath();
43232
43269
  const directory = dirname23(path);
43233
- const normalized = normalizedJoin(join44);
43270
+ const normalized = normalizedJoin(join45);
43234
43271
  mkdirSync33(directory, { recursive: true });
43235
43272
  if (!existsSync39(path))
43236
43273
  return writeJoins([normalized], false);
@@ -43260,8 +43297,8 @@ function saveJoinWhileLocked(join44, options = {}) {
43260
43297
  const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
43261
43298
  return writeJoins(next, true);
43262
43299
  }
43263
- function saveJoin(join44, options = {}) {
43264
- return withJoinCredentialLock(() => saveJoinWhileLocked(join44, options));
43300
+ function saveJoin(join45, options = {}) {
43301
+ return withJoinCredentialLock(() => saveJoinWhileLocked(join45, options));
43265
43302
  }
43266
43303
  function removeJoin(cellId) {
43267
43304
  return withJoinCredentialLock(() => {
@@ -43274,7 +43311,7 @@ function removeJoin(cellId) {
43274
43311
  if (cellId) {
43275
43312
  let remaining;
43276
43313
  try {
43277
- remaining = readPersistedJoins(path).filter((join44) => join44.cellId !== cellId);
43314
+ remaining = readPersistedJoins(path).filter((join45) => join45.cellId !== cellId);
43278
43315
  } catch {
43279
43316
  return false;
43280
43317
  }
@@ -43327,7 +43364,7 @@ var init_join = __esm(async () => {
43327
43364
  // ../../adapters/otium/src/peer-files.ts
43328
43365
  import { randomUUID as randomUUID29 } from "crypto";
43329
43366
  import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync34, rmSync as rmSync11, statSync as statSync21 } from "fs";
43330
- import { basename as basename13, extname as extname6, join as join44 } from "path";
43367
+ import { basename as basename13, extname as extname6, join as join45 } from "path";
43331
43368
  function fileType(mimeType) {
43332
43369
  if (mimeType.startsWith("image/"))
43333
43370
  return "image";
@@ -43393,7 +43430,7 @@ function recordFile(args) {
43393
43430
  function insertLocalFile(args) {
43394
43431
  const id = randomUUID29();
43395
43432
  mkdirSync34(PEER_FILES_DIR, { recursive: true });
43396
- const path = join44(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
43433
+ const path = join45(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
43397
43434
  copyFileSync5(args.sourcePath, path);
43398
43435
  return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
43399
43436
  }
@@ -43439,7 +43476,7 @@ function installPeerFileHooks() {
43439
43476
  var PEER_FILES_DIR;
43440
43477
  var init_peer_files = __esm(async () => {
43441
43478
  await init_src();
43442
- PEER_FILES_DIR = join44(DATA_DIR, "otium-peer-files");
43479
+ PEER_FILES_DIR = join45(DATA_DIR, "otium-peer-files");
43443
43480
  db.exec(`
43444
43481
  CREATE TABLE IF NOT EXISTS otium_peer_files (
43445
43482
  id TEXT PRIMARY KEY,
@@ -43851,19 +43888,19 @@ function readCache() {
43851
43888
  return {};
43852
43889
  }
43853
43890
  }
43854
- function cachedSurfaceScope(join45) {
43855
- const record = readCache()[join45.cellId];
43856
- if (!record || record.central !== join45.central || !record.workspaceId)
43891
+ function cachedSurfaceScope(join46) {
43892
+ const record = readCache()[join46.cellId];
43893
+ if (!record || record.central !== join46.central || !record.workspaceId)
43857
43894
  return null;
43858
43895
  return surfaceScopeFor(record.central, record.workspaceId);
43859
43896
  }
43860
- function cacheSurfaceScope(join45, workspaceId) {
43861
- const scope = surfaceScopeFor(join45.central, workspaceId);
43897
+ function cacheSurfaceScope(join46, workspaceId) {
43898
+ const scope = surfaceScopeFor(join46.central, workspaceId);
43862
43899
  const path = scopeCachePath();
43863
43900
  try {
43864
43901
  mkdirSync35(dirname24(path), { recursive: true });
43865
43902
  const cache = readCache();
43866
- cache[join45.cellId] = { central: join45.central, workspaceId, scope };
43903
+ cache[join46.cellId] = { central: join46.central, workspaceId, scope };
43867
43904
  writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
43868
43905
  `, { mode: 384 });
43869
43906
  } catch (err2) {
@@ -43871,23 +43908,23 @@ function cacheSurfaceScope(join45, workspaceId) {
43871
43908
  }
43872
43909
  return scope;
43873
43910
  }
43874
- async function resolveSurfaceScope(join45) {
43875
- const cached = cachedSurfaceScope(join45);
43911
+ async function resolveSurfaceScope(join46) {
43912
+ const cached = cachedSurfaceScope(join46);
43876
43913
  if (cached)
43877
43914
  return cached;
43878
43915
  try {
43879
- const workspaceId = await peerWorkspaceIdForCell(join45.cellId);
43916
+ const workspaceId = await peerWorkspaceIdForCell(join46.cellId);
43880
43917
  if (!workspaceId)
43881
43918
  return null;
43882
- return cacheSurfaceScope(join45, workspaceId);
43919
+ return cacheSurfaceScope(join46, workspaceId);
43883
43920
  } catch (err2) {
43884
43921
  logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
43885
43922
  return null;
43886
43923
  }
43887
43924
  }
43888
43925
  function surfaceScopeForCell(cellId) {
43889
- const join45 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
43890
- return join45 ? cachedSurfaceScope(join45) : null;
43926
+ const join46 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
43927
+ return join46 ? cachedSurfaceScope(join46) : null;
43891
43928
  }
43892
43929
  function unscopedRoomsAddressable() {
43893
43930
  return attachedOtiumCells().length < 2;
@@ -45020,13 +45057,13 @@ function replacePendingEnrollment(pending2) {
45020
45057
  throw error2;
45021
45058
  }
45022
45059
  }
45023
- function recordClaimedCredential(pending2, join45) {
45060
+ function recordClaimedCredential(pending2, join46) {
45024
45061
  withJoinCredentialLock(() => {
45025
45062
  const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
45026
45063
  if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
45027
45064
  throw new Error("pending Otium enrollment changed while its claim was in flight");
45028
45065
  }
45029
- const claimed = { digest: joinCredentialDigest(join45), cellId: join45.cellId };
45066
+ const claimed = { digest: joinCredentialDigest(join46), cellId: join46.cellId };
45030
45067
  if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
45031
45068
  throw new Error("central returned different credentials for an idempotent enrollment claim");
45032
45069
  }
@@ -45084,30 +45121,30 @@ async function claimEnrollment(invite, nodeName) {
45084
45121
  const secret = openCredential(credential, pending2.privateKey);
45085
45122
  if (!secret.startsWith("rcs_"))
45086
45123
  throw new Error("central returned an invalid runtime credential");
45087
- const join45 = {
45124
+ const join46 = {
45088
45125
  v: 2,
45089
45126
  central: invite.central,
45090
45127
  relay: String(response.relayUrl),
45091
45128
  cellId: String(response.cell?.id),
45092
45129
  secret
45093
45130
  };
45094
- if (!join45.relay || !join45.cellId || join45.cellId === "undefined") {
45131
+ if (!join46.relay || !join46.cellId || join46.cellId === "undefined") {
45095
45132
  throw new Error("central returned an incomplete enrollment response");
45096
45133
  }
45097
- assertSecureRelayUrl(join45.relay);
45098
- recordClaimedCredential(pending2, join45);
45099
- return join45;
45134
+ assertSecureRelayUrl(join46.relay);
45135
+ recordClaimedCredential(pending2, join46);
45136
+ return join46;
45100
45137
  }
45101
- function commitEnrollment(join45, options = {}) {
45138
+ function commitEnrollment(join46, options = {}) {
45102
45139
  return withJoinCredentialLock(() => {
45103
45140
  const pendingPath = pendingEnrollmentPath();
45104
45141
  const pending2 = existsSync40(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
45105
- const digest = joinCredentialDigest(join45);
45106
- if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join45.cellId)) {
45142
+ const digest = joinCredentialDigest(join46);
45143
+ if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join46.cellId)) {
45107
45144
  throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
45108
45145
  }
45109
- const path = saveJoinWhileLocked(join45, options);
45110
- if (!isJoinPersisted(join45)) {
45146
+ const path = saveJoinWhileLocked(join46, options);
45147
+ if (!isJoinPersisted(join46)) {
45111
45148
  throw new Error("Otium join credentials were not durably persisted");
45112
45149
  }
45113
45150
  if (!pending2)
@@ -45701,24 +45738,24 @@ function refreshDefaultSurfaceScope() {
45701
45738
  setSurfaceScopeRequired(scopes.length > 1);
45702
45739
  }
45703
45740
  function startOtiumNodeRuntime(options) {
45704
- const { join: join45 } = options;
45705
- attachOtiumCentralCell(join45);
45741
+ const { join: join46 } = options;
45742
+ attachOtiumCentralCell(join46);
45706
45743
  const releaseGlobals = acquireGlobalOtiumServices();
45707
45744
  let stopped = false;
45708
- logger.info({ central: join45.central, cellId: join45.cellId }, "otium: worker mode enabled");
45709
- mountedScopes.set(join45.cellId, cachedSurfaceScope(join45));
45745
+ logger.info({ central: join46.central, cellId: join46.cellId }, "otium: worker mode enabled");
45746
+ mountedScopes.set(join46.cellId, cachedSurfaceScope(join46));
45710
45747
  refreshDefaultSurfaceScope();
45711
- selfPeerNodeForCell(join45.cellId).then((self) => {
45748
+ selfPeerNodeForCell(join46.cellId).then((self) => {
45712
45749
  if (self) {
45713
45750
  logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
45714
45751
  }
45715
45752
  }).catch((err2) => {
45716
45753
  logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
45717
45754
  });
45718
- resolveSurfaceScope(join45).then((scope) => {
45755
+ resolveSurfaceScope(join46).then((scope) => {
45719
45756
  if (!scope || stopped)
45720
45757
  return;
45721
- mountedScopes.set(join45.cellId, scope);
45758
+ mountedScopes.set(join46.cellId, scope);
45722
45759
  refreshDefaultSurfaceScope();
45723
45760
  if (mountedScopes.size === 1)
45724
45761
  stampUnscopedOtiumTopics(scope);
@@ -45727,14 +45764,14 @@ function startOtiumNodeRuntime(options) {
45727
45764
  });
45728
45765
  return {
45729
45766
  name: "otium",
45730
- join: join45,
45767
+ join: join46,
45731
45768
  stop: () => {
45732
45769
  if (stopped)
45733
45770
  return;
45734
45771
  stopped = true;
45735
- mountedScopes.delete(join45.cellId);
45772
+ mountedScopes.delete(join46.cellId);
45736
45773
  refreshDefaultSurfaceScope();
45737
- detachOtiumCentralCell(join45.cellId);
45774
+ detachOtiumCentralCell(join46.cellId);
45738
45775
  releaseGlobals();
45739
45776
  }
45740
45777
  };
@@ -45829,14 +45866,14 @@ __export(exports_node_runtime, {
45829
45866
  function sameCredentials(left, right) {
45830
45867
  return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
45831
45868
  }
45832
- function attachOtiumWorkspace(join45) {
45833
- const current3 = mounted.get(join45.cellId);
45869
+ function attachOtiumWorkspace(join46) {
45870
+ const current3 = mounted.get(join46.cellId);
45834
45871
  if (current3) {
45835
- if (sameCredentials(current3.join, join45))
45872
+ if (sameCredentials(current3.join, join46))
45836
45873
  return false;
45837
- detachOtiumWorkspace(join45.cellId);
45874
+ detachOtiumWorkspace(join46.cellId);
45838
45875
  }
45839
- mounted.set(join45.cellId, startOtiumNodeRuntime({ join: join45 }));
45876
+ mounted.set(join46.cellId, startOtiumNodeRuntime({ join: join46 }));
45840
45877
  return true;
45841
45878
  }
45842
45879
  function detachOtiumWorkspace(cellId) {
@@ -45851,7 +45888,7 @@ function mountedOtiumWorkspaces() {
45851
45888
  return [...mounted.values()].map((runtime2) => runtime2.join);
45852
45889
  }
45853
45890
  function reconcileOtiumWorkspaces(joins = loadJoins()) {
45854
- const wanted = new Map(joins.map((join45) => [join45.cellId, join45]));
45891
+ const wanted = new Map(joins.map((join46) => [join46.cellId, join46]));
45855
45892
  const detached = [];
45856
45893
  for (const cellId of [...mounted.keys()]) {
45857
45894
  if (wanted.has(cellId))
@@ -45860,8 +45897,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
45860
45897
  detached.push(cellId);
45861
45898
  }
45862
45899
  const attached = [];
45863
- for (const [cellId, join45] of wanted) {
45864
- if (attachOtiumWorkspace(join45))
45900
+ for (const [cellId, join46] of wanted) {
45901
+ if (attachOtiumWorkspace(join46))
45865
45902
  attached.push(cellId);
45866
45903
  }
45867
45904
  if (attached.length > 0 || detached.length > 0) {
@@ -45884,9 +45921,9 @@ async function handleOtiumAdapterControlRequest(req) {
45884
45921
  if (req.method === "GET") {
45885
45922
  return Response.json({
45886
45923
  ok: true,
45887
- workspaces: mountedOtiumWorkspaces().map((join45) => ({
45888
- cellId: join45.cellId,
45889
- central: join45.central
45924
+ workspaces: mountedOtiumWorkspaces().map((join46) => ({
45925
+ cellId: join46.cellId,
45926
+ central: join46.central
45890
45927
  }))
45891
45928
  });
45892
45929
  }
@@ -45907,8 +45944,8 @@ function mountConfiguredOtiumNodeRuntime() {
45907
45944
  const joins = loadJoins();
45908
45945
  if (joins.length === 0)
45909
45946
  return null;
45910
- for (const join45 of joins)
45911
- attachOtiumWorkspace(join45);
45947
+ for (const join46 of joins)
45948
+ attachOtiumWorkspace(join46);
45912
45949
  registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
45913
45950
  let stopped = false;
45914
45951
  return {
@@ -46020,11 +46057,11 @@ async function joinCommand(args) {
46020
46057
  process.exitCode = 1;
46021
46058
  return;
46022
46059
  }
46023
- let join45;
46060
+ let join46;
46024
46061
  let productionEnrollment = false;
46025
46062
  if (args.includes("--legacy")) {
46026
46063
  try {
46027
- join45 = parseInviteCode(code);
46064
+ join46 = parseInviteCode(code);
46028
46065
  } catch (err2) {
46029
46066
  console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
46030
46067
  process.exitCode = 1;
@@ -46050,7 +46087,7 @@ async function joinCommand(args) {
46050
46087
  return;
46051
46088
  }
46052
46089
  }
46053
- join45 = await claimEnrollment(invite, nodeName);
46090
+ join46 = await claimEnrollment(invite, nodeName);
46054
46091
  productionEnrollment = true;
46055
46092
  } catch (err2) {
46056
46093
  console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
@@ -46061,16 +46098,16 @@ async function joinCommand(args) {
46061
46098
  let path;
46062
46099
  try {
46063
46100
  const saveOptions = { replaceExisting: args.includes("--replace") };
46064
- path = productionEnrollment ? commitEnrollment(join45, saveOptions) : saveJoin(join45, saveOptions);
46101
+ path = productionEnrollment ? commitEnrollment(join46, saveOptions) : saveJoin(join46, saveOptions);
46065
46102
  } catch (err2) {
46066
46103
  console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
46067
46104
  process.exitCode = 1;
46068
46105
  return;
46069
46106
  }
46070
46107
  console.log(`otium join credentials saved to ${path}`);
46071
- console.log(` central: ${join45.central}`);
46072
- console.log(` cellId: ${join45.cellId}`);
46073
- configureOtiumCentral(join45);
46108
+ console.log(` central: ${join46.central}`);
46109
+ console.log(` cellId: ${join46.cellId}`);
46110
+ configureOtiumCentral(join46);
46074
46111
  try {
46075
46112
  const self = await selfPeerNode();
46076
46113
  if (self) {
@@ -46112,19 +46149,19 @@ async function statusCommand() {
46112
46149
  return;
46113
46150
  }
46114
46151
  configureOtiumCentral(joins[0] ?? null);
46115
- for (const join45 of joins.slice(1))
46116
- attachOtiumCentralCell(join45);
46117
- for (const [index, join45] of joins.entries()) {
46152
+ for (const join46 of joins.slice(1))
46153
+ attachOtiumCentralCell(join46);
46154
+ for (const [index, join46] of joins.entries()) {
46118
46155
  if (index > 0)
46119
46156
  console.log("");
46120
- console.log(`cellId: ${join45.cellId}`);
46121
- console.log(`central: ${join45.central}`);
46122
- if (join45.relay)
46123
- console.log(`relay: ${join45.relay}`);
46157
+ console.log(`cellId: ${join46.cellId}`);
46158
+ console.log(`central: ${join46.central}`);
46159
+ if (join46.relay)
46160
+ console.log(`relay: ${join46.relay}`);
46124
46161
  try {
46125
- const self = await selfPeerNodeForCell(join45.cellId);
46162
+ const self = await selfPeerNodeForCell(join46.cellId);
46126
46163
  if (self) {
46127
- console.log(`node: ${self.nodeName ?? join45.cellId}${self.isPrimary ? " (primary)" : ""}`);
46164
+ console.log(`node: ${self.nodeName ?? join46.cellId}${self.isPrimary ? " (primary)" : ""}`);
46128
46165
  console.log(`baseUrl: ${self.baseUrl}`);
46129
46166
  } else {
46130
46167
  console.warn(" warning: central answered but this cell has no visible assignment yet \u2014 check the workspace assignment");
@@ -46160,13 +46197,13 @@ function resolveTunnelTargets(joins, relayOverride) {
46160
46197
  const targets = [];
46161
46198
  const skippedNoRelay = [];
46162
46199
  const envRelay = process.env.OTIUM_RELAY_URL?.trim();
46163
- for (const join45 of joins) {
46164
- const relayUrl = relayOverride?.trim() || join45.relay || envRelay;
46200
+ for (const join46 of joins) {
46201
+ const relayUrl = relayOverride?.trim() || join46.relay || envRelay;
46165
46202
  if (!relayUrl) {
46166
- skippedNoRelay.push(join45.cellId);
46203
+ skippedNoRelay.push(join46.cellId);
46167
46204
  continue;
46168
46205
  }
46169
- targets.push({ cellId: join45.cellId, relayUrl, secret: join45.secret });
46206
+ targets.push({ cellId: join46.cellId, relayUrl, secret: join46.secret });
46170
46207
  }
46171
46208
  return { targets, skippedNoRelay };
46172
46209
  }
@@ -46403,9 +46440,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
46403
46440
  if (joins.length === 0)
46404
46441
  throw new Error("not joined to an Otium workspace");
46405
46442
  if (!targetCellId && joins.length > 1) {
46406
- throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join45) => join45.cellId).join(", ")}`);
46443
+ throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join46) => join46.cellId).join(", ")}`);
46407
46444
  }
46408
- if (targetCellId && !joins.some((join45) => join45.cellId === targetCellId)) {
46445
+ if (targetCellId && !joins.some((join46) => join46.cellId === targetCellId)) {
46409
46446
  throw new Error(`not joined as ${targetCellId}`);
46410
46447
  }
46411
46448
  removeJoin2(targetCellId);
@@ -47192,4 +47229,4 @@ switch (command) {
47192
47229
  }
47193
47230
  }
47194
47231
 
47195
- //# debugId=8E2AD65C6830FCEF64756E2164756E21
47232
+ //# debugId=805E32726887103964756E2164756E21