negotium 0.2.23 → 0.2.25

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 +84 -30
  2. package/dist/agent-helpers.js.map +6 -6
  3. package/dist/background-bash.js +2 -1
  4. package/dist/background-bash.js.map +3 -3
  5. package/dist/browser-runtime.js +2 -1
  6. package/dist/browser-runtime.js.map +3 -3
  7. package/dist/{chunk-rhp26p2c.js → chunk-1y2xw0xe.js} +2 -1
  8. package/dist/{chunk-rhp26p2c.js.map → chunk-1y2xw0xe.js.map} +3 -3
  9. package/dist/hosted-agent.js +3 -2
  10. package/dist/hosted-agent.js.map +4 -4
  11. package/dist/main.js +224 -366
  12. package/dist/main.js.map +27 -28
  13. package/dist/mcp-factories.js +86 -31
  14. package/dist/mcp-factories.js.map +7 -7
  15. package/dist/prompts.js +2 -1
  16. package/dist/prompts.js.map +3 -3
  17. package/dist/query-runtime.js +2 -1
  18. package/dist/query-runtime.js.map +3 -3
  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/index.ts +3 -7
  23. package/dist/runtime/src/mcp/session-comm/default-host.ts +6 -1
  24. package/dist/runtime/src/mcp/session-comm/topic-catalog.ts +17 -1
  25. package/dist/runtime/src/mcp/session-comm/topics.ts +23 -1
  26. package/dist/runtime/src/node-host.ts +4 -4
  27. package/dist/runtime/src/platform/config.ts +12 -0
  28. package/dist/runtime/src/storage/api-topics.ts +160 -52
  29. package/dist/runtime/src/storage/storage-public.ts +0 -1
  30. package/dist/runtime/src/topics/create.ts +12 -6
  31. package/dist/runtime/src/topics/derive.ts +20 -11
  32. package/dist/runtime/src/types/api.ts +8 -4
  33. package/dist/runtime/src/version.ts +1 -1
  34. package/dist/runtime-helpers.js +2 -1
  35. package/dist/runtime-helpers.js.map +3 -3
  36. package/dist/storage.js +92 -38
  37. package/dist/storage.js.map +3 -3
  38. package/dist/types/packages/core/src/mcp/session-comm/topic-catalog.d.ts +7 -0
  39. package/dist/types/packages/core/src/platform/config.d.ts +12 -0
  40. package/dist/types/packages/core/src/storage/api-topics.d.ts +34 -19
  41. package/dist/types/packages/core/src/storage/storage-public.d.ts +1 -1
  42. package/dist/types/packages/core/src/topics/derive.d.ts +13 -4
  43. package/dist/types/packages/core/src/types/api.d.ts +8 -4
  44. package/dist/types/packages/core/src/version.d.ts +1 -1
  45. package/dist/vault.js +2 -1
  46. package/dist/vault.js.map +3 -3
  47. package/package.json +1 -1
  48. package/dist/runtime/src/application/switch-topic-access-mode.ts +0 -110
@@ -313,7 +313,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
313
313
  function codexAuthFilePath() {
314
314
  return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
315
315
  }
