@tritard/waterbrother 0.16.51 → 0.16.52

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/gateway.js +115 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.51",
3
+ "version": "0.16.52",
4
4
  "description": "Waterbrother: bring-your-own-model coding CLI with local tools, sessions, operator modes, and approval controls",
5
5
  "type": "module",
6
6
  "bin": {
package/src/gateway.js CHANGED
@@ -270,19 +270,28 @@ function formatGatewaySessionStatus({ sessionId, userId, username, cwd, runtimeP
270
270
  return bits.join("\n");
271
271
  }
272
272
 
273
- function formatTelegramProjectMarkup({ cwd, project, chatId = "", title = "" }) {
273
+ function formatTelegramSummaryMarkup({ cwd, project, chatId = "", title = "", executor = {} }) {
274
+ const roomLabel = project?.room?.chatId
275
+ ? `${project.room.provider || "telegram"} ${project.room.chatId}${project.room.title ? ` (${project.room.title})` : ""}`
276
+ : "not linked";
277
+ const activeOperator = project?.activeOperator?.id
278
+ ? `${project.activeOperator.name || project.activeOperator.id} (${project.activeOperator.id})`
279
+ : "none";
274
280
  const lines = [
275
- "<b>Linked project</b>",
281
+ "<b>Project summary</b>",
282
+ `project: <code>${escapeTelegramHtml(project?.projectName || path.basename(cwd || "") || "-")}</code>`,
276
283
  `cwd: <code>${escapeTelegramHtml(cwd || "-")}</code>`,
277
- `name: <code>${escapeTelegramHtml(project?.projectName || path.basename(cwd || "") || "-")}</code>`,
278
284
  `shared: <code>${project?.enabled ? "yes" : "no"}</code>`
279
285
  ];
280
286
  if (project?.enabled) {
281
287
  lines.push(`room mode: <code>${escapeTelegramHtml(project.roomMode || "chat")}</code>`);
282
- lines.push(
283
- `bound chat: <code>${escapeTelegramHtml(project.room?.provider === "telegram" && project.room?.chatId ? `${project.room.provider} ${project.room.chatId}` : "none")}</code>`
284
- );
288
+ lines.push(`bound chat: <code>${escapeTelegramHtml(roomLabel)}</code>`);
289
+ lines.push(`active operator: <code>${escapeTelegramHtml(activeOperator)}</code>`);
285
290
  lines.push(`members: <code>${escapeTelegramHtml(String((project.members || []).length))}</code>`);
291
+ if (executor?.surface) lines.push(`executor: <code>${escapeTelegramHtml(executor.surface)}</code>`);
292
+ if (executor?.provider && executor?.model) {
293
+ lines.push(`runtime: <code>${escapeTelegramHtml(`${executor.provider}/${executor.model}`)}</code>`);
294
+ }
286
295
  }
287
296
  if (!project?.enabled) {
288
297
  lines.push("Use <code>/project share</code> to turn on Roundtable for this project in this chat.");
@@ -292,6 +301,10 @@ function formatTelegramProjectMarkup({ cwd, project, chatId = "", title = "" })
292
301
  return lines.join("\n");
293
302
  }
294
303
 
304
+ function formatTelegramProjectMarkup({ cwd, project, chatId = "", title = "" }) {
305
+ return formatTelegramSummaryMarkup({ cwd, project, chatId, title });
306
+ }
307
+
295
308
  function normalizeTelegramProjectIntentText(text = "") {
296
309
  return String(text || "").trim().replace(/\s+/g, " ");
297
310
  }
@@ -519,6 +532,31 @@ function formatTelegramRoomMarkup(project, options = {}) {
519
532
  ].join("\n");
520
533
  }
521
534
 
535
+ function formatOwnerActionMarkup({
536
+ title = "Roundtable update",
537
+ projectName = "",
538
+ cwd = "",
539
+ paired = null,
540
+ member = null,
541
+ role = "",
542
+ targetName = "",
543
+ targetId = "",
544
+ note = ""
545
+ } = {}) {
546
+ const lines = [
547
+ `<b>${escapeTelegramHtml(title)}</b>`,
548
+ targetName || targetId ? `person: <code>${escapeTelegramHtml(targetName || targetId)}</code>` : "",
549
+ targetId ? `user id: <code>${escapeTelegramHtml(targetId)}</code>` : "",
550
+ paired === null ? "" : `paired: <code>${paired ? "yes" : "no"}</code>`,
551
+ member === null ? "" : `project member: <code>${member ? "yes" : "no"}</code>`,
552
+ role ? `role: <code>${escapeTelegramHtml(role)}</code>` : "",
553
+ projectName ? `project: <code>${escapeTelegramHtml(projectName)}</code>` : "",
554
+ cwd ? `cwd: <code>${escapeTelegramHtml(cwd)}</code>` : "",
555
+ note ? escapeTelegramHtml(note) : ""
556
+ ].filter(Boolean);
557
+ return lines.join("\n");
558
+ }
559
+
522
560
  function formatTelegramMembersMarkup(project) {
523
561
  if (!project?.enabled) {
524
562
  return "This project is not shared.";
@@ -1050,7 +1088,16 @@ class TelegramGateway {
1050
1088
  return {
1051
1089
  kind: "member",
1052
1090
  project: nextProject,
1053
- markup: `Updated <code>${escapeTelegramHtml(target.displayName || target.userId)}</code> to <code>${escapeTelegramHtml(intent.role)}</code> in this shared project.`
1091
+ markup: formatOwnerActionMarkup({
1092
+ title: "Roundtable member updated",
1093
+ projectName: nextProject.projectName || path.basename(session.cwd || this.cwd),
1094
+ cwd: session.cwd || this.cwd,
1095
+ paired: true,
1096
+ member: true,
1097
+ role: intent.role,
1098
+ targetName: target.displayName || target.userId,
1099
+ targetId: target.userId
1100
+ })
1054
1101
  };
1055
1102
  }
1056
1103
  const known = this.listKnownChatPeople(message);
@@ -1068,7 +1115,16 @@ class TelegramGateway {
1068
1115
  return {
1069
1116
  kind: "member",
1070
1117
  project: nextProject,
1071
- markup: `${pairedNow ? `Paired <code>${escapeTelegramHtml(target.displayName || target.userId)}</code> <i>(${escapeTelegramHtml(target.userId)})</i> and ` : ""}added <code>${escapeTelegramHtml(target.displayName || target.userId)}</code> to this shared project as <code>${escapeTelegramHtml(intent.role)}</code>.`
1118
+ markup: formatOwnerActionMarkup({
1119
+ title: pairedNow ? "Paired and added to Roundtable" : "Added to Roundtable",
1120
+ projectName: nextProject.projectName || path.basename(session.cwd || this.cwd),
1121
+ cwd: session.cwd || this.cwd,
1122
+ paired: true,
1123
+ member: true,
1124
+ role: intent.role,
1125
+ targetName: target.displayName || target.userId,
1126
+ targetId: target.userId
1127
+ })
1072
1128
  };
1073
1129
  }
1074
1130
 
@@ -1078,7 +1134,7 @@ class TelegramGateway {
1078
1134
  const known = this.listKnownChatPeople(message);
1079
1135
 
1080
1136
  if (intent.action === "project-status") {
1081
- return formatTelegramProjectMarkup({
1137
+ return formatTelegramSummaryMarkup({
1082
1138
  cwd,
1083
1139
  project,
1084
1140
  chatId: String(message.chat.id),
@@ -1454,7 +1510,15 @@ class TelegramGateway {
1454
1510
  runtimeProfile: project?.runtimeProfile || this.channel.defaultRuntimeProfile || this.gateway.defaultRuntimeProfile || "",
1455
1511
  hostSessionId: host?.sessionId || ""
1456
1512
  };
1457
- return project?.enabled ? formatTelegramRoomMarkup(project, { executor }) : "<b>Shared room</b>\nThis project is not shared.";
1513
+ return project?.enabled
1514
+ ? `${formatTelegramSummaryMarkup({
1515
+ cwd: project.cwd || this.cwd,
1516
+ project,
1517
+ chatId: String(message.chat.id),
1518
+ title: String(message.chat.title || "").trim(),
1519
+ executor
1520
+ })}\n\n${formatTelegramRoomMarkup(project, { executor })}`
1521
+ : "<b>Shared room</b>\nThis project is not shared.";
1458
1522
  }
1459
1523
 
1460
1524
  async ensureSharedOperator(message, sessionId) {
@@ -1776,9 +1840,18 @@ class TelegramGateway {
1776
1840
  }
1777
1841
  try {
1778
1842
  const result = await this.pairKnownTelegramUser(message, rawTarget);
1843
+ const pairSession = await loadSession(sessionId);
1779
1844
  await this.sendMarkup(
1780
1845
  message.chat.id,
1781
- `Paired <code>${escapeTelegramHtml(result.displayName)}</code> <i>(${escapeTelegramHtml(result.userId)})</i> via <code>${escapeTelegramHtml(result.configPath)}</code>.`,
1846
+ formatOwnerActionMarkup({
1847
+ title: "Telegram user paired",
1848
+ projectName: path.basename(pairSession.cwd || this.cwd),
1849
+ cwd: pairSession.cwd || this.cwd,
1850
+ paired: true,
1851
+ targetName: result.displayName,
1852
+ targetId: result.userId,
1853
+ note: `via ${result.configPath}`
1854
+ }),
1782
1855
  message.message_id
1783
1856
  );
1784
1857
  } catch (error) {
@@ -1789,13 +1862,19 @@ class TelegramGateway {
1789
1862
 
1790
1863
  if (text === "/project") {
1791
1864
  const { session, project } = await this.bindSharedRoomForMessage(message, sessionId);
1865
+ const host = await this.getLiveBridgeHost();
1792
1866
  await this.sendMarkup(
1793
1867
  message.chat.id,
1794
- formatTelegramProjectMarkup({
1868
+ formatTelegramSummaryMarkup({
1795
1869
  cwd: session.cwd || this.cwd,
1796
1870
  project,
1797
1871
  chatId: String(message.chat.id),
1798
- title: String(message.chat.title || "").trim()
1872
+ title: String(message.chat.title || "").trim(),
1873
+ executor: {
1874
+ surface: host ? "live-tui" : "telegram-fallback",
1875
+ provider: this.runtime.provider,
1876
+ model: this.runtime.model
1877
+ }
1799
1878
  }),
1800
1879
  message.message_id
1801
1880
  );
@@ -1807,7 +1886,7 @@ class TelegramGateway {
1807
1886
  const { session, project } = await this.ensureProjectSharedForChat(message, sessionId);
1808
1887
  await this.sendMarkup(
1809
1888
  message.chat.id,
1810
- `${formatTelegramProjectMarkup({
1889
+ `${formatTelegramSummaryMarkup({
1811
1890
  cwd: session.cwd || this.cwd,
1812
1891
  project,
1813
1892
  chatId: String(message.chat.id),
@@ -1831,7 +1910,7 @@ class TelegramGateway {
1831
1910
  const result = await this.switchPeerProject(message, rawPath);
1832
1911
  await this.sendMarkup(
1833
1912
  message.chat.id,
1834
- `${formatTelegramProjectMarkup({
1913
+ `${formatTelegramSummaryMarkup({
1835
1914
  cwd: result.cwd,
1836
1915
  project: result.project,
1837
1916
  chatId: String(message.chat.id),
@@ -1855,7 +1934,7 @@ class TelegramGateway {
1855
1934
  const result = await this.switchPeerProject(message, projectName, { create: true });
1856
1935
  await this.sendMarkup(
1857
1936
  message.chat.id,
1858
- `${formatTelegramProjectMarkup({
1937
+ `${formatTelegramSummaryMarkup({
1859
1938
  cwd: result.cwd,
1860
1939
  project: result.project,
1861
1940
  chatId: String(message.chat.id),
@@ -2425,7 +2504,15 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
2425
2504
  const result = await this.pairKnownTelegramUser(message, pairingIntent.target);
2426
2505
  await this.sendMarkup(
2427
2506
  message.chat.id,
2428
- `Paired <code>${escapeTelegramHtml(result.displayName)}</code> <i>(${escapeTelegramHtml(result.userId)})</i> via <code>${escapeTelegramHtml(result.configPath)}</code>.`,
2507
+ formatOwnerActionMarkup({
2508
+ title: "Telegram user paired",
2509
+ projectName: sharedBinding.project?.projectName || path.basename(sharedBinding.session.cwd || this.cwd),
2510
+ cwd: sharedBinding.session.cwd || this.cwd,
2511
+ paired: true,
2512
+ targetName: result.displayName,
2513
+ targetId: result.userId,
2514
+ note: `via ${result.configPath}`
2515
+ }),
2429
2516
  message.message_id
2430
2517
  );
2431
2518
  } catch (error) {
@@ -2445,13 +2532,19 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
2445
2532
  }
2446
2533
  const projectIntent = parseTelegramProjectIntent(promptText);
2447
2534
  if (projectIntent?.action === "show-project") {
2535
+ const host = await this.getLiveBridgeHost();
2448
2536
  await this.sendMarkup(
2449
2537
  message.chat.id,
2450
- formatTelegramProjectMarkup({
2538
+ formatTelegramSummaryMarkup({
2451
2539
  cwd: sharedBinding.session.cwd || this.cwd,
2452
2540
  project: sharedBinding.project,
2453
2541
  chatId: String(message.chat.id),
2454
- title: String(message.chat.title || "").trim()
2542
+ title: String(message.chat.title || "").trim(),
2543
+ executor: {
2544
+ surface: host ? "live-tui" : "telegram-fallback",
2545
+ provider: this.runtime.provider,
2546
+ model: this.runtime.model
2547
+ }
2455
2548
  }),
2456
2549
  message.message_id
2457
2550
  );
@@ -2462,7 +2555,7 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
2462
2555
  const { session, project } = await this.ensureProjectSharedForChat(message, sessionId);
2463
2556
  await this.sendMarkup(
2464
2557
  message.chat.id,
2465
- `${formatTelegramProjectMarkup({
2558
+ `${formatTelegramSummaryMarkup({
2466
2559
  cwd: session.cwd || this.cwd,
2467
2560
  project,
2468
2561
  chatId: String(message.chat.id),
@@ -2480,7 +2573,7 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
2480
2573
  const result = await this.switchPeerProject(message, projectIntent.target);
2481
2574
  await this.sendMarkup(
2482
2575
  message.chat.id,
2483
- `${formatTelegramProjectMarkup({
2576
+ `${formatTelegramSummaryMarkup({
2484
2577
  cwd: result.cwd,
2485
2578
  project: result.project,
2486
2579
  chatId: String(message.chat.id),
@@ -2498,7 +2591,7 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
2498
2591
  const result = await this.switchPeerProject(message, projectIntent.name, { create: true, share: projectIntent.share });
2499
2592
  await this.sendMarkup(
2500
2593
  message.chat.id,
2501
- `${formatTelegramProjectMarkup({
2594
+ `${formatTelegramSummaryMarkup({
2502
2595
  cwd: result.cwd,
2503
2596
  project: result.project,
2504
2597
  chatId: String(message.chat.id),