negotium 0.2.24 → 0.2.26

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 (31) hide show
  1. package/dist/agent-helpers.js +82 -29
  2. package/dist/agent-helpers.js.map +5 -5
  3. package/dist/hosted-agent.js +2 -2
  4. package/dist/hosted-agent.js.map +2 -2
  5. package/dist/main.js +167 -364
  6. package/dist/main.js.map +26 -27
  7. package/dist/mcp-factories.js +84 -30
  8. package/dist/mcp-factories.js.map +6 -6
  9. package/dist/registry.js +2 -2
  10. package/dist/registry.js.map +2 -2
  11. package/dist/runtime/src/index.ts +3 -7
  12. package/dist/runtime/src/mcp/session-comm/default-host.ts +6 -1
  13. package/dist/runtime/src/mcp/session-comm/topic-catalog.ts +17 -1
  14. package/dist/runtime/src/mcp/session-comm/topics.ts +23 -1
  15. package/dist/runtime/src/node-host.ts +2 -3
  16. package/dist/runtime/src/storage/api-topics.ts +164 -52
  17. package/dist/runtime/src/storage/storage-public.ts +0 -1
  18. package/dist/runtime/src/topics/create.ts +12 -6
  19. package/dist/runtime/src/topics/derive.ts +20 -11
  20. package/dist/runtime/src/types/api.ts +8 -4
  21. package/dist/runtime/src/version.ts +1 -1
  22. package/dist/storage.js +92 -38
  23. package/dist/storage.js.map +3 -3
  24. package/dist/types/packages/core/src/mcp/session-comm/topic-catalog.d.ts +7 -0
  25. package/dist/types/packages/core/src/storage/api-topics.d.ts +34 -19
  26. package/dist/types/packages/core/src/storage/storage-public.d.ts +1 -1
  27. package/dist/types/packages/core/src/topics/derive.d.ts +13 -4
  28. package/dist/types/packages/core/src/types/api.d.ts +8 -4
  29. package/dist/types/packages/core/src/version.d.ts +1 -1
  30. package/package.json +1 -1
  31. package/dist/runtime/src/application/switch-topic-access-mode.ts +0 -110
@@ -2395,7 +2395,7 @@ var init_claude_registry = __esm(() => {
2395
2395
  });
2396
2396
 
2397
2397
  // ../../packages/core/src/version.ts
2398
- var NEGOTIUM_VERSION = "0.2.24";
2398
+ var NEGOTIUM_VERSION = "0.2.26";
2399
2399
 
2400
2400
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
2401
2401
  import { spawn as spawn2 } from "child_process";
@@ -7341,6 +7341,12 @@ var init_api_topic_config = __esm(async () => {
7341
7341
  });
7342
7342
 
7343
7343
  // ../../packages/core/src/storage/api-topics.ts