316
- 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;
316
+ 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, NODE_ID, 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;
317
317
  var init_config = __esm(() => {
318
318
  init_config_helpers();
319
319
  init_logger();
@@ -355,6 +355,7 @@ var init_config = __esm(() => {
355
355
  BG_BASH_MAX_PORT = parsePortEnv(process.env.BG_BASH_MAX_PORT, 9799);
356
356
  RUNTIME_MCP_SECRET = loadOrCreateLocalSecret("RUNTIME_MCP_SECRET", "runtime-mcp-secret");
357
357
  NODE_CONTROL_TOKEN = loadOrCreateLocalSecret("NEGOTIUM_CONTROL_TOKEN", "node-control-token");
358
+ NODE_ID = loadOrCreateLocalSecret("NEGOTIUM_NODE_ID", "node-id");
358
359
  VAULT_MASTER_KEY = loadOrCreateLocalSecret("NEGOTIUM_VAULT_MASTER_KEY", "vault-master-key", { persistEnvValue: true });
359
360
  delete process.env.NEGOTIUM_VAULT_MASTER_KEY;
360
361
  NEGOTIUM_PORT = parseInt(process.env.NEGOTIUM_PORT || "7777", 10);
@@ -1015,7 +1016,7 @@ var init_claude_registry = __esm(() => {
1015
1016
  });
1016
1017
 
1017
1018
  // ../../packages/core/src/version.ts
1018
- var NEGOTIUM_VERSION = "0.2.23";
1019
+ var NEGOTIUM_VERSION = "0.2.25";
1019
1020
 
1020
1021
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1021
1022
  import { spawn as spawn2 } from "child_process";
@@ -4058,6 +4059,12 @@ var init_constants = __esm(() => {
4058
4059
  });
4059
4060
 
4060
4061
  // ../../packages/core/src/storage/api-topics.ts
4062
+ function defaultTopicSurface() {
4063
+ return normalizeTopicSurface(process.env.NEGOTIUM_DEFAULT_SURFACE);
4064
+ }
4065
+ function normalizeTopicSurface(value2) {
4066
+ return value2 === "telegram" || value2 === "otium" || value2 === "terminal" ? value2 : "terminal";
4067
+ }
4061
4068
  function tableColumns(table) {
4062
4069
  const rows = db.query(`PRAGMA table_info(${table})`).all();
4063
4070
  return new Set(rows.map((row) => row.name));
@@ -4181,7 +4188,7 @@ function initializeApiTopicsSchema() {
4181
4188
  is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
4182
4189
  is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
4183
4190
  visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
4184
- access_mode TEXT NOT NULL DEFAULT 'private' CHECK (access_mode IN ('private','shared')),
4191
+ surface TEXT NOT NULL DEFAULT 'terminal' CHECK (surface IN ('terminal','telegram','otium')),
4185
4192
  browser_profile TEXT NOT NULL DEFAULT 'default',
4186
4193
  browser_profile_owner TEXT,
4187
4194
  session_id TEXT,
@@ -4227,8 +4234,8 @@ function initializeApiTopicsSchema() {
4227
4234
  const legacyBaseEffort = row.base_effort ?? row.default_effort;
4228
4235
  db.query(`INSERT INTO api_topics_next
4229
4236
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
4230
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,session_id)
4231
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(String(row.id), String(row.title), normalized.kind, typeof row.description === "string" ? row.description : null, normalized.agent ?? null, typeof legacyBaseModel === "string" ? legacyBaseModel : null, typeof legacyBaseEffort === "string" ? legacyBaseEffort : null, normalized.aiMode, String(row.created_at), typeof row.last_message_at === "string" ? row.last_message_at : null, typeof row.parent_topic_id === "string" ? row.parent_topic_id : null, typeof row.memory_topic_id === "string" ? row.memory_topic_id : null, typeof row.memory_key === "string" ? row.memory_key : null, Number(row.is_fork ?? 0) !== 0 ? 1 : 0, Number(row.is_subagent ?? 0) !== 0 ? 1 : 0, row.visibility === "hidden" ? "hidden" : "visible", row.access_mode === "shared" ? "shared" : "private", typeof row.session_id === "string" ? row.session_id : null);
4237
+ created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,session_id)
4238
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(String(row.id), String(row.title), normalized.kind, typeof row.description === "string" ? row.description : null, normalized.agent ?? null, typeof legacyBaseModel === "string" ? legacyBaseModel : null, typeof legacyBaseEffort === "string" ? legacyBaseEffort : null, normalized.aiMode, String(row.created_at), typeof row.last_message_at === "string" ? row.last_message_at : null, typeof row.parent_topic_id === "string" ? row.parent_topic_id : null, typeof row.memory_topic_id === "string" ? row.memory_topic_id : null, typeof row.memory_key === "string" ? row.memory_key : null, Number(row.is_fork ?? 0) !== 0 ? 1 : 0, Number(row.is_subagent ?? 0) !== 0 ? 1 : 0, row.visibility === "hidden" ? "hidden" : "visible", row.surface === undefined ? defaultTopicSurface() : normalizeTopicSurface(row.surface), typeof row.session_id === "string" ? row.session_id : null);
4232
4239
  }
4233
4240
  db.exec("DROP TABLE IF EXISTS topic_members");
4234
4241
  db.exec("DROP TABLE api_topics");
@@ -4282,8 +4289,15 @@ function initializeApiTopicsSchema() {
4282
4289
  if (!tableColumns("api_topics").has("visibility")) {
4283
4290
  db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
4284
4291
  }
4285
- if (!tableColumns("api_topics").has("access_mode")) {
4286
- db.exec("ALTER TABLE api_topics ADD COLUMN access_mode TEXT NOT NULL DEFAULT 'private'");
4292
+ if (!tableColumns("api_topics").has("surface")) {
4293
+ db.exec("ALTER TABLE api_topics ADD COLUMN surface TEXT NOT NULL DEFAULT 'terminal'");
4294
+ }
4295
+ if (tableColumns("api_topics").has("access_mode")) {
4296
+ try {
4297
+ db.exec("ALTER TABLE api_topics DROP COLUMN access_mode");
4298
+ } catch (err) {
4299
+ logger.warn({ err }, "api_topics: could not drop the retired access_mode column");
4300
+ }
4287
4301
  }
4288
4302
  if (!tableColumns("api_topics").has("browser_profile")) {
4289
4303
  db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
@@ -4310,7 +4324,48 @@ function initializeApiTopicsSchema() {
4310
4324
  )
4311
4325
  WHERE browser_profile_owner IS NULL
4312
4326
  `);
4327
+ backfillTopicSurfaces();
4313
4328
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_last_message ON api_topics(last_message_at DESC)");
4329
+ db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface ON api_topics(surface)");
4330
+ }
4331
+ function backfillTopicSurfaces() {
4332
+ db.exec(`
4333
+ CREATE TABLE IF NOT EXISTS api_schema_migrations (
4334
+ key TEXT PRIMARY KEY,
4335
+ applied_at TEXT NOT NULL
4336
+ )
4337
+ `);
4338
+ const applied = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(SURFACE_BACKFILL_MIGRATION);
4339
+ if (applied)
4340
+ return;
4341
+ const surface = defaultTopicSurface();
4342
+ db.transaction(() => {
4343
+ db.query("UPDATE api_topics SET surface = ?").run(surface);
4344
+ renameSurfaceTitleCollisions();
4345
+ db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(SURFACE_BACKFILL_MIGRATION, new Date().toISOString());
4346
+ })();
4347
+ logger.info({ surface }, "api_topics: surface backfilled");
4348
+ }
4349
+ function renameSurfaceTitleCollisions() {
4350
+ const rows = db.query("SELECT id, title, kind, surface FROM api_topics ORDER BY created_at ASC, rowid ASC").all();
4351
+ const taken = new Set;
4352
+ const update = db.query("UPDATE api_topics SET title = ? WHERE id = ?");
4353
+ for (const row of rows) {
4354
+ const key = (title) => `${row.surface}\x00${row.kind}\x00${normalizedTitle(title)}`;
4355
+ if (!taken.has(key(row.title))) {
4356
+ taken.add(key(row.title));
4357
+ continue;
4358
+ }
4359
+ let suffix = 2;
4360
+ let candidate = `${row.title} (${suffix})`;
4361
+ while (taken.has(key(candidate))) {
4362
+ suffix += 1;
4363
+ candidate = `${row.title} (${suffix})`;
4364
+ }
4365
+ taken.add(key(candidate));
4366
+ update.run(candidate, row.id);
4367
+ logger.warn({ topicId: row.id, surface: row.surface, from: row.title, to: candidate }, "api_topics: renamed a duplicate title for surface-scoped uniqueness");
4368
+ }
4314
4369
  }
4315
4370
  function shortSessionId(sessionId) {
4316
4371
  return sessionId ? sessionId.slice(0, 8) : null;
@@ -4370,12 +4425,9 @@ function rowToDto2(r, participants = getTopicParticipants(r.id), tellTargets) {
4370
4425
  subagentReportMode: r.subagent_report_mode === "tell" || r.subagent_report_mode === "status-only" ? r.subagent_report_mode : "auto"
4371
4426
  } : {},
4372
4427
  visibility: normalizeTopicVisibility(r.visibility),
4373
- accessMode: normalizeTopicAccessMode(r.access_mode)
4428
+ surface: normalizeTopicSurface(r.surface)
4374
4429
  };
4375
4430
  }
4376
- function normalizeTopicAccessMode(value2) {
4377
- return value2 === "shared" ? "shared" : "private";
4378
- }
4379
4431
  function normalizeTopicVisibility(value2) {
4380
4432
  return value2 === "hidden" ? "hidden" : "visible";
4381
4433
  }
@@ -4432,7 +4484,7 @@ function upsertTopic(t) {
4432
4484
  db.transaction(() => {
4433
4485
  db.query(`INSERT INTO api_topics
4434
4486
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
4435
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,
4487
+ created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,
4436
4488
  subagent_report_mode)
4437
4489
  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
4438
4490
  ON CONFLICT(id) DO UPDATE SET
@@ -4451,8 +4503,8 @@ function upsertTopic(t) {
4451
4503
  is_fork = excluded.is_fork,
4452
4504
  is_subagent = excluded.is_subagent,
4453
4505
  visibility = excluded.visibility,
4454
- access_mode = excluded.access_mode,
4455
- subagent_report_mode = excluded.subagent_report_mode`).run(t.id, t.title, normalized.kind, t.description ?? null, normalized.agent ?? null, t.defaultModel ?? null, t.defaultEffort ?? null, normalized.aiMode, t.createdAt, t.lastMessageAt ?? null, t.parentTopicId ?? null, t.memoryTopicId ?? null, t.memoryKey ?? null, t.isFork ? 1 : 0, t.isSubagent ? 1 : 0, normalizeTopicVisibility(t.visibility), normalizeTopicAccessMode(t.accessMode), t.subagentReportMode ?? "auto");
4506
+ surface = excluded.surface,
4507
+ subagent_report_mode = excluded.subagent_report_mode`).run(t.id, t.title, normalized.kind, t.description ?? null, normalized.agent ?? null, t.defaultModel ?? null, t.defaultEffort ?? null, normalized.aiMode, t.createdAt, t.lastMessageAt ?? null, t.parentTopicId ?? null, t.memoryTopicId ?? null, t.memoryKey ?? null, t.isFork ? 1 : 0, t.isSubagent ? 1 : 0, normalizeTopicVisibility(t.visibility), normalizeTopicSurface(t.surface), t.subagentReportMode ?? "auto");
4456
4508
  db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
4457
4509
  for (const participant of t.participants) {
4458
4510
  db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
@@ -4465,8 +4517,8 @@ function upsertTopic(t) {
4465
4517
  }
4466
4518
  })();
4467
4519
  }
4468
- function listTopics() {
4469
- const rows = db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
4520
+ function listTopics(opts = {}) {
4521
+ const rows = opts.surface ? db.query("SELECT * FROM api_topics WHERE surface = ? ORDER BY last_message_at DESC").all(normalizeTopicSurface(opts.surface)) : db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
4470
4522
  const participants = getAllTopicParticipants();
4471
4523
  const tellTargets = rows.some((row) => row.is_subagent !== 0) ? getAllSubagentTellTargets() : undefined;
4472
4524
  return rows.map((row) => rowToDto2(row, participants.get(row.id) ?? [], tellTargets));
@@ -4513,14 +4565,15 @@ function getTopicByName(title) {
4513
4565
  }
4514
4566
  function findTopicTitleConflict(title, kind, opts = {}) {
4515
4567
  const wanted = normalizedTitle(title);
4568
+ const surface = normalizeTopicSurface(opts.surface ?? defaultTopicSurface());
4516
4569
  const generalTitleRequested = wanted === normalizedTitle(GENERAL_TOPIC_ID);
4517
4570
  if (generalTitleRequested && opts.excludeTopicId !== GENERAL_TOPIC_ID) {
4518
4571
  const general = db.query("SELECT * FROM api_topics WHERE id = ?").get(GENERAL_TOPIC_ID);
4519
4572
  if (general)
4520
4573
  return rowToDto2(general);
4521
4574
  }
4522
- const params = [wanted];
4523
- let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ?";
4575
+ const params = [wanted, surface];
4576
+ let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ? AND surface = ?";
4524
4577
  if (kind !== "manager") {
4525
4578
  sql += " AND (kind = ? OR id = ?)";
4526
4579
  params.push(kind, GENERAL_TOPIC_ID);
@@ -4605,7 +4658,7 @@ function reparentTopicChildren(deletedTopicId, replacementParentTopicId) {
4605
4658
  }
4606
4659
  return rows.map((row) => row.id);
4607
4660
  }
4608
- var DEFAULT_AGENT_ROOM_AGENT = "maestro";
4661
+ var DEFAULT_AGENT_ROOM_AGENT = "maestro", SURFACE_BACKFILL_MIGRATION = "api_topics_surface_backfill_20260808";
4609
4662
  var init_api_topics = __esm(async () => {
4610
4663
  init_constants();
4611
4664
  init_logger();
@@ -11964,11 +12017,11 @@ __export(exports_derive, {
11964
12017
  });
11965
12018
  import { createHash as createHash4, randomUUID as randomUUID11 } from "crypto";
11966
12019
  import { mkdirSync as mkdirSync14, rmSync as rmSync5, unlinkSync as unlinkSync14 } from "fs";
11967
- function getTopics() {
11968
- return listTopics().filter((topic) => !isLegacySharedGeneral(topic.id));
12020
+ function getTopics(opts = {}) {
12021
+ return listTopics(opts).filter((topic) => !isLegacySharedGeneral(topic.id));
11969
12022
  }
11970
- function getVisibleTopics() {
11971
- return getTopics().filter(isTopicVisible).map((topic) => {
12023
+ function getVisibleTopics(opts = {}) {
12024
+ return getTopics(opts).filter(isTopicVisible).map((topic) => {
11972
12025
  if (!topic.agent)
11973
12026
  return topic;
11974
12027
  const registry = getRegistry(topic.agent);
@@ -11993,8 +12046,8 @@ function updateTopic(topicId, patch) {
11993
12046
  function isParticipant(topic, userId) {
11994
12047
  return topic.participants.some((p) => p.userId === userId);
11995
12048
  }
11996
- function nextDerivedTopicTitle(sourceTitle, kind, suffix) {
11997
- const visibleTitles = new Set(listTopics().filter((topic) => topic.kind === kind).map((topic) => topic.title.toLowerCase()));
12049
+ function nextDerivedTopicTitle(sourceTitle, kind, suffix, surface) {
12050
+ const visibleTitles = new Set(listTopics(surface ? { surface } : {}).filter((topic) => topic.kind === kind).map((topic) => topic.title.toLowerCase()));
11998
12051
  let n = 1;
11999
12052
  let title = `${sourceTitle}-${suffix}-${n}`;
12000
12053
  while (visibleTitles.has(title.toLowerCase())) {
@@ -12058,8 +12111,9 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12058
12111
  { userId, role: "owner" }
12059
12112
  ] : [{ userId, role: "owner" }];
12060
12113
  const kind = topic.kind ?? inferTopicKind(topic);
12061
- const title = opts?.name?.trim() || nextDerivedTopicTitle(topic.title, kind, suffix);
12062
- const conflict = findTopicTitleConflict(title, kind);
12114
+ const surface = topic.surface ?? defaultTopicSurface();
12115
+ const title = opts?.name?.trim() || nextDerivedTopicTitle(topic.title, kind, suffix, surface);
12116
+ const conflict = findTopicTitleConflict(title, kind, { surface });
12063
12117
  if (conflict) {
12064
12118
  logger.info({ sourceTopicId, title, kind, conflictTopicId: conflict.id }, "createDerivedTopic: title conflict");
12065
12119
  throw new TopicTitleConflictError(title);
@@ -12082,7 +12136,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12082
12136
  isFork: copyHistory,
12083
12137
  ...subagent ? { isSubagent: true } : {},
12084
12138
  visibility: topic.visibility,
12085
- accessMode: topic.accessMode
12139
+ surface
12086
12140
  };
12087
12141
  let sessionId;
12088
12142
  let rollbackHandle;
@@ -12190,7 +12244,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12190
12244
  if (!currentSource || currentSource.kind === "manager" || !isParticipant(currentSource, userId) || subagent && isRuntimeTopicMaintenance(sourceTopicId)) {
12191
12245
  throw new TopicDeriveBusyError("Source topic changed while deriving; try again");
12192
12246
  }
12193
- const transactionalConflict = findTopicTitleConflict(title, kind);
12247
+ const transactionalConflict = findTopicTitleConflict(title, kind, { surface });
12194
12248
  if (transactionalConflict)
12195
12249
  throw new TopicTitleConflictError(title);
12196
12250
  upsertTopic(derived);
@@ -17834,7 +17888,8 @@ function parseSessionCommContext(args, defaults) {
17834
17888
  function createSessionTargetCatalog(host) {
17835
17889
  const { currentTopicId, currentTopicName, isAgent, listRows } = host;
17836
17890
  function listTargets() {
17837
- const eligibleRows = listRows().filter((row) => row.kind !== "manager");
17891
+ const currentSurface = host.currentSurface;
17892
+ const eligibleRows = listRows().filter((row) => row.kind !== "manager" && (!currentSurface || (row.surface ?? currentSurface) === currentSurface));
17838
17893
  const titleCounts = new Map;
17839
17894
  const qualifiedCounts = new Map;
17840
17895
  for (const row of eligibleRows) {
@@ -21023,4 +21078,4 @@ export {
21023
21078
  createAgentHealthMcpServer
21024
21079
  };
21025
21080
 
21026
- //# debugId=467CF7DD204969D364756E2164756E21
21081
+ //# debugId=BFB111C75156DA0764756E2164756E21