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
@@ -1016,7 +1016,7 @@ var init_claude_registry = __esm(() => {
1016
1016
  });
1017
1017
 
1018
1018
  // ../../packages/core/src/version.ts
1019
- var NEGOTIUM_VERSION = "0.2.24";
1019
+ var NEGOTIUM_VERSION = "0.2.26";
1020
1020
 
1021
1021
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1022
1022
  import { spawn as spawn2 } from "child_process";
@@ -4059,6 +4059,12 @@ var init_constants = __esm(() => {
4059
4059
  });
4060
4060
 
4061
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
+ }
4062
4068
  function tableColumns(table) {
4063
4069
  const rows = db.query(`PRAGMA table_info(${table})`).all();
4064
4070
  return new Set(rows.map((row) => row.name));
@@ -4182,7 +4188,7 @@ function initializeApiTopicsSchema() {
4182
4188
  is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
4183
4189
  is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
4184
4190
  visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
4185
- 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')),
4186
4192
  browser_profile TEXT NOT NULL DEFAULT 'default',
4187
4193
  browser_profile_owner TEXT,
4188
4194
  session_id TEXT,
@@ -4228,8 +4234,8 @@ function initializeApiTopicsSchema() {
4228
4234
  const legacyBaseEffort = row.base_effort ?? row.default_effort;
4229
4235
  db.query(`INSERT INTO api_topics_next
4230
4236
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
4231
- created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,session_id)
4232
- 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);
4233
4239
  }
4234
4240
  db.exec("DROP TABLE IF EXISTS topic_members");
4235
4241
  db.exec("DROP TABLE api_topics");
@@ -4283,8 +4289,15 @@ function initializeApiTopicsSchema() {
4283
4289
  if (!tableColumns("api_topics").has("visibility")) {
4284
4290
  db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
4285
4291
  }
4286
- if (!tableColumns("api_topics").has("access_mode")) {
4287
- 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
+ }
4288
4301
  }
4289
4302
  if (!tableColumns("api_topics").has("browser_profile")) {
4290
4303
  db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
@@ -4311,7 +4324,48 @@ function initializeApiTopicsSchema() {
4311
4324
  )
4312
4325
  WHERE browser_profile_owner IS NULL
4313
4326
  `);
4327
+ backfillTopicSurfaces();
4314
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, row.kind, normalizedTitle(title)].join("\x00");
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
+ }
4315
4369
  }
4316
4370
  function shortSessionId(sessionId) {
4317
4371
  return sessionId ? sessionId.slice(0, 8) : null;
@@ -4371,12 +4425,9 @@ function rowToDto2(r, participants = getTopicParticipants(r.id), tellTargets) {
4371
4425
  subagentReportMode: r.subagent_report_mode === "tell" || r.subagent_report_mode === "status-only" ? r.subagent_report_mode : "auto"
4372
4426
  } : {},
4373
4427
  visibility: normalizeTopicVisibility(r.visibility),
4374
- accessMode: normalizeTopicAccessMode(r.access_mode)
4428
+ surface: normalizeTopicSurface(r.surface)
4375
4429
  };
4376
4430
  }
4377
- function normalizeTopicAccessMode(value2) {
4378
- return value2 === "shared" ? "shared" : "private";
4379
- }
4380
4431
  function normalizeTopicVisibility(value2) {
4381
4432
  return value2 === "hidden" ? "hidden" : "visible";
4382
4433
  }