7344
+ function defaultTopicSurface() {
7345
+ return normalizeTopicSurface(process.env.NEGOTIUM_DEFAULT_SURFACE);
7346
+ }
7347
+ function normalizeTopicSurface(value) {
7348
+ return value === "telegram" || value === "otium" || value === "terminal" ? value : "terminal";
7349
+ }
7344
7350
  function tableColumns2(table) {
7345
7351
  const rows = db.query(`PRAGMA table_info(${table})`).all();
7346
7352
  return new Set(rows.map((row) => row.name));
@@ -7471,7 +7477,7 @@ function initializeApiTopicsSchema() {
7471
7477
  is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
7472
7478
  is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
7473
7479
  visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
7474
- access_mode TEXT NOT NULL DEFAULT 'private' CHECK (access_mode IN ('private','shared')),
7480
+ surface TEXT NOT NULL DEFAULT 'terminal' CHECK (surface IN ('terminal','telegram','otium')),
7475
7481
  browser_profile TEXT NOT NULL DEFAULT 'default',
7476
7482
  browser_profile_owner TEXT,
7477
7483
  session_id TEXT,
@@ -7517,8 +7523,8 @@ function initializeApiTopicsSchema() {
7517
7523
  const legacyBaseEffort = row.base_effort ?? row.default_effort;
7518
7524
  db.query(`INSERT INTO api_topics_next
7519
7525
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
7520
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,session_id)
7521
- 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);
7526
+ created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,session_id)
7527
+ 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);
7522
7528
  }
7523
7529
  db.exec("DROP TABLE IF EXISTS topic_members");
7524
7530
  db.exec("DROP TABLE api_topics");
@@ -7572,8 +7578,15 @@ function initializeApiTopicsSchema() {
7572
7578
  if (!tableColumns2("api_topics").has("visibility")) {
7573
7579
  db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
7574
7580
  }
7575
- if (!tableColumns2("api_topics").has("access_mode")) {
7576
- db.exec("ALTER TABLE api_topics ADD COLUMN access_mode TEXT NOT NULL DEFAULT 'private'");
7581
+ if (!tableColumns2("api_topics").has("surface")) {
7582
+ db.exec("ALTER TABLE api_topics ADD COLUMN surface TEXT NOT NULL DEFAULT 'terminal'");
7583
+ }
7584
+ if (tableColumns2("api_topics").has("access_mode")) {
7585
+ try {
7586
+ db.exec("ALTER TABLE api_topics DROP COLUMN access_mode");
7587
+ } catch (err) {
7588
+ logger.warn({ err }, "api_topics: could not drop the retired access_mode column");
7589
+ }
7577
7590
  }
7578
7591
  if (!tableColumns2("api_topics").has("browser_profile")) {
7579
7592
  db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
@@ -7600,7 +7613,48 @@ function initializeApiTopicsSchema() {
7600
7613
  )
7601
7614
  WHERE browser_profile_owner IS NULL
7602
7615
  `);
7616
+ backfillTopicSurfaces();
7603
7617
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_last_message ON api_topics(last_message_at DESC)");
7618
+ db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface ON api_topics(surface)");
7619
+ }
7620
+ function backfillTopicSurfaces() {
7621
+ db.exec(`
7622
+ CREATE TABLE IF NOT EXISTS api_schema_migrations (
7623
+ key TEXT PRIMARY KEY,
7624
+ applied_at TEXT NOT NULL
7625
+ )
7626
+ `);
7627
+ const applied = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(SURFACE_BACKFILL_MIGRATION);
7628
+ if (applied)
7629
+ return;
7630
+ const surface = defaultTopicSurface();
7631
+ db.transaction(() => {
7632
+ db.query("UPDATE api_topics SET surface = ?").run(surface);
7633
+ renameSurfaceTitleCollisions();
7634
+ db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(SURFACE_BACKFILL_MIGRATION, new Date().toISOString());
7635
+ })();
7636
+ logger.info({ surface }, "api_topics: surface backfilled");
7637
+ }
7638
+ function renameSurfaceTitleCollisions() {
7639
+ const rows = db.query("SELECT id, title, kind, surface FROM api_topics ORDER BY created_at ASC, rowid ASC").all();
7640
+ const taken = new Set;
7641
+ const update = db.query("UPDATE api_topics SET title = ? WHERE id = ?");
7642
+ for (const row of rows) {
7643
+ const key = (title) => [row.surface, row.kind, normalizedTitle(title)].join("\x00");
7644
+ if (!taken.has(key(row.title))) {
7645
+ taken.add(key(row.title));
7646
+ continue;
7647
+ }
7648
+ let suffix = 2;
7649
+ let candidate = `${row.title} (${suffix})`;
7650
+ while (taken.has(key(candidate))) {
7651
+ suffix += 1;
7652
+ candidate = `${row.title} (${suffix})`;
7653
+ }
7654
+ taken.add(key(candidate));
7655
+ update.run(candidate, row.id);
7656
+ logger.warn({ topicId: row.id, surface: row.surface, from: row.title, to: candidate }, "api_topics: renamed a duplicate title for surface-scoped uniqueness");
7657
+ }
7604
7658
  }
7605
7659
  function shortSessionId(sessionId) {
7606
7660
  return sessionId ? sessionId.slice(0, 8) : null;
@@ -7660,12 +7714,9 @@ function rowToDto2(r, participants = getTopicParticipants(r.id), tellTargets) {
7660
7714
  subagentReportMode: r.subagent_report_mode === "tell" || r.subagent_report_mode === "status-only" ? r.subagent_report_mode : "auto"
7661
7715
  } : {},
7662
7716
  visibility: normalizeTopicVisibility(r.visibility),
7663
- accessMode: normalizeTopicAccessMode(r.access_mode)
7717
+ surface: normalizeTopicSurface(r.surface)
7664
7718
  };
7665
7719
  }
7666
- function normalizeTopicAccessMode(value) {
7667
- return value === "shared" ? "shared" : "private";
7668
- }
7669
7720
  function normalizeTopicVisibility(value) {
7670
7721
  return value === "hidden" ? "hidden" : "visible";
7671
7722
  }
@@ -7722,7 +7773,7 @@ function upsertTopic(t) {
7722
7773
  db.transaction(() => {
7723
7774
  db.query(`INSERT INTO api_topics
7724
7775
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
7725
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,
7776
+ created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,
7726
7777
  subagent_report_mode)
7727
7778
  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
7728
7779
  ON CONFLICT(id) DO UPDATE SET
@@ -7741,8 +7792,8 @@ function upsertTopic(t) {
7741
7792
  is_fork = excluded.is_fork,
7742
7793
  is_subagent = excluded.is_subagent,
7743
7794
  visibility = excluded.visibility,
7744
- access_mode = excluded.access_mode,
7745
- 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");
7795
+ surface = excluded.surface,
7796
+ 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 ?? defaultTopicSurface()), t.subagentReportMode ?? "auto");
7746
7797
  db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
7747
7798
  for (const participant of t.participants) {
7748
7799
  db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
@@ -7755,8 +7806,8 @@ function upsertTopic(t) {
7755
7806
  }
7756
7807
  })();
7757
7808
  }
7758
- function listTopics() {
7759
- const rows = db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
7809
+ function listTopics(opts = {}) {
7810
+ 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();
7760
7811
  const participants = getAllTopicParticipants();
7761
7812
  const tellTargets = rows.some((row) => row.is_subagent !== 0) ? getAllSubagentTellTargets() : undefined;
7762
7813
  return rows.map((row) => rowToDto2(row, participants.get(row.id) ?? [], tellTargets));
@@ -7799,14 +7850,15 @@ function getTopicMemoryOrigin(id) {
7799
7850
  }
7800
7851
  function findTopicTitleConflict(title, kind, opts = {}) {
7801
7852
  const wanted = normalizedTitle(title);
7853
+ const surface = normalizeTopicSurface(opts.surface ?? defaultTopicSurface());
7802
7854
  const generalTitleRequested = wanted === normalizedTitle(GENERAL_TOPIC_ID);
7803
7855
  if (generalTitleRequested && opts.excludeTopicId !== GENERAL_TOPIC_ID) {
7804
7856
  const general = db.query("SELECT * FROM api_topics WHERE id = ?").get(GENERAL_TOPIC_ID);
7805
7857
  if (general)
7806
7858
  return rowToDto2(general);
7807
7859
  }
7808
- const params = [wanted];
7809
- let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ?";
7860
+ const params = [wanted, surface];
7861
+ let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ? AND surface = ?";
7810
7862
  if (kind !== "manager") {
7811
7863
  sql += " AND (kind = ? OR id = ?)";
7812
7864
  params.push(kind, GENERAL_TOPIC_ID);
@@ -7891,7 +7943,7 @@ function reparentTopicChildren(deletedTopicId, replacementParentTopicId) {
7891
7943
  }
7892
7944
  return rows.map((row) => row.id);
7893
7945
  }
7894
- var DEFAULT_AGENT_ROOM_AGENT = "maestro";
7946
+ var DEFAULT_AGENT_ROOM_AGENT = "maestro", SURFACE_BACKFILL_MIGRATION = "api_topics_surface_backfill_20260808";
7895
7947
  var init_api_topics = __esm(async () => {
7896
7948
  init_constants();
7897
7949
  init_logger();
@@ -12618,11 +12670,11 @@ __export(exports_derive, {
12618
12670
  });
12619
12671
  import { createHash as createHash5, randomUUID as randomUUID12 } from "crypto";
12620
12672
  import { mkdirSync as mkdirSync13, rmSync as rmSync5, unlinkSync as unlinkSync13 } from "fs";
12621
- function getTopics() {
12622
- return listTopics().filter((topic) => !isLegacySharedGeneral(topic.id));
12673
+ function getTopics(opts = {}) {
12674
+ return listTopics(opts).filter((topic) => !isLegacySharedGeneral(topic.id));
12623
12675
  }
12624
- function getVisibleTopics() {
12625
- return getTopics().filter(isTopicVisible).map((topic) => {
12676
+ function getVisibleTopics(opts = {}) {
12677
+ return getTopics(opts).filter(isTopicVisible).map((topic) => {
12626
12678
  if (!topic.agent)
12627
12679
  return topic;
12628
12680
  const registry = getRegistry(topic.agent);
@@ -12647,8 +12699,8 @@ function updateTopic(topicId, patch) {
12647
12699
  function isParticipant(topic, userId) {
12648
12700
  return topic.participants.some((p) => p.userId === userId);
12649
12701
  }
12650
- function nextDerivedTopicTitle(sourceTitle, kind, suffix) {
12651
- const visibleTitles = new Set(listTopics().filter((topic) => topic.kind === kind).map((topic) => topic.title.toLowerCase()));
12702
+ function nextDerivedTopicTitle(sourceTitle, kind, suffix, surface) {
12703
+ const visibleTitles = new Set(listTopics(surface ? { surface } : {}).filter((topic) => topic.kind === kind).map((topic) => topic.title.toLowerCase()));
12652
12704
  let n = 1;
12653
12705
  let title = `${sourceTitle}-${suffix}-${n}`;
12654
12706
  while (visibleTitles.has(title.toLowerCase())) {
@@ -12712,8 +12764,9 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12712
12764
  { userId, role: "owner" }
12713
12765
  ] : [{ userId, role: "owner" }];
12714
12766
  const kind = topic.kind ?? inferTopicKind(topic);
12715
- const title = opts?.name?.trim() || nextDerivedTopicTitle(topic.title, kind, suffix);
12716
- const conflict = findTopicTitleConflict(title, kind);
12767
+ const surface = topic.surface ?? defaultTopicSurface();
12768
+ const title = opts?.name?.trim() || nextDerivedTopicTitle(topic.title, kind, suffix, surface);
12769
+ const conflict = findTopicTitleConflict(title, kind, { surface });
12717
12770
  if (conflict) {
12718
12771
  logger.info({ sourceTopicId, title, kind, conflictTopicId: conflict.id }, "createDerivedTopic: title conflict");
12719
12772
  throw new TopicTitleConflictError(title);
@@ -12736,7 +12789,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12736
12789
  isFork: copyHistory,
12737
12790
  ...subagent ? { isSubagent: true } : {},
12738
12791
  visibility: topic.visibility,
12739
- accessMode: topic.accessMode
12792
+ surface
12740
12793
  };
12741
12794
  let sessionId;
12742
12795
  let rollbackHandle;
@@ -12844,7 +12897,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12844
12897
  if (!currentSource || currentSource.kind === "manager" || !isParticipant(currentSource, userId) || subagent && isRuntimeTopicMaintenance(sourceTopicId)) {
12845
12898
  throw new TopicDeriveBusyError("Source topic changed while deriving; try again");
12846
12899
  }
12847
- const transactionalConflict = findTopicTitleConflict(title, kind);
12900
+ const transactionalConflict = findTopicTitleConflict(title, kind, { surface });
12848
12901
  if (transactionalConflict)
12849
12902
  throw new TopicTitleConflictError(title);
12850
12903
  upsertTopic(derived);
@@ -18757,4 +18810,4 @@ export {
18757
18810
  DEFAULT_SELF_CONFIG_PRODUCT
18758
18811
  };
18759
18812
 
18760
- //# debugId=176C184827ED00D364756E2164756E21
18813
+ //# debugId=88F335565B80EFA364756E2164756E21