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
package/dist/storage.js CHANGED
@@ -1204,21 +1204,20 @@ function deleteApiTopicConfig(topicId) {
1204
1204
  var exports_api_topics = {};
1205
1205
  __export(exports_api_topics, {
1206
1206
  upsertTopic: () => upsertTopic,
1207
+ setTopicSurfaces: () => setTopicSurfaces,
1207
1208
  setTopicSessionId: () => setTopicSessionId,
1208
- setTopicAccessModes: () => setTopicAccessModes,
1209
1209
  setApiTopicAgent: () => setApiTopicAgent,
1210
1210
  revokeSubagentTellTarget: () => revokeSubagentTellTarget,
1211
1211
  reparentTopicChildren: () => reparentTopicChildren,
1212
1212
  removeParticipantFromDB: () => removeParticipantFromDB,
1213
1213
  normalizeTopicVisibility: () => normalizeTopicVisibility,
1214
+ normalizeTopicSurface: () => normalizeTopicSurface,
1214
1215
  normalizeTopicState: () => normalizeTopicState,
1215
1216
  normalizeTopicKind: () => normalizeTopicKind,
1216
- normalizeTopicAccessMode: () => normalizeTopicAccessMode,
1217
1217
  normalizeAiMode: () => normalizeAiMode,
1218
1218
  listTopics: () => listTopics,
1219
1219
  listSubagentTellTargetIds: () => listSubagentTellTargetIds,
1220
1220
  isTopicVisible: () => isTopicVisible,
1221
- isTopicShared: () => isTopicShared,
1222
1221
  inferTopicKind: () => inferTopicKind,
1223
1222
  inferAiMode: () => inferAiMode,
1224
1223
  grantSubagentTellTarget: () => grantSubagentTellTarget,
@@ -1231,6 +1230,7 @@ __export(exports_api_topics, {
1231
1230
  getManagerTopicForUser: () => getManagerTopicForUser,
1232
1231
  findTopicTitleConflict: () => findTopicTitleConflict,
1233
1232
  deleteTopic: () => deleteTopic,
1233
+ defaultTopicSurface: () => defaultTopicSurface,
1234
1234
  clearTopicSessionId: () => clearTopicSessionId,
1235
1235
  aiMentionFromMode: () => aiMentionFromMode,
1236
1236
  addParticipantToDB: () => addParticipantToDB
@@ -1248,6 +1248,12 @@ var GENERAL_TOPIC_ID = "general";
1248
1248
 
1249
1249
  // ../../packages/core/src/storage/api-topics.ts
1250
1250
  var DEFAULT_AGENT_ROOM_AGENT = "maestro";
1251
+ function defaultTopicSurface() {
1252
+ return normalizeTopicSurface(process.env.NEGOTIUM_DEFAULT_SURFACE);
1253
+ }
1254
+ function normalizeTopicSurface(value) {
1255
+ return value === "telegram" || value === "otium" || value === "terminal" ? value : "terminal";
1256
+ }
1251
1257
  function tableColumns2(table) {
1252
1258
  const rows = db.query(`PRAGMA table_info(${table})`).all();
1253
1259
  return new Set(rows.map((row) => row.name));
@@ -1378,7 +1384,7 @@ function initializeApiTopicsSchema() {
1378
1384
  is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
1379
1385
  is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
1380
1386
  visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
1381
- access_mode TEXT NOT NULL DEFAULT 'private' CHECK (access_mode IN ('private','shared')),
1387
+ surface TEXT NOT NULL DEFAULT 'terminal' CHECK (surface IN ('terminal','telegram','otium')),
1382
1388
  browser_profile TEXT NOT NULL DEFAULT 'default',
1383
1389
  browser_profile_owner TEXT,
1384
1390
  session_id TEXT,
@@ -1424,8 +1430,8 @@ function initializeApiTopicsSchema() {
1424
1430
  const legacyBaseEffort = row.base_effort ?? row.default_effort;
1425
1431
  db.query(`INSERT INTO api_topics_next
1426
1432
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
1427
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,session_id)
1428
- 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);
1433
+ created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,session_id)
1434
+ 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);
1429
1435
  }
1430
1436
  db.exec("DROP TABLE IF EXISTS topic_members");
1431
1437
  db.exec("DROP TABLE api_topics");
@@ -1479,8 +1485,15 @@ function initializeApiTopicsSchema() {
1479
1485
  if (!tableColumns2("api_topics").has("visibility")) {
1480
1486
  db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
1481
1487
  }
1482
- if (!tableColumns2("api_topics").has("access_mode")) {
1483
- db.exec("ALTER TABLE api_topics ADD COLUMN access_mode TEXT NOT NULL DEFAULT 'private'");
1488
+ if (!tableColumns2("api_topics").has("surface")) {
1489
+ db.exec("ALTER TABLE api_topics ADD COLUMN surface TEXT NOT NULL DEFAULT 'terminal'");
1490
+ }
1491
+ if (tableColumns2("api_topics").has("access_mode")) {
1492
+ try {
1493
+ db.exec("ALTER TABLE api_topics DROP COLUMN access_mode");
1494
+ } catch (err) {
1495
+ logger.warn({ err }, "api_topics: could not drop the retired access_mode column");
1496
+ }
1484
1497
  }
1485
1498
  if (!tableColumns2("api_topics").has("browser_profile")) {
1486
1499
  db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
@@ -1507,7 +1520,49 @@ function initializeApiTopicsSchema() {
1507
1520
  )
1508
1521
  WHERE browser_profile_owner IS NULL
1509
1522
  `);
1523
+ backfillTopicSurfaces();
1510
1524
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_last_message ON api_topics(last_message_at DESC)");
1525
+ db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface ON api_topics(surface)");
1526
+ }
1527
+ var SURFACE_BACKFILL_MIGRATION = "api_topics_surface_backfill_20260808";
1528
+ function backfillTopicSurfaces() {
1529
+ db.exec(`
1530
+ CREATE TABLE IF NOT EXISTS api_schema_migrations (
1531
+ key TEXT PRIMARY KEY,
1532
+ applied_at TEXT NOT NULL
1533
+ )
1534
+ `);
1535
+ const applied = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(SURFACE_BACKFILL_MIGRATION);
1536
+ if (applied)
1537
+ return;
1538
+ const surface = defaultTopicSurface();
1539
+ db.transaction(() => {
1540
+ db.query("UPDATE api_topics SET surface = ?").run(surface);
1541
+ renameSurfaceTitleCollisions();
1542
+ db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(SURFACE_BACKFILL_MIGRATION, new Date().toISOString());
1543
+ })();
1544
+ logger.info({ surface }, "api_topics: surface backfilled");
1545
+ }
1546
+ function renameSurfaceTitleCollisions() {
1547
+ const rows = db.query("SELECT id, title, kind, surface FROM api_topics ORDER BY created_at ASC, rowid ASC").all();
1548
+ const taken = new Set;
1549
+ const update = db.query("UPDATE api_topics SET title = ? WHERE id = ?");
1550
+ for (const row of rows) {
1551
+ const key = (title) => [row.surface, row.kind, normalizedTitle(title)].join("\x00");
1552
+ if (!taken.has(key(row.title))) {
1553
+ taken.add(key(row.title));
1554
+ continue;
1555
+ }
1556
+ let suffix = 2;
1557
+ let candidate = `${row.title} (${suffix})`;
1558
+ while (taken.has(key(candidate))) {
1559
+ suffix += 1;
1560
+ candidate = `${row.title} (${suffix})`;
1561
+ }
1562
+ taken.add(key(candidate));
1563
+ update.run(candidate, row.id);
1564
+ logger.warn({ topicId: row.id, surface: row.surface, from: row.title, to: candidate }, "api_topics: renamed a duplicate title for surface-scoped uniqueness");
1565
+ }
1511
1566
  }
1512
1567
  registerStorageSchemaInitializer(initializeApiTopicsSchema, 20);
1513
1568
  function shortSessionId(sessionId) {
@@ -1568,15 +1623,9 @@ function rowToDto2(r, participants = getTopicParticipants(r.id), tellTargets) {
1568
1623
  subagentReportMode: r.subagent_report_mode === "tell" || r.subagent_report_mode === "status-only" ? r.subagent_report_mode : "auto"
1569
1624
  } : {},
1570
1625
  visibility: normalizeTopicVisibility(r.visibility),
1571
- accessMode: normalizeTopicAccessMode(r.access_mode)
1626
+ surface: normalizeTopicSurface(r.surface)
1572
1627
  };
1573
1628
  }
1574
- function normalizeTopicAccessMode(value) {
1575
- return value === "shared" ? "shared" : "private";
1576
- }
1577
- function isTopicShared(topic) {
1578
- return topic.accessMode === "shared";
1579
- }
1580
1629
  function normalizeTopicVisibility(value) {
1581
1630
  return value === "hidden" ? "hidden" : "visible";
1582
1631
  }
@@ -1653,7 +1702,7 @@ function upsertTopic(t) {
1653
1702
  db.transaction(() => {
1654
1703
  db.query(`INSERT INTO api_topics
1655
1704
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
1656
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,
1705
+ created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,
1657
1706
  subagent_report_mode)
1658
1707
  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
1659
1708
  ON CONFLICT(id) DO UPDATE SET
@@ -1672,8 +1721,8 @@ function upsertTopic(t) {
1672
1721
  is_fork = excluded.is_fork,
1673
1722
  is_subagent = excluded.is_subagent,
1674
1723
  visibility = excluded.visibility,
1675
- access_mode = excluded.access_mode,
1676
- 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");
1724
+ surface = excluded.surface,
1725
+ 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");
1677
1726
  db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
1678
1727
  for (const participant of t.participants) {
1679
1728
  db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
@@ -1686,18 +1735,8 @@ function upsertTopic(t) {
1686
1735
  }
1687
1736
  })();
1688
1737
  }
1689
- function setTopicAccessModes(topicIds, accessMode) {
1690
- if (topicIds.length === 0)
1691
- return;
1692
- const normalized = normalizeTopicAccessMode(accessMode);
1693
- db.transaction(() => {
1694
- const update = db.query("UPDATE api_topics SET access_mode = ? WHERE id = ?");
1695
- for (const topicId of topicIds)
1696
- update.run(normalized, topicId);
1697
- })();
1698
- }
1699
- function listTopics() {
1700
- const rows = db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
1738
+ function listTopics(opts = {}) {
1739
+ 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();
1701
1740
  const participants = getAllTopicParticipants();
1702
1741
  const tellTargets = rows.some((row) => row.is_subagent !== 0) ? getAllSubagentTellTargets() : undefined;
1703
1742
  return rows.map((row) => rowToDto2(row, participants.get(row.id) ?? [], tellTargets));
@@ -1748,14 +1787,15 @@ function getTopicByNameAndKind(title, kind) {
1748
1787
  }
1749
1788
  function findTopicTitleConflict(title, kind, opts = {}) {
1750
1789
  const wanted = normalizedTitle(title);
1790
+ const surface = normalizeTopicSurface(opts.surface ?? defaultTopicSurface());
1751
1791
  const generalTitleRequested = wanted === normalizedTitle(GENERAL_TOPIC_ID);
1752
1792
  if (generalTitleRequested && opts.excludeTopicId !== GENERAL_TOPIC_ID) {
1753
1793
  const general = db.query("SELECT * FROM api_topics WHERE id = ?").get(GENERAL_TOPIC_ID);
1754
1794
  if (general)
1755
1795
  return rowToDto2(general);
1756
1796
  }
1757
- const params = [wanted];
1758
- let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ?";
1797
+ const params = [wanted, surface];
1798
+ let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ? AND surface = ?";
1759
1799
  if (kind !== "manager") {
1760
1800
  sql += " AND (kind = ? OR id = ?)";
1761
1801
  params.push(kind, GENERAL_TOPIC_ID);
@@ -1768,7 +1808,20 @@ function findTopicTitleConflict(title, kind, opts = {}) {
1768
1808
  const row = db.query(sql).get(...params);
1769
1809
  return row ? rowToDto2(row) : null;
1770
1810
  }
1771
- function getTopicByNameForUser(title, userId) {
1811
+ function setTopicSurfaces(topicIds, surface) {
1812
+ if (topicIds.length === 0)
1813
+ return 0;
1814
+ const normalized = normalizeTopicSurface(surface);
1815
+ let changed = 0;
1816
+ db.transaction(() => {
1817
+ const update = db.query("UPDATE api_topics SET surface = ? WHERE id = ? AND surface != ?");
1818
+ for (const topicId of topicIds) {
1819
+ changed += Number(update.run(normalized, topicId, normalized).changes ?? 0);
1820
+ }
1821
+ })();
1822
+ return changed;
1823
+ }
1824
+ function getTopicByNameForUser(title, userId, opts = {}) {
1772
1825
  const trimmed = title.trim();
1773
1826
  const qualified = /^(agent|channel|manager):(.+)$/i.exec(trimmed);
1774
1827
  const requestedKind = qualified ? normalizeTopicKind(qualified[1]?.toLowerCase()) : null;
@@ -1777,9 +1830,10 @@ function getTopicByNameForUser(title, userId) {
1777
1830
  WHERE LOWER(t.title) = LOWER(?)
1778
1831
  AND t.id != ?
1779
1832
  AND t.visibility != 'hidden'
1833
+ AND (? IS NULL OR t.surface = ?)
1780
1834
  AND EXISTS (
1781
1835
  SELECT 1 FROM topic_members m WHERE m.topic_id = t.id AND m.user_id = ?
1782
- )`).all(requestedTitle, GENERAL_TOPIC_ID, userId);
1836
+ )`).all(requestedTitle, GENERAL_TOPIC_ID, opts.surface ?? null, opts.surface ?? null, userId);
1783
1837
  const matches = requestedKind ? rows.filter((row) => row.kind === requestedKind) : rows;
1784
1838
  return matches.length === 1 ? rowToDto2(matches[0]) : null;
1785
1839
  }
@@ -4052,6 +4106,7 @@ export {
4052
4106
  softDeleteApiMessagesByIdPrefix,
4053
4107
  softDeleteApiMessage,
4054
4108
  settleTopicArchiveJob,
4109
+ setTopicSurfaces,
4055
4110
  setTopicSessionId,
4056
4111
  setTopicMcpExtra,
4057
4112
  setTopicMcpEnabled,
@@ -4062,7 +4117,6 @@ export {
4062
4117
  setTopicAgentAndSession,
4063
4118
  setTopicAgentAndClearSession,
4064
4119
  setTopicAgent,
4065
- setTopicAccessModes,
4066
4120
  setSessionForTopic,
4067
4121
  setLastShownConfig,
4068
4122
  setGlobalAiName,
@@ -4096,9 +4150,9 @@ export {
4096
4150
  quarantineAskUserGate,
4097
4151
  prepareAskUserGate,
4098
4152
  normalizeTopicVisibility,
4153
+ normalizeTopicSurface,
4099
4154
  normalizeTopicState,
4100
4155
  normalizeTopicKind,
4101
- normalizeTopicAccessMode,
4102
4156
  normalizeAiMode,
4103
4157
  markPendingAskState,
4104
4158
  markPendingAskSources,
@@ -4113,7 +4167,6 @@ export {
4113
4167
  listApiMessages,
4114
4168
  isTopicVisible,
4115
4169
  isTopicSummaryFile,
4116
- isTopicShared,
4117
4170
  isTopicBriefFile,
4118
4171
  isRuntimeProcessLeaseAlive,
4119
4172
  inferTopicKind,
@@ -4174,6 +4227,7 @@ export {
4174
4227
  deletePendingAsksForTopic,
4175
4228
  deleteMessagesForTopic,
4176
4229
  deleteApiTopicConfig,
4230
+ defaultTopicSurface,
4177
4231
  db2 as db,
4178
4232
  createTasks,
4179
4233
  createPendingAsk,
@@ -4218,4 +4272,4 @@ export {
4218
4272
  ConversationLogDivergedError
4219
4273
  };
4220
4274
 
4221
- //# debugId=F0363958E9B9826264756E2164756E21
4275
+ //# debugId=5FCBA76877162FB364756E2164756E21