negotium 0.2.19 → 0.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/agent-helpers.js +367 -313
  2. package/dist/agent-helpers.js.map +17 -17
  3. package/dist/background-bash.js +3 -2
  4. package/dist/background-bash.js.map +3 -3
  5. package/dist/browser-runtime.js +60 -36
  6. package/dist/browser-runtime.js.map +6 -6
  7. package/dist/{chunk-fc58cqry.js → chunk-g2kqfdwm.js} +154 -24
  8. package/dist/{chunk-fc58cqry.js.map → chunk-g2kqfdwm.js.map} +6 -5
  9. package/dist/hosted-agent.js +183 -53
  10. package/dist/hosted-agent.js.map +7 -6
  11. package/dist/main.js +671 -425
  12. package/dist/main.js.map +23 -23
  13. package/dist/mcp-factories.js +334 -280
  14. package/dist/mcp-factories.js.map +17 -17
  15. package/dist/prompts.js +3 -2
  16. package/dist/prompts.js.map +3 -3
  17. package/dist/query-runtime.js +47 -26
  18. package/dist/query-runtime.js.map +4 -4
  19. package/dist/registry.js +3 -3
  20. package/dist/registry.js.map +2 -2
  21. package/dist/rollout.js +1 -1
  22. package/dist/runtime/src/agents/archiver.ts +3 -2
  23. package/dist/runtime/src/application/switch-topic-access-mode.ts +81 -8
  24. package/dist/runtime/src/bus.ts +19 -2
  25. package/dist/runtime/src/mcp/session-comm/runtime.ts +16 -0
  26. package/dist/runtime/src/platform/config.ts +13 -1
  27. package/dist/runtime/src/runtime/ask-callbacks.ts +19 -11
  28. package/dist/runtime/src/runtime/background-sessions.ts +9 -7
  29. package/dist/runtime/src/runtime/visual-store.ts +31 -23
  30. package/dist/runtime/src/storage/api-topics.ts +26 -0
  31. package/dist/runtime/src/storage/browser-profiles.ts +16 -8
  32. package/dist/runtime/src/storage/runtime-events.ts +30 -20
  33. package/dist/runtime/src/storage/runtime-gateway-submissions.ts +24 -16
  34. package/dist/runtime/src/storage/runtime-turn-requests.ts +9 -1
  35. package/dist/runtime/src/storage/self-schedules.ts +32 -24
  36. package/dist/runtime/src/storage/storage-host.ts +105 -43
  37. package/dist/runtime/src/storage/vault.ts +12 -3
  38. package/dist/runtime/src/version.ts +1 -1
  39. package/dist/runtime-helpers.js +47 -26
  40. package/dist/runtime-helpers.js.map +4 -4
  41. package/dist/storage.js +76 -41
  42. package/dist/storage.js.map +4 -4
  43. package/dist/types/packages/core/src/storage/api-topics.d.ts +15 -0
  44. package/dist/types/packages/core/src/storage/storage-host.d.ts +10 -0
  45. package/dist/types/packages/core/src/version.d.ts +1 -1
  46. package/dist/vault.js +140 -10
  47. package/dist/vault.js.map +6 -5
  48. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -152,7 +152,7 @@ function browserRsMeetsMinimumVersion(candidate) {
152
152
  try {
153
153
  const output = execFileSync(candidate, ["--version"], {
154
154
  encoding: "utf8",
155
- timeout: 2000,
155
+ timeout: BROWSER_RS_VERSION_PROBE_TIMEOUT_MS,
156
156
  stdio: ["ignore", "pipe", "ignore"]
157
157
  }).trim();
158
158
  const match = output.match(/^browser-rs (\d+)\.(\d+)\.(\d+)$/);
@@ -263,7 +263,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
263
263
  function codexAuthFilePath() {
264
264
  return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
265
265
  }
266
- var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.19", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", SNIPPETS_API_URL, BROWSER_RS_BIN, BASH_RS_VERSION = "v0.1.5", BASH_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, BASHRS_SPILL_ROOT, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
266
+ var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.19", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000, SNIPPETS_API_URL, BROWSER_RS_BIN, BASH_RS_VERSION = "v0.1.5", BASH_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, BASHRS_SPILL_ROOT, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
267
267
  var init_config = __esm(() => {
268
268
  init_config_helpers();
269
269
  init_logger();
@@ -421,6 +421,225 @@ var init_sqlite = __esm(async () => {
421
421
  }
422
422
  });
423
423
 
424
+ // ../../packages/core/src/storage/storage-host.ts
425
+ import { mkdirSync as mkdirSync2 } from "fs";
426
+ import { homedir as homedir2 } from "os";
427
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
428
+ function storageState() {
429
+ const holder = globalThis;
430
+ const existing = holder[STORAGE_HOST_STATE];
431
+ if (existing)
432
+ return existing;
433
+ const created = {
434
+ configuredHost: {},
435
+ fallbackDatabase: null,
436
+ fallbackDatabasePath: null,
437
+ frames: [],
438
+ schemaInitializers: [],
439
+ initializedSchemas: new WeakMap,
440
+ initializingDatabases: new WeakSet
441
+ };
442
+ Object.defineProperty(holder, STORAGE_HOST_STATE, {
443
+ value: created,
444
+ enumerable: false,
445
+ writable: false,
446
+ configurable: false
447
+ });
448
+ return created;
449
+ }
450
+ function envPath(name, fallback) {
451
+ const value = process.env[name]?.trim();
452
+ return resolve2(value || fallback);
453
+ }
454
+ function defaultStateDir() {
455
+ return envPath("NEGOTIUM_STATE_DIR", join2(homedir2(), ".negotium"));
456
+ }
457
+ function defaultDataDir() {
458
+ return envPath("NEGOTIUM_DATA_DIR", join2(defaultStateDir(), "data"));
459
+ }
460
+ function defaultLogDir() {
461
+ return envPath("NEGOTIUM_LOG_DIR", join2(defaultStateDir(), "logs"));
462
+ }
463
+ function defaultWorkspaceDir() {
464
+ return envPath("NEGOTIUM_WORKSPACE_DIR", join2(defaultStateDir(), "workspace"));
465
+ }
466
+ function defaultSessionAsksDir() {
467
+ const runDir = envPath("NEGOTIUM_RUN_DIR", join2(defaultStateDir(), "runtime"));
468
+ return join2(runDir, "session-asks");
469
+ }
470
+ function defaultSessionsDatabasePath() {
471
+ return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
472
+ }
473
+ function isSqliteBusy(error) {
474
+ const message = error instanceof Error ? error.message : String(error);
475
+ return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
476
+ }
477
+ function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
478
+ const deadline = Date.now() + timeoutMs;
479
+ while (true) {
480
+ try {
481
+ database.exec(sql);
482
+ return;
483
+ } catch (error) {
484
+ if (!isSqliteBusy(error) || Date.now() >= deadline)
485
+ throw error;
486
+ Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
487
+ }
488
+ }
489
+ }
490
+ function initializeDatabase(database) {
491
+ database.exec("PRAGMA busy_timeout = 5000");
492
+ execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
493
+ database.exec("PRAGMA foreign_keys = ON");
494
+ database.exec("PRAGMA wal_autocheckpoint = 1000");
495
+ try {
496
+ database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
497
+ } catch {}
498
+ }
499
+ function defaultDatabase() {
500
+ const path = defaultSessionsDatabasePath();
501
+ const state = storageState();
502
+ if (state.fallbackDatabase && state.fallbackDatabasePath === path)
503
+ return state.fallbackDatabase;
504
+ if (state.fallbackDatabase)
505
+ state.fallbackDatabase.close();
506
+ mkdirSync2(dirname2(path), { recursive: true });
507
+ state.fallbackDatabase = new Database(path, { create: true });
508
+ state.fallbackDatabasePath = path;
509
+ initializeDatabase(state.fallbackDatabase);
510
+ return state.fallbackDatabase;
511
+ }
512
+ function resolveStorageDatabase() {
513
+ return storageState().configuredHost.database ?? defaultDatabase();
514
+ }
515
+ function resolveStorageDataDir() {
516
+ return storageState().configuredHost.dataDir ?? defaultDataDir();
517
+ }
518
+ function resolveStorageLogDir() {
519
+ return storageState().configuredHost.logDir ?? defaultLogDir();
520
+ }
521
+ function resolveStorageSessionAsksDir() {
522
+ return storageState().configuredHost.sessionAsksDir ?? defaultSessionAsksDir();
523
+ }
524
+ function resolveStorageWorkspaceDir() {
525
+ return storageState().configuredHost.workspaceDir ?? defaultWorkspaceDir();
526
+ }
527
+ function resolveStorageSharedWikiDir() {
528
+ return storageState().configuredHost.sharedWikiDir ?? join2(resolveStorageWorkspaceDir(), "wiki");
529
+ }
530
+ function resolveStorageUsersLogDir() {
531
+ return storageState().configuredHost.usersLogDir ?? join2(resolveStorageDataDir(), "users");
532
+ }
533
+ function normalizeStorageHostPatch(options) {
534
+ const patch = {};
535
+ if (options.database !== undefined) {
536
+ options.database.exec("PRAGMA foreign_keys = ON");
537
+ patch.database = options.database;
538
+ }
539
+ for (const key of STORAGE_PATH_KEYS) {
540
+ const value = options[key];
541
+ if (value === undefined)
542
+ continue;
543
+ if (!value.trim())
544
+ throw new TypeError(`${key} must not be empty`);
545
+ patch[key] = resolve2(value);
546
+ }
547
+ return Object.freeze(patch);
548
+ }
549
+ function refreshConfiguredHost() {
550
+ const state = storageState();
551
+ state.configuredHost = Object.freeze(Object.assign({}, ...state.frames.filter((frame) => frame.active).map((frame) => frame.patch)));
552
+ }
553
+ function configureStorageHost(options) {
554
+ const frame = { active: true, patch: normalizeStorageHostPatch(options) };
555
+ storageState().frames.push(frame);
556
+ refreshConfiguredHost();
557
+ return () => {
558
+ if (!frame.active)
559
+ return;
560
+ frame.active = false;
561
+ const frames = storageState().frames;
562
+ const index = frames.indexOf(frame);
563
+ if (index >= 0)
564
+ frames.splice(index, 1);
565
+ refreshConfiguredHost();
566
+ };
567
+ }
568
+ function resetStorageHost() {
569
+ const frames = storageState().frames;
570
+ for (const frame of frames)
571
+ frame.active = false;
572
+ frames.length = 0;
573
+ refreshConfiguredHost();
574
+ }
575
+ function closeStorageDatabase() {
576
+ const state = storageState();
577
+ if (!state.fallbackDatabase)
578
+ return;
579
+ state.fallbackDatabase.close();
580
+ state.fallbackDatabase = null;
581
+ state.fallbackDatabasePath = null;
582
+ }
583
+ function registerStorageSchemaInitializer(initialize, priority = 100) {
584
+ const initializers = storageState().schemaInitializers;
585
+ initializers.push({ initialize, priority });
586
+ initializers.sort((a, b) => a.priority - b.priority);
587
+ }
588
+ function ensureStorageSchemas(database = resolveStorageDatabase()) {
589
+ const state = storageState();
590
+ if (state.initializingDatabases.has(database))
591
+ return;
592
+ let initialized = state.initializedSchemas.get(database);
593
+ if (!initialized) {
594
+ initialized = new Set;
595
+ state.initializedSchemas.set(database, initialized);
596
+ }
597
+ state.initializingDatabases.add(database);
598
+ try {
599
+ for (const entry of state.schemaInitializers) {
600
+ if (initialized.has(entry.initialize))
601
+ continue;
602
+ initialized.add(entry.initialize);
603
+ try {
604
+ entry.initialize(database);
605
+ } catch (error) {
606
+ initialized.delete(entry.initialize);
607
+ throw error;
608
+ }
609
+ }
610
+ } finally {
611
+ state.initializingDatabases.delete(database);
612
+ }
613
+ }
614
+ var STORAGE_HOST_STATE, SQLITE_INIT_RETRY_MS = 25, SQLITE_INIT_TIMEOUT_MS = 5000, SQLITE_INIT_SLEEP, STORAGE_PATH_KEYS, internalStorageDatabase, storageDatabase;
615
+ var init_storage_host = __esm(async () => {
616
+ await init_sqlite();
617
+ STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
618
+ SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
619
+ STORAGE_PATH_KEYS = [
620
+ "dataDir",
621
+ "logDir",
622
+ "sessionAsksDir",
623
+ "workspaceDir",
624
+ "sharedWikiDir",
625
+ "usersLogDir"
626
+ ];
627
+ internalStorageDatabase = new Proxy({}, {
628
+ get(_target, property) {
629
+ const database = resolveStorageDatabase();
630
+ ensureStorageSchemas(database);
631
+ const value = Reflect.get(database, property, database);
632
+ return typeof value === "function" ? value.bind(database) : value;
633
+ },
634
+ set(_target, property, value) {
635
+ const database = resolveStorageDatabase();
636
+ ensureStorageSchemas(database);
637
+ return Reflect.set(database, property, value, database);
638
+ }
639
+ });
640
+ storageDatabase = internalStorageDatabase;
641
+ });
642
+
424
643
  // ../../packages/core/src/storage/vault-crypto-core.ts
425
644
  import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes2 } from "crypto";
426
645
  function encryptionKey(masterKey) {
@@ -477,8 +696,8 @@ var init_vault_crypto = __esm(() => {
477
696
  });
478
697
 
479
698
  // ../../packages/core/src/storage/vault.ts
480
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "fs";
481
- import { join as join2 } from "path";
699
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync3 } from "fs";
700
+ import { join as join3 } from "path";
482
701
  function initializeVaultDatabase(database) {
483
702
  database.exec("PRAGMA journal_mode = WAL");
484
703
  database.exec("PRAGMA busy_timeout = 5000");
@@ -513,17 +732,18 @@ function initializeVaultDatabase(database) {
513
732
  }
514
733
  }
515
734
  function openVaultDatabase(dataDir) {
516
- const vaultDir = join2(dataDir, "vault");
517
- const path = join2(vaultDir, "vault.db");
518
- mkdirSync2(vaultDir, { recursive: true, mode: 448 });
735
+ const vaultDir = join3(dataDir, "vault");
736
+ const path = join3(vaultDir, "vault.db");
737
+ mkdirSync3(vaultDir, { recursive: true, mode: 448 });
519
738
  const database = new Database(path, { create: true });
520
739
  chmodSync2(path, 384);
521
740
  initializeVaultDatabase(database);
522
741
  return database;
523
742
  }
524
743
  function activeVaultDatabase() {
525
- if (!vaultDb)
526
- vaultDb = openVaultDatabase(DATA_DIR);
744
+ if (!vaultDb) {
745
+ vaultDb = openVaultDatabase(resolveStorageDataDir());
746
+ }
527
747
  return vaultDb;
528
748
  }
529
749
  function configureVaultStorage(options) {
@@ -532,7 +752,7 @@ function configureVaultStorage(options) {
532
752
  }
533
753
  const previousDb = vaultDb;
534
754
  const previousMasterKey = vaultMasterKey;
535
- const configuredDb = options.database ?? openVaultDatabase(options.dataDir ?? DATA_DIR);
755
+ const configuredDb = options.database ?? openVaultDatabase(options.dataDir ?? resolveStorageDataDir());
536
756
  if (options.database)
537
757
  initializeVaultDatabase(configuredDb);
538
758
  vaultDb = configuredDb;
@@ -687,7 +907,10 @@ var vaultDb, vaultMasterKey, VAULT_KEY_PATTERN, VAULT_VALUE_MIN_BYTES = 4, VAULT
687
907
  var init_vault = __esm(async () => {
688
908
  init_config();
689
909
  init_vault_crypto();
690
- await init_sqlite();
910
+ await __promiseAll([
911
+ init_sqlite(),
912
+ init_storage_host()
913
+ ]);
691
914
  vaultMasterKey = VAULT_MASTER_KEY;
692
915
  VAULT_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
693
916
  VAULT_VALUE_MAX_BYTES = 64 * 1024;
@@ -701,8 +924,8 @@ __export(exports_auth_check, {
701
924
  });
702
925
  import { execFileSync as execFileSync2 } from "child_process";
703
926
  import { existsSync as existsSync2 } from "fs";
704
- import { homedir as homedir2, platform } from "os";
705
- import { join as join3 } from "path";
927
+ import { homedir as homedir3, platform } from "os";
928
+ import { join as join4 } from "path";
706
929
  function hasMaestroCredential(host, key, userId) {
707
930
  if (userId && host.getVaultValue(userId, key)?.trim())
708
931
  return true;
@@ -722,7 +945,7 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost, userId) {
722
945
  case "claude": {
723
946
  if (host.environment.ANTHROPIC_API_KEY)
724
947
  return { ok: true };
725
- const path = join3(host.homeDirectory(), ".claude", ".credentials.json");
948
+ const path = join4(host.homeDirectory(), ".claude", ".credentials.json");
726
949
  if (host.operatingSystem() === "darwin") {
727
950
  if (host.hasMacOsCredential("Claude Code-credentials") || host.exists(path)) {
728
951
  return { ok: true };
@@ -775,7 +998,7 @@ var init_auth_check = __esm(async () => {
775
998
  codexAuthFilePath,
776
999
  exists: existsSync2,
777
1000
  environment: process.env,
778
- homeDirectory: homedir2,
1001
+ homeDirectory: homedir3,
779
1002
  operatingSystem: platform,
780
1003
  hasMacOsCredential(service) {
781
1004
  try {
@@ -842,12 +1065,12 @@ function createCodexTreeManager(host, options = {}) {
842
1065
  const previous = spawnChain;
843
1066
  let release;
844
1067
  let released = false;
845
- spawnChain = new Promise((resolve2) => {
1068
+ spawnChain = new Promise((resolve3) => {
846
1069
  release = () => {
847
1070
  if (released)
848
1071
  return;
849
1072
  released = true;
850
- resolve2();
1073
+ resolve3();
851
1074
  };
852
1075
  });
853
1076
  await previous;
@@ -954,7 +1177,7 @@ function createCodexTreeManager(host, options = {}) {
954
1177
  return;
955
1178
  const deadline = Date.now() + Math.max(0, graceMs);
956
1179
  while (Date.now() < deadline && hasSurvivors(plan.targets)) {
957
- await new Promise((resolve2) => setTimeout(resolve2, Math.min(100, Math.max(1, deadline - Date.now()))));
1180
+ await new Promise((resolve3) => setTimeout(resolve3, Math.min(100, Math.max(1, deadline - Date.now()))));
958
1181
  }
959
1182
  killStragglers(plan);
960
1183
  }
@@ -1102,8 +1325,8 @@ function renderUserTurnBatch(messages) {
1102
1325
  }
1103
1326
 
1104
1327
  // ../../packages/core/src/agents/rollout/shared.ts
1105
- import { mkdirSync as mkdirSync3 } from "fs";
1106
- import { resolve as resolve2 } from "path";
1328
+ import { mkdirSync as mkdirSync4 } from "fs";
1329
+ import { resolve as resolve3 } from "path";
1107
1330
  function clone(obj) {
1108
1331
  return structuredClone(obj);
1109
1332
  }
@@ -1113,7 +1336,7 @@ function assertUuidLike(label, value) {
1113
1336
  }
1114
1337
  }
1115
1338
  function assertCwdInWorkspace(cwd) {
1116
- const abs = resolve2(cwd);
1339
+ const abs = resolve3(cwd);
1117
1340
  const ok = trustedWorkspaceRoots.some((root) => abs === root || abs.startsWith(`${root}/`));
1118
1341
  if (!ok) {
1119
1342
  throw new Error(`rollout: cwd outside trusted workspace roots: ${cwd} (resolved=${abs})`);
@@ -1122,7 +1345,7 @@ function assertCwdInWorkspace(cwd) {
1122
1345
  function ensureCwdExists(cwd) {
1123
1346
  assertCwdInWorkspace(cwd);
1124
1347
  try {
1125
- mkdirSync3(cwd, { recursive: true });
1348
+ mkdirSync4(cwd, { recursive: true });
1126
1349
  } catch (err) {
1127
1350
  logger.warn({ err, cwd }, "rollout: ensureCwdExists failed \u2014 caller should pre-create cwd");
1128
1351
  }
@@ -1243,8 +1466,8 @@ var init_shared = __esm(() => {
1243
1466
  DM_WORKSPACE_DIR,
1244
1467
  SESSION_WORKSPACE_DIR,
1245
1468
  TOPIC_WORKSPACE_DIR
1246
- ].map((root) => resolve2(root));
1247
- FIXTURES_DIR = resolve2(PROJECT_ROOT, "src", "agents", "fixtures");
1469
+ ].map((root) => resolve3(root));
1470
+ FIXTURES_DIR = resolve3(PROJECT_ROOT, "src", "agents", "fixtures");
1248
1471
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1249
1472
  });
1250
1473
 
@@ -1279,7 +1502,7 @@ import {
1279
1502
  appendFileSync,
1280
1503
  closeSync,
1281
1504
  fsyncSync,
1282
- mkdirSync as mkdirSync4,
1505
+ mkdirSync as mkdirSync5,
1283
1506
  openSync,
1284
1507
  readFileSync as readFileSync2,
1285
1508
  renameSync,
@@ -1287,7 +1510,7 @@ import {
1287
1510
  unlinkSync as unlinkSync2,
1288
1511
  writeFileSync as writeFileSync2
1289
1512
  } from "fs";
1290
- import { dirname as dirname2 } from "path";
1513
+ import { dirname as dirname3 } from "path";
1291
1514
  function readJsonlLines(filePath) {
1292
1515
  return readFileSync2(filePath, "utf-8").trim().split(`
1293
1516
  `).filter(Boolean);
@@ -1335,7 +1558,7 @@ function appendJsonlEntry(filePath, entry) {
1335
1558
  `);
1336
1559
  }
1337
1560
  function appendJsonlLine(filePath, line) {
1338
- mkdirSync4(dirname2(filePath), { recursive: true });
1561
+ mkdirSync5(dirname3(filePath), { recursive: true });
1339
1562
  const lockPath = `${filePath}${LOCK_SUFFIX}`;
1340
1563
  const payload = line.endsWith(`
1341
1564
  `) ? line : `${line}
@@ -1366,8 +1589,8 @@ function appendJsonlLine(filePath, line) {
1366
1589
  }
1367
1590
  }
1368
1591
  function writeJsonlFile(filePath, entries) {
1369
- const dir = dirname2(filePath);
1370
- mkdirSync4(dir, { recursive: true });
1592
+ const dir = dirname3(filePath);
1593
+ mkdirSync5(dir, { recursive: true });
1371
1594
  const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
1372
1595
  const payload = `${entries.map((e) => JSON.stringify(e)).join(`
1373
1596
  `)}
@@ -1425,12 +1648,12 @@ var init_jsonl = __esm(() => {
1425
1648
  // ../../packages/core/src/agents/rollout/claude.ts
1426
1649
  import { randomUUID } from "crypto";
1427
1650
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1428
- import { homedir as homedir3 } from "os";
1429
- import { join as join4 } from "path";
1651
+ import { homedir as homedir4 } from "os";
1652
+ import { join as join5 } from "path";
1430
1653
  function loadClaudeAttachments() {
1431
1654
  if (_attachmentsCache)
1432
1655
  return _attachmentsCache;
1433
- const raw = readFileSync3(join4(FIXTURES_DIR, "claude-attachments.jsonl"), "utf8");
1656
+ const raw = readFileSync3(join5(FIXTURES_DIR, "claude-attachments.jsonl"), "utf8");
1434
1657
  const lines = parseJsonlText(raw);
1435
1658
  if (lines.length < 2) {
1436
1659
  throw new Error(`loadClaudeAttachments: expected >=2 entries in claude-attachments.jsonl, got ${lines.length}`);
@@ -1531,8 +1754,8 @@ function writeClaudeRollout(opts) {
1531
1754
  });
1532
1755
  lastUuid = assistantUuid;
1533
1756
  }
1534
- const projectsDir = join4(homedir3(), ".claude", "projects", encodeClaudeCwd(opts.cwd));
1535
- const path = join4(projectsDir, `${sessionId}.jsonl`);
1757
+ const projectsDir = join5(homedir4(), ".claude", "projects", encodeClaudeCwd(opts.cwd));
1758
+ const path = join5(projectsDir, `${sessionId}.jsonl`);
1536
1759
  writeJsonlFile(path, lines);
1537
1760
  logger.info({ sessionId, path, pairs: pairs.length }, "writeClaudeRollout: synthetic rollout placed");
1538
1761
  return { sessionId, rolloutPath: path };
@@ -1545,9 +1768,9 @@ var init_claude = __esm(() => {
1545
1768
  });
1546
1769
 
1547
1770
  // ../../packages/core/src/agents/claude-registry.ts
1548
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "fs";
1549
- import { homedir as homedir4 } from "os";
1550
- import { join as join5 } from "path";
1771
+ import { existsSync as existsSync4, mkdirSync as mkdirSync6, readdirSync, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "fs";
1772
+ import { homedir as homedir5 } from "os";
1773
+ import { join as join6 } from "path";
1551
1774
  var ALIAS_MAP, VALID_ALIASES, VALID_EFFORTS, claudeRegistry, claudeRegistryOperations;
1552
1775
  var init_claude_registry = __esm(() => {
1553
1776
  init_claude();
@@ -1594,15 +1817,15 @@ var init_claude_registry = __esm(() => {
1594
1817
  const result = await forkSession(parentSessionId, {
1595
1818
  ...title ? { title } : {}
1596
1819
  });
1597
- const projectsRoot = join5(homedir4(), ".claude", "projects");
1598
- const destDir = join5(projectsRoot, encodeClaudeCwd(cwd));
1599
- const destPath = join5(destDir, `${result.sessionId}.jsonl`);
1820
+ const projectsRoot = join6(homedir5(), ".claude", "projects");
1821
+ const destDir = join6(projectsRoot, encodeClaudeCwd(cwd));
1822
+ const destPath = join6(destDir, `${result.sessionId}.jsonl`);
1600
1823
  if (!existsSync4(destPath)) {
1601
- const sourcePath = readdirSync(projectsRoot).map((d) => join5(projectsRoot, d, `${result.sessionId}.jsonl`)).find((p) => existsSync4(p));
1824
+ const sourcePath = readdirSync(projectsRoot).map((d) => join6(projectsRoot, d, `${result.sessionId}.jsonl`)).find((p) => existsSync4(p));
1602
1825
  if (!sourcePath) {
1603
1826
  throw new Error(`claude forkSession: fork rollout ${result.sessionId}.jsonl not found under ${projectsRoot}`);
1604
1827
  }
1605
- mkdirSync5(destDir, { recursive: true });
1828
+ mkdirSync6(destDir, { recursive: true });
1606
1829
  renameSync2(sourcePath, destPath);
1607
1830
  }
1608
1831
  return {
@@ -1611,10 +1834,10 @@ var init_claude_registry = __esm(() => {
1611
1834
  };
1612
1835
  },
1613
1836
  async cleanupRollouts({ cwd, sessionIds }) {
1614
- const projectsDir = join5(homedir4(), ".claude", "projects", encodeClaudeCwd(cwd));
1837
+ const projectsDir = join6(homedir5(), ".claude", "projects", encodeClaudeCwd(cwd));
1615
1838
  const failures = [];
1616
1839
  for (const sid of sessionIds) {
1617
- const path = join5(projectsDir, `${sid}.jsonl`);
1840
+ const path = join6(projectsDir, `${sid}.jsonl`);
1618
1841
  try {
1619
1842
  unlinkSync3(path);
1620
1843
  } catch (e) {
@@ -1636,7 +1859,7 @@ var exports_version = {};
1636
1859
  __export(exports_version, {
1637
1860
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
1638
1861
  });
1639
- var NEGOTIUM_VERSION = "0.2.19";
1862
+ var NEGOTIUM_VERSION = "0.2.20";
1640
1863
 
1641
1864
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1642
1865
  import { spawn } from "child_process";
@@ -1654,7 +1877,7 @@ import {
1654
1877
  } from "fs";
1655
1878
  import { createRequire as createRequire2 } from "module";
1656
1879
  import { tmpdir } from "os";
1657
- import { dirname as dirname3, join as join6 } from "path";
1880
+ import { dirname as dirname4, join as join7 } from "path";
1658
1881
  function readPackageVersion(packageJsonPath) {
1659
1882
  const parsed = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
1660
1883
  if (typeof parsed.version !== "string" || !parsed.version.trim()) {
@@ -1663,7 +1886,7 @@ function readPackageVersion(packageJsonPath) {
1663
1886
  return parsed.version;
1664
1887
  }
1665
1888
  function codexCliScriptPath() {
1666
- return join6(dirname3(bundledCodexPackagePath), "bin", "codex.js");
1889
+ return join7(dirname4(bundledCodexPackagePath), "bin", "codex.js");
1667
1890
  }
1668
1891
  function parseCodexModelCache(contents, sourcePath) {
1669
1892
  let parsed;
@@ -1704,14 +1927,14 @@ function writePrivateFileAtomic(path, contents) {
1704
1927
  }
1705
1928
  }
1706
1929
  function bundledCodexModelCachePath(authFilePath) {
1707
- return join6(dirname3(authFilePath), NEGOTIUM_MODEL_CACHE);
1930
+ return join7(dirname4(authFilePath), NEGOTIUM_MODEL_CACHE);
1708
1931
  }
1709
1932
  async function bootstrapCodexModelCache(codexHome, cachePath) {
1710
1933
  const child = spawn(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
1711
1934
  env: { ...process.env, CODEX_HOME: codexHome },
1712
1935
  stdio: ["pipe", "pipe", "pipe"]
1713
1936
  });
1714
- await new Promise((resolve3, reject) => {
1937
+ await new Promise((resolve4, reject) => {
1715
1938
  let settled = false;
1716
1939
  let stdoutBuffer = "";
1717
1940
  let stderr = "";
@@ -1730,7 +1953,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
1730
1953
  else if (!existsSync5(cachePath))
1731
1954
  reject(new Error("Codex did not create its model cache"));
1732
1955
  else
1733
- resolve3();
1956
+ resolve4();
1734
1957
  };
1735
1958
  const send = (message) => {
1736
1959
  child.stdin.write(`${JSON.stringify(message)}
@@ -1789,16 +2012,16 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
1789
2012
  });
1790
2013
  }
1791
2014
  async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
1792
- const sourceHome = dirname3(authFilePath);
1793
- const isolatedHome = mkdtempSync(join6(tmpdir(), "negotium-codex-models-"));
1794
- const isolatedCachePath = join6(isolatedHome, "models_cache.json");
2015
+ const sourceHome = dirname4(authFilePath);
2016
+ const isolatedHome = mkdtempSync(join7(tmpdir(), "negotium-codex-models-"));
2017
+ const isolatedCachePath = join7(isolatedHome, "models_cache.json");
1795
2018
  try {
1796
- const isolatedAuthPath = join6(isolatedHome, "auth.json");
2019
+ const isolatedAuthPath = join7(isolatedHome, "auth.json");
1797
2020
  copyFileSync(authFilePath, isolatedAuthPath);
1798
2021
  chmodSync3(isolatedAuthPath, 384);
1799
- const sourceConfigPath = join6(sourceHome, "config.toml");
2022
+ const sourceConfigPath = join7(sourceHome, "config.toml");
1800
2023
  if (existsSync5(sourceConfigPath)) {
1801
- const isolatedConfigPath = join6(isolatedHome, "config.toml");
2024
+ const isolatedConfigPath = join7(isolatedHome, "config.toml");
1802
2025
  copyFileSync(sourceConfigPath, isolatedConfigPath);
1803
2026
  chmodSync3(isolatedConfigPath, 384);
1804
2027
  }
@@ -1809,7 +2032,7 @@ async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
1809
2032
  }
1810
2033
  }
1811
2034
  async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexModelCache) {
1812
- const codexHome = dirname3(authFilePath);
2035
+ const codexHome = dirname4(authFilePath);
1813
2036
  const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
1814
2037
  if (configuredCachePath) {
1815
2038
  if (!existsSync5(configuredCachePath)) {
@@ -1819,7 +2042,7 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
1819
2042
  return configuredCachePath;
1820
2043
  }
1821
2044
  const bundledCachePath = bundledCodexModelCachePath(authFilePath);
1822
- const sharedCachePath = join6(codexHome, "models_cache.json");
2045
+ const sharedCachePath = join7(codexHome, "models_cache.json");
1823
2046
  if (existsSync5(sharedCachePath)) {
1824
2047
  try {
1825
2048
  const shared = readCompatibleCodexModelCache(sharedCachePath);
@@ -1838,8 +2061,8 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
1838
2061
  return bundledCachePath;
1839
2062
  }
1840
2063
  function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
1841
- const codexHome = dirname3(authFilePath);
1842
- const outputPath = join6(codexHome, NEGOTIUM_MODEL_CATALOG);
2064
+ const codexHome = dirname4(authFilePath);
2065
+ const outputPath = join7(codexHome, NEGOTIUM_MODEL_CATALOG);
1843
2066
  const parsed = readCodexModelCache(sourcePath).parsed;
1844
2067
  const models = parsed.models.map((model, index) => {
1845
2068
  if (!model || typeof model !== "object" || Array.isArray(model)) {
@@ -1866,9 +2089,9 @@ var init_codex_native_multi_agent = __esm(() => {
1866
2089
 
1867
2090
  // ../../packages/core/src/security/sensitive-path.ts
1868
2091
  import { realpathSync } from "fs";
1869
- import { resolve as resolve3 } from "path";
2092
+ import { resolve as resolve4 } from "path";
1870
2093
  function isSensitivePath(filePath) {
1871
- const normalized = resolve3(filePath);
2094
+ const normalized = resolve4(filePath);
1872
2095
  if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
1873
2096
  return true;
1874
2097
  try {
@@ -2388,12 +2611,12 @@ function createBackgroundBashManager(options = {}) {
2388
2611
  usedPorts.clear();
2389
2612
  knownContexts.clear();
2390
2613
  const deadline = now() + 3000;
2391
- await Promise.all(entries.map((instance) => new Promise((resolve4) => {
2614
+ await Promise.all(entries.map((instance) => new Promise((resolve5) => {
2392
2615
  if (instance.process.exitCode !== null || instance.process.killed)
2393
- return resolve4();
2394
- instance.process.once("exit", resolve4);
2395
- instance.process.once("error", resolve4);
2396
- const timer = setTimeout(resolve4, Math.max(0, deadline - now()));
2616
+ return resolve5();
2617
+ instance.process.once("exit", resolve5);
2618
+ instance.process.once("error", resolve5);
2619
+ const timer = setTimeout(resolve5, Math.max(0, deadline - now()));
2397
2620
  timer.unref?.();
2398
2621
  })));
2399
2622
  }
@@ -3006,7 +3229,7 @@ var init_mcp_config = __esm(() => {
3006
3229
 
3007
3230
  // ../../packages/core/src/agents/execution-host.ts
3008
3231
  import { AsyncLocalStorage } from "async_hooks";
3009
- import { dirname as dirname4 } from "path";
3232
+ import { dirname as dirname5 } from "path";
3010
3233
  function activeHost() {
3011
3234
  const scoped = scopedHost.getStore();
3012
3235
  if (scoped)
@@ -3041,7 +3264,7 @@ function hostedCodexAuthFilePath() {
3041
3264
  return activeHost().codexAuthFilePath();
3042
3265
  }
3043
3266
  function hostedCodexHomePath() {
3044
- return dirname4(hostedCodexAuthFilePath());
3267
+ return dirname5(hostedCodexAuthFilePath());
3045
3268
  }
3046
3269
  var defaultHost, hostRegistrations, scopedHost;
3047
3270
  var init_execution_host = __esm(async () => {
@@ -3065,14 +3288,14 @@ var init_execution_host = __esm(async () => {
3065
3288
  // ../../packages/core/src/agents/rollout/codex.ts
3066
3289
  import { randomBytes as randomBytes4 } from "crypto";
3067
3290
  import { existsSync as existsSync6, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync2, unlinkSync as unlinkSync5 } from "fs";
3068
- import { basename, dirname as dirname5, join as join7, resolve as resolve4 } from "path";
3291
+ import { basename, dirname as dirname6, join as join8, resolve as resolve5 } from "path";
3069
3292
  function codexSessionsDir() {
3070
- return join7(hostedCodexHomePath(), "sessions");
3293
+ return join8(hostedCodexHomePath(), "sessions");
3071
3294
  }
3072
3295
  function loadCodexShell() {
3073
3296
  if (_shellCache)
3074
3297
  return _shellCache;
3075
- const raw = readFileSync5(join7(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
3298
+ const raw = readFileSync5(join8(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
3076
3299
  const lines = parseJsonlText(raw);
3077
3300
  if (lines.length < 5) {
3078
3301
  throw new Error(`loadCodexShell: expected >=5 entries in codex-shell.jsonl, got ${lines.length}`);
@@ -3139,16 +3362,16 @@ function codexRolloutPath(threadId, fallback) {
3139
3362
  const hh = String(createdAt.getHours()).padStart(2, "0");
3140
3363
  const min = String(createdAt.getMinutes()).padStart(2, "0");
3141
3364
  const ss = String(createdAt.getSeconds()).padStart(2, "0");
3142
- const dir = join7(codexSessionsDir(), yyyy, mm, dd);
3143
- return join7(dir, `rollout-${yyyy}-${mm}-${dd}T${hh}-${min}-${ss}-${threadId}.jsonl`);
3365
+ const dir = join8(codexSessionsDir(), yyyy, mm, dd);
3366
+ return join8(dir, `rollout-${yyyy}-${mm}-${dd}T${hh}-${min}-${ss}-${threadId}.jsonl`);
3144
3367
  }
3145
3368
  function canonicalFilePath(path) {
3146
- const absolute = resolve4(path);
3369
+ const absolute = resolve5(path);
3147
3370
  try {
3148
3371
  return realpathSync2(absolute);
3149
3372
  } catch {
3150
3373
  try {
3151
- return join7(realpathSync2(dirname5(absolute)), basename(absolute));
3374
+ return join8(realpathSync2(dirname6(absolute)), basename(absolute));
3152
3375
  } catch {
3153
3376
  return absolute;
3154
3377
  }
@@ -3378,19 +3601,19 @@ function latestCodexRolloutPath(threadId) {
3378
3601
  try {
3379
3602
  if (buckets) {
3380
3603
  for (const bucket of buckets) {
3381
- const dir = join7(sessionsDir, bucket);
3604
+ const dir = join8(sessionsDir, bucket);
3382
3605
  if (!existsSync6(dir))
3383
3606
  continue;
3384
3607
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
3385
3608
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
3386
- candidates.push(join7(dir, rel));
3609
+ candidates.push(join8(dir, rel));
3387
3610
  }
3388
3611
  }
3389
3612
  }
3390
3613
  if (candidates.length === 0) {
3391
3614
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
3392
3615
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
3393
- candidates.push(join7(sessionsDir, rel));
3616
+ candidates.push(join8(sessionsDir, rel));
3394
3617
  }
3395
3618
  }
3396
3619
  return candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
@@ -3511,13 +3734,13 @@ function sweepPriorRolloutsForThread(threadId) {
3511
3734
  return;
3512
3735
  }
3513
3736
  for (const bucket of buckets) {
3514
- const dir = join7(sessionsDir, bucket);
3737
+ const dir = join8(sessionsDir, bucket);
3515
3738
  if (!existsSync6(dir))
3516
3739
  continue;
3517
3740
  try {
3518
3741
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
3519
3742
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
3520
- const fullPath = join7(dir, rel);
3743
+ const fullPath = join8(dir, rel);
3521
3744
  try {
3522
3745
  unlinkSync5(fullPath);
3523
3746
  } catch (e) {
@@ -3535,7 +3758,7 @@ function sweepPriorRolloutsFullTree(threadId, sessionsDir) {
3535
3758
  try {
3536
3759
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
3537
3760
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
3538
- const fullPath = join7(sessionsDir, rel);
3761
+ const fullPath = join8(sessionsDir, rel);
3539
3762
  try {
3540
3763
  unlinkSync5(fullPath);
3541
3764
  } catch (e) {
@@ -3601,7 +3824,7 @@ import { spawn as spawn3 } from "child_process";
3601
3824
  function createCodexAppServerForker(host) {
3602
3825
  return async (parentThreadId) => {
3603
3826
  const child = host.spawnServer();
3604
- return await new Promise((resolve5, reject) => {
3827
+ return await new Promise((resolve6, reject) => {
3605
3828
  let settled = false;
3606
3829
  let stdoutBuffer = "";
3607
3830
  let stderr = "";
@@ -3618,7 +3841,7 @@ function createCodexAppServerForker(host) {
3618
3841
  if (error)
3619
3842
  reject(error);
3620
3843
  else if (result)
3621
- resolve5(result);
3844
+ resolve6(result);
3622
3845
  else
3623
3846
  reject(new Error("Codex thread fork returned no result"));
3624
3847
  };
@@ -3715,7 +3938,7 @@ var init_codex_app_server = __esm(async () => {
3715
3938
 
3716
3939
  // ../../packages/core/src/agents/codex-registry.ts
3717
3940
  import { existsSync as existsSync7, unlinkSync as unlinkSync6 } from "fs";
3718
- import { join as join8 } from "path";
3941
+ import { join as join9 } from "path";
3719
3942
  var VALID_EFFORTS2, codexRegistry, codexRegistryOperations;
3720
3943
  var init_codex_registry = __esm(async () => {
3721
3944
  init_logger();
@@ -3760,7 +3983,7 @@ var init_codex_registry = __esm(async () => {
3760
3983
  async cleanupRollouts({ sessionIds }) {
3761
3984
  if (sessionIds.length === 0)
3762
3985
  return;
3763
- const sessionsDir = join8(hostedCodexHomePath(), "sessions");
3986
+ const sessionsDir = join9(hostedCodexHomePath(), "sessions");
3764
3987
  if (!existsSync7(sessionsDir))
3765
3988
  return;
3766
3989
  const failures = [];
@@ -3768,7 +3991,7 @@ var init_codex_registry = __esm(async () => {
3768
3991
  try {
3769
3992
  const glob = new Bun.Glob(`**/rollout-*-${tid}.jsonl`);
3770
3993
  for await (const rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
3771
- const path = join8(sessionsDir, rel);
3994
+ const path = join9(sessionsDir, rel);
3772
3995
  try {
3773
3996
  unlinkSync6(path);
3774
3997
  } catch (e) {
@@ -3792,17 +4015,17 @@ var init_codex_registry = __esm(async () => {
3792
4015
 
3793
4016
  // ../../packages/core/src/agents/maestro-registry.ts
3794
4017
  import { randomUUID as randomUUID3 } from "crypto";
3795
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
3796
- import { homedir as homedir5 } from "os";
3797
- import { join as join9, resolve as resolve5 } from "path";
4018
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4019
+ import { homedir as homedir6 } from "os";
4020
+ import { join as join10, resolve as resolve6 } from "path";
3798
4021
  function maestroSessionsDir() {
3799
- return join9(process.env.MAESTRO_DATA_DIR ? resolve5(process.env.MAESTRO_DATA_DIR) : join9(homedir5(), ".maestro"), "sessions");
4022
+ return join10(process.env.MAESTRO_DATA_DIR ? resolve6(process.env.MAESTRO_DATA_DIR) : join10(homedir6(), ".maestro"), "sessions");
3800
4023
  }
3801
4024
  function maestroSessionPath(sessionId) {
3802
- return join9(maestroSessionsDir(), `${sessionId}.jsonl`);
4025
+ return join10(maestroSessionsDir(), `${sessionId}.jsonl`);
3803
4026
  }
3804
4027
  function maestroActiveSessionPath(sessionId) {
3805
- return join9(maestroSessionsDir(), `${sessionId}.active.jsonl`);
4028
+ return join10(maestroSessionsDir(), `${sessionId}.active.jsonl`);
3806
4029
  }
3807
4030
  function existingCreatedAt(path) {
3808
4031
  if (!existsSync8(path))
@@ -3824,7 +4047,7 @@ function writeRollout(options) {
3824
4047
  assertUuidLike("sessionId", sessionId);
3825
4048
  ensureCwdExists(options.cwd);
3826
4049
  const path = maestroSessionPath(sessionId);
3827
- mkdirSync6(maestroSessionsDir(), { recursive: true });
4050
+ mkdirSync7(maestroSessionsDir(), { recursive: true });
3828
4051
  const messages = extractChatPairs(options.entries).flatMap((pair) => [
3829
4052
  { role: "user", content: nonEmptyTurnText(pair.userText) },
3830
4053
  { role: "assistant", content: nonEmptyTurnText(pair.assistantText) }
@@ -4026,200 +4249,6 @@ function sanitizeId(id) {
4026
4249
  return id.replace(/[^a-zA-Z0-9_-]/g, "_") || "_";
4027
4250
  }
4028
4251
 
4029
- // ../../packages/core/src/storage/storage-host.ts
4030
- import { mkdirSync as mkdirSync7 } from "fs";
4031
- import { homedir as homedir6 } from "os";
4032
- import { dirname as dirname6, join as join10, resolve as resolve6 } from "path";
4033
- function envPath(name, fallback) {
4034
- const value = process.env[name]?.trim();
4035
- return resolve6(value || fallback);
4036
- }
4037
- function defaultStateDir() {
4038
- return envPath("NEGOTIUM_STATE_DIR", join10(homedir6(), ".negotium"));
4039
- }
4040
- function defaultDataDir() {
4041
- return envPath("NEGOTIUM_DATA_DIR", join10(defaultStateDir(), "data"));
4042
- }
4043
- function defaultLogDir() {
4044
- return envPath("NEGOTIUM_LOG_DIR", join10(defaultStateDir(), "logs"));
4045
- }
4046
- function defaultWorkspaceDir() {
4047
- return envPath("NEGOTIUM_WORKSPACE_DIR", join10(defaultStateDir(), "workspace"));
4048
- }
4049
- function defaultSessionAsksDir() {
4050
- const runDir = envPath("NEGOTIUM_RUN_DIR", join10(defaultStateDir(), "runtime"));
4051
- return join10(runDir, "session-asks");
4052
- }
4053
- function defaultSessionsDatabasePath() {
4054
- return envPath("SESSIONS_DB_PATH", join10(resolveStorageDataDir(), "sessions.db"));
4055
- }
4056
- function isSqliteBusy(error) {
4057
- const message = error instanceof Error ? error.message : String(error);
4058
- return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
4059
- }
4060
- function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
4061
- const deadline = Date.now() + timeoutMs;
4062
- while (true) {
4063
- try {
4064
- database.exec(sql);
4065
- return;
4066
- } catch (error) {
4067
- if (!isSqliteBusy(error) || Date.now() >= deadline)
4068
- throw error;
4069
- Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
4070
- }
4071
- }
4072
- }
4073
- function initializeDatabase(database) {
4074
- database.exec("PRAGMA busy_timeout = 5000");
4075
- execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
4076
- database.exec("PRAGMA foreign_keys = ON");
4077
- database.exec("PRAGMA wal_autocheckpoint = 1000");
4078
- try {
4079
- database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
4080
- } catch {}
4081
- }
4082
- function defaultDatabase() {
4083
- const path = defaultSessionsDatabasePath();
4084
- if (fallbackDatabase && fallbackDatabasePath === path)
4085
- return fallbackDatabase;
4086
- if (fallbackDatabase)
4087
- fallbackDatabase.close();
4088
- mkdirSync7(dirname6(path), { recursive: true });
4089
- fallbackDatabase = new Database(path, { create: true });
4090
- fallbackDatabasePath = path;
4091
- initializeDatabase(fallbackDatabase);
4092
- return fallbackDatabase;
4093
- }
4094
- function resolveStorageDatabase() {
4095
- return configuredHost.database ?? defaultDatabase();
4096
- }
4097
- function resolveStorageDataDir() {
4098
- return configuredHost.dataDir ?? defaultDataDir();
4099
- }
4100
- function resolveStorageLogDir() {
4101
- return configuredHost.logDir ?? defaultLogDir();
4102
- }
4103
- function resolveStorageSessionAsksDir() {
4104
- return configuredHost.sessionAsksDir ?? defaultSessionAsksDir();
4105
- }
4106
- function resolveStorageWorkspaceDir() {
4107
- return configuredHost.workspaceDir ?? defaultWorkspaceDir();
4108
- }
4109
- function resolveStorageSharedWikiDir() {
4110
- return configuredHost.sharedWikiDir ?? join10(resolveStorageWorkspaceDir(), "wiki");
4111
- }
4112
- function resolveStorageUsersLogDir() {
4113
- return configuredHost.usersLogDir ?? join10(resolveStorageDataDir(), "users");
4114
- }
4115
- function normalizeStorageHostPatch(options) {
4116
- const patch = {};
4117
- if (options.database !== undefined) {
4118
- options.database.exec("PRAGMA foreign_keys = ON");
4119
- patch.database = options.database;
4120
- }
4121
- for (const key of STORAGE_PATH_KEYS) {
4122
- const value = options[key];
4123
- if (value === undefined)
4124
- continue;
4125
- if (!value.trim())
4126
- throw new TypeError(`${key} must not be empty`);
4127
- patch[key] = resolve6(value);
4128
- }
4129
- return Object.freeze(patch);
4130
- }
4131
- function refreshConfiguredHost() {
4132
- configuredHost = Object.freeze(Object.assign({}, ...storageHostFrames.filter((frame) => frame.active).map((frame) => frame.patch)));
4133
- }
4134
- function configureStorageHost(options) {
4135
- const frame = { active: true, patch: normalizeStorageHostPatch(options) };
4136
- storageHostFrames.push(frame);
4137
- refreshConfiguredHost();
4138
- return () => {
4139
- if (!frame.active)
4140
- return;
4141
- frame.active = false;
4142
- const index = storageHostFrames.indexOf(frame);
4143
- if (index >= 0)
4144
- storageHostFrames.splice(index, 1);
4145
- refreshConfiguredHost();
4146
- };
4147
- }
4148
- function resetStorageHost() {
4149
- for (const frame of storageHostFrames)
4150
- frame.active = false;
4151
- storageHostFrames.length = 0;
4152
- refreshConfiguredHost();
4153
- }
4154
- function closeStorageDatabase() {
4155
- if (!fallbackDatabase)
4156
- return;
4157
- fallbackDatabase.close();
4158
- fallbackDatabase = null;
4159
- fallbackDatabasePath = null;
4160
- }
4161
- function registerStorageSchemaInitializer(initialize, priority = 100) {
4162
- schemaInitializers.push({ initialize, priority });
4163
- schemaInitializers.sort((a, b) => a.priority - b.priority);
4164
- }
4165
- function ensureStorageSchemas(database = resolveStorageDatabase()) {
4166
- if (initializingDatabases.has(database))
4167
- return;
4168
- let initialized = initializedSchemas.get(database);
4169
- if (!initialized) {
4170
- initialized = new Set;
4171
- initializedSchemas.set(database, initialized);
4172
- }
4173
- initializingDatabases.add(database);
4174
- try {
4175
- for (const entry of schemaInitializers) {
4176
- if (initialized.has(entry.initialize))
4177
- continue;
4178
- initialized.add(entry.initialize);
4179
- try {
4180
- entry.initialize(database);
4181
- } catch (error) {
4182
- initialized.delete(entry.initialize);
4183
- throw error;
4184
- }
4185
- }
4186
- } finally {
4187
- initializingDatabases.delete(database);
4188
- }
4189
- }
4190
- var configuredHost, fallbackDatabase = null, fallbackDatabasePath = null, storageHostFrames, schemaInitializers, initializedSchemas, initializingDatabases, SQLITE_INIT_RETRY_MS = 25, SQLITE_INIT_TIMEOUT_MS = 5000, SQLITE_INIT_SLEEP, STORAGE_PATH_KEYS, internalStorageDatabase, storageDatabase;
4191
- var init_storage_host = __esm(async () => {
4192
- await init_sqlite();
4193
- configuredHost = {};
4194
- storageHostFrames = [];
4195
- schemaInitializers = [];
4196
- initializedSchemas = new WeakMap;
4197
- initializingDatabases = new WeakSet;
4198
- SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
4199
- STORAGE_PATH_KEYS = [
4200
- "dataDir",
4201
- "logDir",
4202
- "sessionAsksDir",
4203
- "workspaceDir",
4204
- "sharedWikiDir",
4205
- "usersLogDir"
4206
- ];
4207
- internalStorageDatabase = new Proxy({}, {
4208
- get(_target, property) {
4209
- const database = resolveStorageDatabase();
4210
- ensureStorageSchemas(database);
4211
- const value = Reflect.get(database, property, database);
4212
- return typeof value === "function" ? value.bind(database) : value;
4213
- },
4214
- set(_target, property, value) {
4215
- const database = resolveStorageDatabase();
4216
- ensureStorageSchemas(database);
4217
- return Reflect.set(database, property, value, database);
4218
- }
4219
- });
4220
- storageDatabase = internalStorageDatabase;
4221
- });
4222
-
4223
4252
  // ../../packages/core/src/storage/tasks.ts
4224
4253
  var exports_tasks = {};
4225
4254
  __export(exports_tasks, {
@@ -6745,7 +6774,10 @@ function listRecentRuntimeEventsForTopic(topicId, limit = 300) {
6745
6774
  }
6746
6775
  var RUNTIME_EVENT_TYPES, types;
6747
6776
  var init_runtime_events = __esm(async () => {
6748
- await init_forum_db();
6777
+ await __promiseAll([
6778
+ init_forum_db(),
6779
+ init_storage_host()
6780
+ ]);
6749
6781
  RUNTIME_EVENT_TYPES = [
6750
6782
  "message",
6751
6783
  "message-updated",
@@ -6754,26 +6786,28 @@ var init_runtime_events = __esm(async () => {
6754
6786
  "topic-updated",
6755
6787
  "topic-deleted"
6756
6788
  ];
6757
- db.exec(`
6758
- CREATE TABLE IF NOT EXISTS runtime_events (
6759
- seq INTEGER PRIMARY KEY AUTOINCREMENT,
6760
- source_id TEXT NOT NULL,
6761
- event_type TEXT NOT NULL CHECK (
6762
- event_type IN (
6763
- 'message',
6764
- 'message-updated',
6765
- 'ai-status',
6766
- 'topic-created',
6767
- 'topic-updated',
6768
- 'topic-deleted'
6769
- )
6770
- ),
6771
- topic_id TEXT NOT NULL,
6772
- payload_json TEXT NOT NULL,
6773
- created_at TEXT NOT NULL
6774
- )
6775
- `);
6776
- db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_events_topic_seq ON runtime_events(topic_id, seq)");
6789
+ registerStorageSchemaInitializer((database) => {
6790
+ database.exec(`
6791
+ CREATE TABLE IF NOT EXISTS runtime_events (
6792
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
6793
+ source_id TEXT NOT NULL,
6794
+ event_type TEXT NOT NULL CHECK (
6795
+ event_type IN (
6796
+ 'message',
6797
+ 'message-updated',
6798
+ 'ai-status',
6799
+ 'topic-created',
6800
+ 'topic-updated',
6801
+ 'topic-deleted'
6802
+ )
6803
+ ),
6804
+ topic_id TEXT NOT NULL,
6805
+ payload_json TEXT NOT NULL,
6806
+ created_at TEXT NOT NULL
6807
+ )
6808
+ `);
6809
+ database.exec("CREATE INDEX IF NOT EXISTS idx_runtime_events_topic_seq ON runtime_events(topic_id, seq)");
6810
+ });
6777
6811
  types = new Set(RUNTIME_EVENT_TYPES);
6778
6812
  });
6779
6813
 
@@ -6934,18 +6968,19 @@ class SqliteRuntimeBus {
6934
6968
  }
6935
6969
  }
6936
6970
  function runtimeBus() {
6971
+ if (!current)
6972
+ current = new SqliteRuntimeBus;
6937
6973
  return current;
6938
6974
  }
6939
6975
  function setRuntimeBus(bus) {
6940
6976
  current = bus;
6941
6977
  }
6942
- var current, WsHub;
6978
+ var current = null, WsHub;
6943
6979
  var init_bus = __esm(async () => {
6944
6980
  await init_runtime_events();
6945
- current = new SqliteRuntimeBus;
6946
6981
  WsHub = {
6947
6982
  get() {
6948
- return current;
6983
+ return runtimeBus();
6949
6984
  }
6950
6985
  };
6951
6986
  });
@@ -7479,6 +7514,7 @@ var exports_api_topics = {};
7479
7514
  __export(exports_api_topics, {
7480
7515
  upsertTopic: () => upsertTopic,
7481
7516
  setTopicSessionId: () => setTopicSessionId,
7517
+ setTopicAccessModes: () => setTopicAccessModes,
7482
7518
  setApiTopicAgent: () => setApiTopicAgent,
7483
7519
  revokeSubagentTellTarget: () => revokeSubagentTellTarget,
7484
7520
  reparentTopicChildren: () => reparentTopicChildren,
@@ -7945,6 +7981,16 @@ function upsertTopic(t) {
7945
7981
  }
7946
7982
  })();
7947
7983
  }
7984
+ function setTopicAccessModes(topicIds, accessMode) {
7985
+ if (topicIds.length === 0)
7986
+ return;
7987
+ const normalized = normalizeTopicAccessMode(accessMode);
7988
+ db.transaction(() => {
7989
+ const update = db.query("UPDATE api_topics SET access_mode = ? WHERE id = ?");
7990
+ for (const topicId of topicIds)
7991
+ update.run(normalized, topicId);
7992
+ })();
7993
+ }
7948
7994
  function listTopics() {
7949
7995
  const rows = db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
7950
7996
  const participants = getAllTopicParticipants();
@@ -9525,32 +9571,35 @@ var init_self_schedules = __esm(async () => {
9525
9571
  await __promiseAll([
9526
9572
  init_forum_db(),
9527
9573
  init_runtime_leases(),
9528
- init_runtime_topic_state()
9574
+ init_runtime_topic_state(),
9575
+ init_storage_host()
9529
9576
  ]);
9530
- db.exec(`
9531
- CREATE TABLE IF NOT EXISTS runtime_self_schedules (
9532
- id TEXT PRIMARY KEY,
9533
- topic_id TEXT NOT NULL,
9534
- user_id TEXT NOT NULL,
9535
- message TEXT NOT NULL,
9536
- deliver_at INTEGER NOT NULL,
9537
- created_at INTEGER NOT NULL,
9538
- updated_at INTEGER NOT NULL,
9539
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running')),
9540
- claimed_by TEXT,
9541
- claimed_at INTEGER,
9542
- running_query_id TEXT
9543
- )
9544
- `);
9545
- db.exec(`
9546
- CREATE UNIQUE INDEX IF NOT EXISTS idx_runtime_self_schedules_one_pending
9547
- ON runtime_self_schedules(topic_id)
9548
- WHERE status = 'pending'
9549
- `);
9550
- db.exec(`
9551
- CREATE INDEX IF NOT EXISTS idx_runtime_self_schedules_due
9552
- ON runtime_self_schedules(status, deliver_at)
9553
- `);
9577
+ registerStorageSchemaInitializer((database) => {
9578
+ database.exec(`
9579
+ CREATE TABLE IF NOT EXISTS runtime_self_schedules (
9580
+ id TEXT PRIMARY KEY,
9581
+ topic_id TEXT NOT NULL,
9582
+ user_id TEXT NOT NULL,
9583
+ message TEXT NOT NULL,
9584
+ deliver_at INTEGER NOT NULL,
9585
+ created_at INTEGER NOT NULL,
9586
+ updated_at INTEGER NOT NULL,
9587
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running')),
9588
+ claimed_by TEXT,
9589
+ claimed_at INTEGER,
9590
+ running_query_id TEXT
9591
+ )
9592
+ `);
9593
+ database.exec(`
9594
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_runtime_self_schedules_one_pending
9595
+ ON runtime_self_schedules(topic_id)
9596
+ WHERE status = 'pending'
9597
+ `);
9598
+ database.exec(`
9599
+ CREATE INDEX IF NOT EXISTS idx_runtime_self_schedules_due
9600
+ ON runtime_self_schedules(status, deliver_at)
9601
+ `);
9602
+ });
9554
9603
  });
9555
9604
 
9556
9605
  // ../../packages/core/src/topics/links.ts
@@ -10240,15 +10289,20 @@ function deleteBrowserProfile(ownerId, rawName) {
10240
10289
  }
10241
10290
  var DEFAULT_BROWSER_PROFILE = "default";
10242
10291
  var init_browser_profiles = __esm(async () => {
10243
- await init_forum_db();
10244
- db.exec(`
10245
- CREATE TABLE IF NOT EXISTS browser_profiles (
10246
- owner_id TEXT NOT NULL,
10247
- name TEXT NOT NULL,
10248
- created_at TEXT NOT NULL,
10249
- PRIMARY KEY (owner_id, name)
10250
- )
10251
- `);
10292
+ await __promiseAll([
10293
+ init_forum_db(),
10294
+ init_storage_host()
10295
+ ]);
10296
+ registerStorageSchemaInitializer((database) => {
10297
+ database.exec(`
10298
+ CREATE TABLE IF NOT EXISTS browser_profiles (
10299
+ owner_id TEXT NOT NULL,
10300
+ name TEXT NOT NULL,
10301
+ created_at TEXT NOT NULL,
10302
+ PRIMARY KEY (owner_id, name)
10303
+ )
10304
+ `);
10305
+ });
10252
10306
  });
10253
10307
 
10254
10308
  // ../../packages/core/src/platform/playwright/manager.ts
@@ -11977,9 +12031,9 @@ function formatArchiverBytes(bytes) {
11977
12031
  function formatArchiverTool(name, input) {
11978
12032
  const params = Object.entries(input).map(([key, value]) => {
11979
12033
  const rendered = typeof value === "string" ? value : JSON.stringify(value);
11980
- return `${key}: ${sessionText(rendered ?? "")}`;
12034
+ return `${key}: ${summarizeDisplayText(sessionText(rendered ?? ""))}`;
11981
12035
  }).join(", ");
11982
- return `${name}${params ? `(${params})` : ""}`;
12036
+ return summarizeDisplayText(`${name}${params ? `(${params})` : ""}`);
11983
12037
  }
11984
12038
  function createArchiverRuntime(host) {
11985
12039
  const activeSessions = new Map;
@@ -12289,6 +12343,7 @@ function listActiveMemoryArchiverSessions(userId) {
12289
12343
  }
12290
12344
  var MAX_BRIEF_ENTRIES = 8, defaultArchiverRuntime;
12291
12345
  var init_archiver = __esm(async () => {
12346
+ init_tool_format();
12292
12347
  init_config();
12293
12348
  init_logger();
12294
12349
  init_builders();
@@ -13096,7 +13151,7 @@ function detailedToolStep(payload) {
13096
13151
  const label = compactToolLabel(String(payload.label ?? name));
13097
13152
  if (shortName === "ask_session" || shortName === "tell_session") {
13098
13153
  const target = typeof input.to === "string" && input.to.trim() ? input.to.trim() : "session";
13099
- const msg = typeof input.message === "string" ? text(input.message) : "";
13154
+ const msg = typeof input.message === "string" ? summarizeDisplayText(text(input.message)) : "";
13100
13155
  const action = shortName === "ask_session" ? "Ask" : "Tell";
13101
13156
  return `${action} ${target}${msg ? ` \u2014 ${msg}` : ""}`;
13102
13157
  }
@@ -13122,22 +13177,22 @@ function detailedToolStep(payload) {
13122
13177
  return `${action} file`;
13123
13178
  }
13124
13179
  if (shortName === "grep" || shortName === "glob") {
13125
- const pattern = typeof input.pattern === "string" ? input.pattern : "";
13180
+ const pattern = typeof input.pattern === "string" ? summarizeDisplayText(text(input.pattern)) : "";
13126
13181
  const action = shortName === "grep" ? "Grep" : "Glob";
13127
13182
  if (pattern)
13128
- return `${action} '${text(pattern)}'${path ? ` in ${path}` : ""}`;
13183
+ return `${action} '${pattern}'${path ? ` in ${path}` : ""}`;
13129
13184
  if (path)
13130
13185
  return `${action} ${path}`;
13131
13186
  }
13132
13187
  if (shortName === "bash") {
13133
- const cmd = typeof input.command === "string" ? text(input.command) : "";
13188
+ const cmd = typeof input.command === "string" ? summarizeDisplayText(text(input.command)) : "";
13134
13189
  return cmd ? `Bash: ${cmd}` : "Bash";
13135
13190
  }
13136
13191
  if (shortName === "webfetch" || shortName === "url_fetch") {
13137
- const url = typeof input.url === "string" ? text(input.url) : "";
13192
+ const url = typeof input.url === "string" ? summarizeDisplayText(text(input.url)) : "";
13138
13193
  return url ? `Fetch ${url}` : "WebFetch";
13139
13194
  }
13140
- return label;
13195
+ return summarizeDisplayText(label);
13141
13196
  }
13142
13197
  function backgroundSessionProgress(topicId, queryId) {
13143
13198
  let status = "Running";
@@ -13165,7 +13220,7 @@ function backgroundSessionProgress(topicId, queryId) {
13165
13220
  step = text(payload.content);
13166
13221
  else if (kind === "tool_output") {
13167
13222
  const toolName = toolNames.get(text(payload.toolUseId)) ?? "Tool";
13168
- step = payload.isError ? `${toolName} failed: ${text(payload.content) || "unknown error"}` : `${toolName} result \xB7 ${byteSize(payload.content)}`;
13223
+ step = payload.isError ? `${toolName} failed: ${summarizeDisplayText(text(payload.content)) || "unknown error"}` : `${toolName} result \xB7 ${byteSize(payload.content)}`;
13169
13224
  } else if (kind === "ai_done") {
13170
13225
  const usage = payload.usage && typeof payload.usage === "object" ? payload.usage : undefined;
13171
13226
  const input = typeof usage?.input === "number" ? usage.input.toLocaleString() : "";
@@ -13230,6 +13285,7 @@ function listBackgroundSessionsForUser(userId) {
13230
13285
  }
13231
13286
  var providers, transientSessions;
13232
13287
  var init_background_sessions = __esm(async () => {
13288
+ init_tool_format();
13233
13289
  init_background_session_policy();
13234
13290
  await __promiseAll([
13235
13291
  init_archiver(),
@@ -13535,10 +13591,11 @@ var init_runtime_turn_requests = __esm(async () => {
13535
13591
  await __promiseAll([
13536
13592
  init_forum_db(),
13537
13593
  init_runtime_leases(),
13538
- init_runtime_topic_state()
13594
+ init_runtime_topic_state(),
13595
+ init_storage_host()
13539
13596
  ]);
13540
13597
  REQUEST_CLAIM_STALE_MS = TURN_LEASE_STALE_MS;
13541
- ensureRuntimeUserTurnRequestsSchema(db);
13598
+ registerStorageSchemaInitializer((database) => ensureRuntimeUserTurnRequestsSchema(database));
13542
13599
  });
13543
13600
 
13544
13601
  // ../../packages/core/src/topics/session.ts
@@ -15800,19 +15857,22 @@ var pendingAsks, MAX_ASK_AGE_MS;
15800
15857
  var init_ask_callbacks = __esm(async () => {
15801
15858
  await __promiseAll([
15802
15859
  init_forum_db(),
15803
- init_session_asks()
15860
+ init_session_asks(),
15861
+ init_storage_host()
15804
15862
  ]);
15805
- db.exec(`
15806
- CREATE TABLE IF NOT EXISTS remote_ask_callbacks (
15807
- target_query_id TEXT PRIMARY KEY,
15808
- request_id TEXT NOT NULL,
15809
- node_name TEXT NOT NULL,
15810
- node_cell_id TEXT NOT NULL,
15811
- topic_id TEXT NOT NULL,
15812
- user_id TEXT NOT NULL,
15813
- created_at INTEGER NOT NULL
15814
- )
15815
- `);
15863
+ registerStorageSchemaInitializer((database) => {
15864
+ database.exec(`
15865
+ CREATE TABLE IF NOT EXISTS remote_ask_callbacks (
15866
+ target_query_id TEXT PRIMARY KEY,
15867
+ request_id TEXT NOT NULL,
15868
+ node_name TEXT NOT NULL,
15869
+ node_cell_id TEXT NOT NULL,
15870
+ topic_id TEXT NOT NULL,
15871
+ user_id TEXT NOT NULL,
15872
+ created_at INTEGER NOT NULL
15873
+ )
15874
+ `);
15875
+ });
15816
15876
  pendingAsks = new Map;
15817
15877
  MAX_ASK_AGE_MS = PENDING_ASK_TTL_MS;
15818
15878
  });
@@ -16034,30 +16094,35 @@ paint();document.body.appendChild(b)}
16034
16094
  if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",init);else init()})();
16035
16095
  </script>`;
16036
16096
  var init_visual_store = __esm(async () => {
16037
- await init_forum_db();
16038
- db.exec(`
16039
- CREATE TABLE IF NOT EXISTS api_topic_visuals (
16040
- id INTEGER PRIMARY KEY AUTOINCREMENT,
16041
- topic_id TEXT NOT NULL,
16042
- html TEXT NOT NULL,
16043
- title TEXT,
16044
- created_at INTEGER NOT NULL,
16045
- kind TEXT NOT NULL DEFAULT 'html',
16046
- source TEXT,
16047
- file_id TEXT,
16048
- mime_type TEXT,
16049
- media_token TEXT
16050
- );
16051
- CREATE INDEX IF NOT EXISTS idx_api_topic_visuals_topic
16052
- ON api_topic_visuals(topic_id, created_at DESC);
16053
-
16054
- CREATE TABLE IF NOT EXISTS api_topic_visual_views (
16055
- topic_id TEXT NOT NULL,
16056
- user_id TEXT NOT NULL,
16057
- visual_id INTEGER NOT NULL,
16058
- PRIMARY KEY (topic_id, user_id)
16059
- );
16060
- `);
16097
+ await __promiseAll([
16098
+ init_forum_db(),
16099
+ init_storage_host()
16100
+ ]);
16101
+ registerStorageSchemaInitializer((database) => {
16102
+ database.exec(`
16103
+ CREATE TABLE IF NOT EXISTS api_topic_visuals (
16104
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
16105
+ topic_id TEXT NOT NULL,
16106
+ html TEXT NOT NULL,
16107
+ title TEXT,
16108
+ created_at INTEGER NOT NULL,
16109
+ kind TEXT NOT NULL DEFAULT 'html',
16110
+ source TEXT,
16111
+ file_id TEXT,
16112
+ mime_type TEXT,
16113
+ media_token TEXT
16114
+ );
16115
+ CREATE INDEX IF NOT EXISTS idx_api_topic_visuals_topic
16116
+ ON api_topic_visuals(topic_id, created_at DESC);
16117
+
16118
+ CREATE TABLE IF NOT EXISTS api_topic_visual_views (
16119
+ topic_id TEXT NOT NULL,
16120
+ user_id TEXT NOT NULL,
16121
+ visual_id INTEGER NOT NULL,
16122
+ PRIMARY KEY (topic_id, user_id)
16123
+ );
16124
+ `);
16125
+ });
16061
16126
  for (const statement of [
16062
16127
  "ALTER TABLE api_topic_visuals ADD COLUMN kind TEXT NOT NULL DEFAULT 'html'",
16063
16128
  "ALTER TABLE api_topic_visuals ADD COLUMN source TEXT",
@@ -20168,20 +20233,26 @@ function recordRuntimeGatewaySubmission(submission) {
20168
20233
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(submission.clientMessageId, submission.requestId, submission.topicId, submission.messageId, submission.userId, submission.createdAt, submission.ackCursor, submission.messageCursor, submission.payloadHash ?? null);
20169
20234
  }
20170
20235
  var init_runtime_gateway_submissions = __esm(async () => {
20171
- await init_forum_db();
20172
- db.exec(`
20173
- CREATE TABLE IF NOT EXISTS runtime_gateway_submissions (
20174
- client_message_id TEXT PRIMARY KEY,
20175
- request_id TEXT NOT NULL UNIQUE,
20176
- topic_id TEXT NOT NULL,
20177
- message_id TEXT NOT NULL,
20178
- user_id TEXT NOT NULL,
20179
- created_at TEXT NOT NULL,
20180
- ack_cursor INTEGER NOT NULL DEFAULT 0,
20181
- message_cursor INTEGER NOT NULL DEFAULT 0,
20182
- payload_hash TEXT
20183
- )
20184
- `);
20236
+ await __promiseAll([
20237
+ init_forum_db(),
20238
+ init_storage_host()
20239
+ ]);
20240
+ registerStorageSchemaInitializer((database) => {
20241
+ database.exec(`
20242
+ CREATE TABLE IF NOT EXISTS runtime_gateway_submissions (
20243
+ client_message_id TEXT PRIMARY KEY,
20244
+ request_id TEXT NOT NULL UNIQUE,
20245
+ topic_id TEXT NOT NULL,
20246
+ message_id TEXT NOT NULL,
20247
+ user_id TEXT NOT NULL,
20248
+ created_at TEXT NOT NULL,
20249
+ ack_cursor INTEGER NOT NULL DEFAULT 0,
20250
+ message_cursor INTEGER NOT NULL DEFAULT 0,
20251
+ payload_hash TEXT
20252
+ )
20253
+ `);
20254
+ database.exec("CREATE INDEX IF NOT EXISTS idx_runtime_gateway_submissions_topic ON runtime_gateway_submissions(topic_id)");
20255
+ });
20185
20256
  try {
20186
20257
  db.exec("ALTER TABLE runtime_gateway_submissions ADD COLUMN ack_cursor INTEGER NOT NULL DEFAULT 0");
20187
20258
  } catch {}
@@ -20191,7 +20262,6 @@ var init_runtime_gateway_submissions = __esm(async () => {
20191
20262
  try {
20192
20263
  db.exec("ALTER TABLE runtime_gateway_submissions ADD COLUMN message_cursor INTEGER NOT NULL DEFAULT 0");
20193
20264
  } catch {}
20194
- db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_gateway_submissions_topic ON runtime_gateway_submissions(topic_id)");
20195
20265
  });
20196
20266
 
20197
20267
  // ../../packages/core/src/application/submit-runtime-gateway-turn.ts
@@ -20364,6 +20434,33 @@ var init_submit_user_message = __esm(async () => {
20364
20434
  });
20365
20435
 
20366
20436
  // ../../packages/core/src/application/switch-topic-access-mode.ts
20437
+ function collectSubagentDescendants(rootTopicId) {
20438
+ const all = listTopics();
20439
+ const childrenByParent = new Map;
20440
+ for (const topic of all) {
20441
+ if (!topic.isSubagent || !topic.parentTopicId)
20442
+ continue;
20443
+ const siblings = childrenByParent.get(topic.parentTopicId);
20444
+ if (siblings)
20445
+ siblings.push(topic);
20446
+ else
20447
+ childrenByParent.set(topic.parentTopicId, [topic]);
20448
+ }
20449
+ const descendants = [];
20450
+ const seen = new Set([rootTopicId]);
20451
+ const queue = [rootTopicId];
20452
+ while (queue.length > 0) {
20453
+ const parentId = queue.shift();
20454
+ for (const child of childrenByParent.get(parentId) ?? []) {
20455
+ if (seen.has(child.id))
20456
+ continue;
20457
+ seen.add(child.id);
20458
+ descendants.push(child);
20459
+ queue.push(child.id);
20460
+ }
20461
+ }
20462
+ return descendants;
20463
+ }
20367
20464
  function switchTopicAccessMode(params) {
20368
20465
  const topic = getTopic(params.topicId);
20369
20466
  if (!topic)
@@ -20371,13 +20468,32 @@ function switchTopicAccessMode(params) {
20371
20468
  const owner = topic.participants.some((participant) => participant.userId === params.userId && participant.role === "owner");
20372
20469
  if (!owner)
20373
20470
  return { ok: false, error: "Only topic owners can change privacy" };
20374
- topic.accessMode = params.accessMode;
20375
- upsertTopic(topic);
20376
- WsHub.get().broadcastTopicUpdated(topic.id);
20471
+ if (topic.isSubagent) {
20472
+ return {
20473
+ ok: false,
20474
+ error: "Subagent rooms inherit privacy from their parent topic"
20475
+ };
20476
+ }
20477
+ const descendants = collectSubagentDescendants(topic.id);
20478
+ const changed = [topic, ...descendants].filter((candidate) => (candidate.accessMode ?? "private") !== params.accessMode);
20479
+ if (changed.length === 0) {
20480
+ return {
20481
+ ok: true,
20482
+ accessMode: params.accessMode,
20483
+ text: params.accessMode === "shared" ? `"${topic.title}" is already public to the connected Otium Hub.` : `"${topic.title}" is already private to this worker.`,
20484
+ topicIds: []
20485
+ };
20486
+ }
20487
+ setTopicAccessModes(changed.map((candidate) => candidate.id), params.accessMode);
20488
+ for (const candidate of changed)
20489
+ WsHub.get().broadcastTopicUpdated(candidate.id);
20490
+ const subagentCount = changed.filter((candidate) => candidate.id !== topic.id).length;
20491
+ const suffix = subagentCount > 0 ? ` (${subagentCount} subagent room${subagentCount === 1 ? "" : "s"} updated)` : "";
20377
20492
  return {
20378
20493
  ok: true,
20379
20494
  accessMode: params.accessMode,
20380
- text: params.accessMode === "shared" ? `"${topic.title}" is public to the connected Otium Hub.` : `"${topic.title}" is private to this worker.`
20495
+ topicIds: changed.map((candidate) => candidate.id),
20496
+ text: (params.accessMode === "shared" ? `"${topic.title}" is public to the connected Otium Hub.` : `"${topic.title}" is private to this worker.`) + suffix
20381
20497
  };
20382
20498
  }
20383
20499
  var init_switch_topic_access_mode = __esm(async () => {
@@ -27509,6 +27625,7 @@ __export(exports_storage_public, {
27509
27625
  setTopicAgentAndSession: () => setTopicAgentAndSession,
27510
27626
  setTopicAgentAndClearSession: () => setTopicAgentAndClearSession,
27511
27627
  setTopicAgent: () => setTopicAgent,
27628
+ setTopicAccessModes: () => setTopicAccessModes,
27512
27629
  setSessionForTopic: () => setSessionForTopic,
27513
27630
  setLastShownConfig: () => setLastShownConfig,
27514
27631
  setGlobalAiName: () => setGlobalAiName,
@@ -32474,7 +32591,7 @@ function setTopics(state, topics, preferredTitle) {
32474
32591
  activeTopicId: nextActive,
32475
32592
  scrollOffset: nextActive === state.activeTopicId ? state.scrollOffset : 0,
32476
32593
  askChoiceIndex: nextActive === state.activeTopicId ? state.askChoiceIndex : 0,
32477
- topicPickerIndex: state.overlay === "topics" ? pickedTopicIndex : Math.max(0, orderedTopics.findIndex((topic) => topic.id === nextActive)),
32594
+ topicPickerIndex: state.overlay === "topics" || state.overlay === "confirm-share" ? pickedTopicIndex : Math.max(0, orderedTopics.findIndex((topic) => topic.id === nextActive)),
32478
32595
  topicPickerBackgroundId: state.topicPickerBackgroundId
32479
32596
  });
32480
32597
  }
@@ -34136,14 +34253,16 @@ function subagentTreePrefix(topic, topics, rootTopicId) {
34136
34253
  function topicPickerHints(topicPickerRoot) {
34137
34254
  const exit = topicPickerRoot ? "Esc/Ctrl-C exit" : "Esc close \xB7 Ctrl-C exit; work continues";
34138
34255
  const shortExit = topicPickerRoot ? "Esc/Ctrl-C exit" : "Esc close";
34139
- return [
34256
+ const candidates = [
34257
+ `\u2191\u2193 select \xB7 Enter open \xB7 type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete \xB7 Ctrl-P public/private \xB7 ${exit}`,
34258
+ `\u2191\u2193 select \xB7 Enter open \xB7 type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete \xB7 Ctrl-P public/private \xB7 ${shortExit}`,
34140
34259
  `\u2191\u2193 select \xB7 Enter open \xB7 type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete \xB7 ${exit}`,
34141
- `\u2191\u2193 select \xB7 Enter open \xB7 type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete \xB7 ${shortExit}`,
34142
- `\u2191\u2193 select \xB7 Enter open \xB7 type to filter \xB7 Ctrl-N/D \xB7 ${shortExit}`,
34143
- "\u2191\u2193 \xB7 Enter \xB7 type to filter \xB7 Ctrl-N/D",
34144
- "type to filter \xB7 Ctrl-N/D",
34145
- "Ctrl-N/D"
34260
+ `\u2191\u2193 select \xB7 Enter open \xB7 type to filter \xB7 Ctrl-N/D/P \xB7 ${shortExit}`,
34261
+ "\u2191\u2193 \xB7 Enter \xB7 type to filter \xB7 Ctrl-N/D/P",
34262
+ "type to filter \xB7 Ctrl-N/D/P",
34263
+ "Ctrl-N/D/P"
34146
34264
  ];
34265
+ return [...new Set(candidates)].sort((a, b) => displayWidth(b) - displayWidth(a));
34147
34266
  }
34148
34267
  function topicOverlayCursor(state, width, bodyHeight) {
34149
34268
  if (state.overlay !== "topics")
@@ -34499,6 +34618,34 @@ function conversationLines(state, width, height, animationFrame = 0, nowMs = ter
34499
34618
  line(" Press y to delete or n to cancel.", { fg: theme.amber })
34500
34619
  ].slice(0, height);
34501
34620
  }
34621
+ if (state.overlay === "confirm-share") {
34622
+ const topic = state.topics.find((candidate) => candidate.id === state.pendingShareTopicId);
34623
+ const subagents = (() => {
34624
+ if (!topic)
34625
+ return 0;
34626
+ const reached = new Set([topic.id]);
34627
+ for (;; ) {
34628
+ const next = state.topics.filter((candidate) => candidate.isSubagent && !reached.has(candidate.id) && candidate.parentTopicId !== undefined && reached.has(candidate.parentTopicId));
34629
+ if (next.length === 0)
34630
+ break;
34631
+ for (const candidate of next)
34632
+ reached.add(candidate.id);
34633
+ }
34634
+ return reached.size - 1;
34635
+ })();
34636
+ const scope = subagents > 0 ? ` Its ${subagents} subagent room${subagents === 1 ? "" : "s"} become public too.` : " Subagent rooms spawned from it will be public too.";
34637
+ return [
34638
+ line(""),
34639
+ line(` Make \u201C${topic?.title ?? "this topic"}\u201D public?`, {
34640
+ fg: theme.amber,
34641
+ bold: true
34642
+ }),
34643
+ line(""),
34644
+ line(" The transcript becomes visible to the connected Otium Hub."),
34645
+ line(scope),
34646
+ line(" Press y to publish or n to cancel.", { fg: theme.amber })
34647
+ ].slice(0, height);
34648
+ }
34502
34649
  const all = conversationContentLines(state, width, animationFrame, nowMs, includeTasks);
34503
34650
  const { contentHeight, maxOffset, offset } = conversationViewport(all.length, height, state.scrollOffset);
34504
34651
  const end = all.length - offset;
@@ -34766,6 +34913,9 @@ function footerHintText(state) {
34766
34913
  }
34767
34914
  return state.topicPickerRoot ? ["Esc/Ctrl-C exit ", "Esc exit "] : ["Esc close \xB7 Ctrl-C exit; work continues ", "Esc close \xB7 Ctrl-C exit ", "Esc "];
34768
34915
  }
34916
+ if (state.overlay === "confirm-share") {
34917
+ return ["Y make public \xB7 N cancel ", "Y/N "];
34918
+ }
34769
34919
  if (state.overlay === "background-session") {
34770
34920
  return [
34771
34921
  "wheel/PgUp/PgDn scroll \xB7 Esc back \xB7 read-only ",
@@ -37722,6 +37872,16 @@ class TerminalApp {
37722
37872
  }
37723
37873
  return;
37724
37874
  }
37875
+ if (this.#state.overlay === "confirm-share") {
37876
+ const key = chunk.toLowerCase();
37877
+ if (CONFIRM_KEYS.has(key))
37878
+ this.#confirmTopicShare();
37879
+ else if (CANCEL_KEYS.has(key) || chunk === "\x1B") {
37880
+ this.#state = { ...this.#state, overlay: "topics", pendingShareTopicId: undefined };
37881
+ this.#queueRender();
37882
+ }
37883
+ return;
37884
+ }
37725
37885
  if (this.#state.overlay === "background-session") {
37726
37886
  if (chunk === "\x1B") {
37727
37887
  this.#state = { ...this.#state, overlay: "topics" };
@@ -38030,6 +38190,22 @@ class TerminalApp {
38030
38190
  this.#queueRender();
38031
38191
  return;
38032
38192
  }
38193
+ if (chunk === TOGGLE_ACCESS_MODE_KEY) {
38194
+ const topic = pickedTopic(this.#state);
38195
+ if (topic) {
38196
+ this.#requestAccessModeToggle(topic);
38197
+ return;
38198
+ }
38199
+ if (!this.#state.topicPickerBackgroundId)
38200
+ return;
38201
+ this.#state = {
38202
+ ...this.#state,
38203
+ notice: "Background sessions are read-only",
38204
+ noticeLevel: "warn"
38205
+ };
38206
+ this.#queueRender();
38207
+ return;
38208
+ }
38033
38209
  if (FILTER_BACKSPACE_KEYS.has(chunk)) {
38034
38210
  if (this.#state.topicFilter.length === 0)
38035
38211
  return;
@@ -38472,6 +38648,65 @@ class TerminalApp {
38472
38648
  };
38473
38649
  this.#queueRender();
38474
38650
  }
38651
+ async#requestAccessModeToggle(topic) {
38652
+ if (topic.kind !== "agent") {
38653
+ this.#state = {
38654
+ ...this.#state,
38655
+ notice: `${topic.kind === "manager" ? "Manager" : "Channel"} topics cannot be published`,
38656
+ noticeLevel: "warn"
38657
+ };
38658
+ this.#queueRender();
38659
+ return;
38660
+ }
38661
+ if (topic.isSubagent) {
38662
+ this.#state = {
38663
+ ...this.#state,
38664
+ notice: "Subagent rooms inherit privacy from their parent topic",
38665
+ noticeLevel: "warn"
38666
+ };
38667
+ this.#queueRender();
38668
+ return;
38669
+ }
38670
+ if (topic.accessMode === "shared") {
38671
+ await this.#applyAccessMode(topic, "private");
38672
+ return;
38673
+ }
38674
+ this.#state = {
38675
+ ...this.#state,
38676
+ overlay: "confirm-share",
38677
+ pendingShareTopicId: topic.id
38678
+ };
38679
+ this.#queueRender();
38680
+ }
38681
+ async#confirmTopicShare() {
38682
+ const topic = this.#state.topics.find((candidate) => candidate.id === this.#state.pendingShareTopicId);
38683
+ this.#state = { ...this.#state, overlay: "topics", pendingShareTopicId: undefined };
38684
+ if (!topic) {
38685
+ this.#state = {
38686
+ ...this.#state,
38687
+ notice: "That topic no longer exists",
38688
+ noticeLevel: "warn"
38689
+ };
38690
+ this.#queueRender();
38691
+ return;
38692
+ }
38693
+ await this.#applyAccessMode(topic, "shared");
38694
+ }
38695
+ async#applyAccessMode(topic, accessMode) {
38696
+ this.#state = { ...this.#state, overlay: "topics" };
38697
+ try {
38698
+ const notice = await this.#client.setAccessMode(topic, accessMode);
38699
+ await this.#refreshTopics();
38700
+ this.#state = { ...this.#state, notice, noticeLevel: "success" };
38701
+ } catch (error2) {
38702
+ this.#state = {
38703
+ ...this.#state,
38704
+ notice: error2 instanceof Error ? error2.message : String(error2),
38705
+ noticeLevel: "error"
38706
+ };
38707
+ }
38708
+ this.#queueRender();
38709
+ }
38475
38710
  #requestTopicDelete(topic) {
38476
38711
  if (!topic)
38477
38712
  return;
@@ -38750,6 +38985,7 @@ class TerminalApp {
38750
38985
  ...this.#state,
38751
38986
  overlay: null,
38752
38987
  pendingDeleteTopicId: undefined,
38988
+ pendingShareTopicId: undefined,
38753
38989
  creatingTopic: false,
38754
38990
  notice: "Input cleared",
38755
38991
  noticeLevel: "info"
@@ -38812,7 +39048,7 @@ class TerminalApp {
38812
39048
  await this.#client.stop();
38813
39049
  }
38814
39050
  }
38815
- var KITTY_KEYBOARD_PUSH = "\x1B[>1u", KITTY_KEYBOARD_POP = "\x1B[<u", CANVAS_RGB, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, ABORT_ENTER_ALT_SCREEN, NEW_TOPIC_KEY = "\x0E", DELETE_TOPIC_KEY = "\x04", FILTER_BACKSPACE_KEYS, CONFIRM_KEYS, CANCEL_KEYS, INPUT_CARRY_FLUSH_MS = 15;
39051
+ var KITTY_KEYBOARD_PUSH = "\x1B[>1u", KITTY_KEYBOARD_POP = "\x1B[<u", CANVAS_RGB, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, ABORT_ENTER_ALT_SCREEN, NEW_TOPIC_KEY = "\x0E", DELETE_TOPIC_KEY = "\x04", TOGGLE_ACCESS_MODE_KEY = "\x10", FILTER_BACKSPACE_KEYS, CONFIRM_KEYS, CANCEL_KEYS, INPUT_CARRY_FLUSH_MS = 15;
38816
39052
  var init_app = __esm(async () => {
38817
39053
  init_color_depth();
38818
39054
  init_commands();
@@ -44460,8 +44696,13 @@ function shareOtiumTopic(options) {
44460
44696
  if (!owned.ok)
44461
44697
  return owned;
44462
44698
  if (!isTopicShared(owned.topic)) {
44463
- upsertTopic({ ...owned.topic, accessMode: "shared" });
44464
- WsHub.get().broadcastTopicUpdated(owned.topic.id);
44699
+ const switched = switchTopicAccessMode({
44700
+ topicId: options.localTopicId,
44701
+ userId: options.userId,
44702
+ accessMode: "shared"
44703
+ });
44704
+ if (!switched.ok)
44705
+ return { ok: false, error: switched.error, status: 409 };
44465
44706
  }
44466
44707
  return bindOtiumTopic(options);
44467
44708
  }
@@ -44471,8 +44712,13 @@ function setOtiumTopicPrivate(options) {
44471
44712
  return owned;
44472
44713
  const removedBindings = unbindSharedPeerSessionsForLocalTopic(options.localTopicId);
44473
44714
  if (isTopicShared(owned.topic)) {
44474
- upsertTopic({ ...owned.topic, accessMode: "private" });
44475
- WsHub.get().broadcastTopicUpdated(owned.topic.id);
44715
+ const switched = switchTopicAccessMode({
44716
+ topicId: options.localTopicId,
44717
+ userId: options.userId,
44718
+ accessMode: "private"
44719
+ });
44720
+ if (!switched.ok)
44721
+ return { ok: false, error: switched.error, status: 409 };
44476
44722
  }
44477
44723
  return { ok: true, localTopicId: options.localTopicId, removedBindings };
44478
44724
  }
@@ -46915,4 +47161,4 @@ switch (command) {
46915
47161
  }
46916
47162
  }
46917
47163
 
46918
- //# debugId=1F2F931DB2BD421464756E2164756E21
47164
+ //# debugId=69B455239128A0A264756E2164756E21