@@ -4433,7 +4484,7 @@ function upsertTopic(t) {
4433
4484
  db.transaction(() => {
4434
4485
  db.query(`INSERT INTO api_topics
4435
4486
  (id,title,kind,description,agent,base_model,base_effort,response_policy,
4436
- 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,
4437
4488
  subagent_report_mode)
4438
4489
  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
4439
4490
  ON CONFLICT(id) DO UPDATE SET
@@ -4452,8 +4503,8 @@ function upsertTopic(t) {
4452
4503
  is_fork = excluded.is_fork,
4453
4504
  is_subagent = excluded.is_subagent,
4454
4505
  visibility = excluded.visibility,
4455
- access_mode = excluded.access_mode,
4456
- 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 ?? defaultTopicSurface()), t.subagentReportMode ?? "auto");
4457
4508
  db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
4458
4509
  for (const participant of t.participants) {
4459
4510
  db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
@@ -4466,8 +4517,8 @@ function upsertTopic(t) {
4466
4517
  }
4467
4518
  })();
4468
4519
  }
4469
- function listTopics() {
4470
- 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();
4471
4522
  const participants = getAllTopicParticipants();
4472
4523
  const tellTargets = rows.some((row) => row.is_subagent !== 0) ? getAllSubagentTellTargets() : undefined;
4473
4524
  return rows.map((row) => rowToDto2(row, participants.get(row.id) ?? [], tellTargets));
@@ -4514,14 +4565,15 @@ function getTopicByName(title) {
4514
4565
  }
4515
4566
  function findTopicTitleConflict(title, kind, opts = {}) {
4516
4567
  const wanted = normalizedTitle(title);
4568
+ const surface = normalizeTopicSurface(opts.surface ?? defaultTopicSurface());
4517
4569
  const generalTitleRequested = wanted === normalizedTitle(GENERAL_TOPIC_ID);
4518
4570
  if (generalTitleRequested && opts.excludeTopicId !== GENERAL_TOPIC_ID) {
4519
4571
  const general = db.query("SELECT * FROM api_topics WHERE id = ?").get(GENERAL_TOPIC_ID);
4520
4572
  if (general)
4521
4573
  return rowToDto2(general);
4522
4574
  }
4523
- const params = [wanted];
4524
- 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 = ?";
4525
4577
  if (kind !== "manager") {
4526
4578
  sql += " AND (kind = ? OR id = ?)";
4527
4579
  params.push(kind, GENERAL_TOPIC_ID);
@@ -4606,7 +4658,7 @@ function reparentTopicChildren(deletedTopicId, replacementParentTopicId) {
4606
4658
  }
4607
4659
  return rows.map((row) => row.id);
4608
4660
  }
4609
- var DEFAULT_AGENT_ROOM_AGENT = "maestro";
4661
+ var DEFAULT_AGENT_ROOM_AGENT = "maestro", SURFACE_BACKFILL_MIGRATION = "api_topics_surface_backfill_20260808";
4610
4662
  var init_api_topics = __esm(async () => {
4611
4663
  init_constants();
4612
4664
  init_logger();
@@ -11965,11 +12017,11 @@ __export(exports_derive, {
11965
12017
  });
11966
12018
  import { createHash as createHash4, randomUUID as randomUUID11 } from "crypto";
11967
12019
  import { mkdirSync as mkdirSync14, rmSync as rmSync5, unlinkSync as unlinkSync14 } from "fs";
11968
- function getTopics() {
11969
- return listTopics().filter((topic) => !isLegacySharedGeneral(topic.id));
12020
+ function getTopics(opts = {}) {
12021
+ return listTopics(opts).filter((topic) => !isLegacySharedGeneral(topic.id));
11970
12022
  }
11971
- function getVisibleTopics() {
11972
- return getTopics().filter(isTopicVisible).map((topic) => {
12023
+ function getVisibleTopics(opts = {}) {
12024
+ return getTopics(opts).filter(isTopicVisible).map((topic) => {
11973
12025
  if (!topic.agent)
11974
12026
  return topic;
11975
12027
  const registry = getRegistry(topic.agent);
@@ -11994,8 +12046,8 @@ function updateTopic(topicId, patch) {
11994
12046
  function isParticipant(topic, userId) {
11995
12047
  return topic.participants.some((p) => p.userId === userId);
11996
12048
  }
11997
- function nextDerivedTopicTitle(sourceTitle, kind, suffix) {
11998
- 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()));
11999
12051
  let n = 1;
12000
12052
  let title = `${sourceTitle}-${suffix}-${n}`;
12001
12053
  while (visibleTitles.has(title.toLowerCase())) {
@@ -12059,8 +12111,9 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12059
12111
  { userId, role: "owner" }
12060
12112
  ] : [{ userId, role: "owner" }];
12061
12113
  const kind = topic.kind ?? inferTopicKind(topic);
12062
- const title = opts?.name?.trim() || nextDerivedTopicTitle(topic.title, kind, suffix);
12063
- 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 });
12064
12117
  if (conflict) {
12065
12118
  logger.info({ sourceTopicId, title, kind, conflictTopicId: conflict.id }, "createDerivedTopic: title conflict");
12066
12119
  throw new TopicTitleConflictError(title);
@@ -12083,7 +12136,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12083
12136
  isFork: copyHistory,
12084
12137
  ...subagent ? { isSubagent: true } : {},
12085
12138
  visibility: topic.visibility,
12086
- accessMode: topic.accessMode
12139
+ surface
12087
12140
  };
12088
12141
  let sessionId;
12089
12142
  let rollbackHandle;
@@ -12191,7 +12244,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12191
12244
  if (!currentSource || currentSource.kind === "manager" || !isParticipant(currentSource, userId) || subagent && isRuntimeTopicMaintenance(sourceTopicId)) {
12192
12245
  throw new TopicDeriveBusyError("Source topic changed while deriving; try again");
12193
12246
  }
12194
- const transactionalConflict = findTopicTitleConflict(title, kind);
12247
+ const transactionalConflict = findTopicTitleConflict(title, kind, { surface });
12195
12248
  if (transactionalConflict)
12196
12249
  throw new TopicTitleConflictError(title);
12197
12250
  upsertTopic(derived);
@@ -17835,7 +17888,8 @@ function parseSessionCommContext(args, defaults) {
17835
17888
  function createSessionTargetCatalog(host) {
17836
17889
  const { currentTopicId, currentTopicName, isAgent, listRows } = host;
17837
17890
  function listTargets() {
17838
- 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));
17839
17893
  const titleCounts = new Map;
17840
17894
  const qualifiedCounts = new Map;
17841
17895
  for (const row of eligibleRows) {
@@ -21024,4 +21078,4 @@ export {
21024
21078
  createAgentHealthMcpServer
21025
21079
  };
21026
21080
 
21027
- //# debugId=638C8687E0A0627864756E2164756E21
21081
+ //# debugId=A3645A70D023CC9C64756E2164756E21