@tritard/waterbrother 0.16.32 → 0.16.33

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.
package/README.md CHANGED
@@ -283,6 +283,8 @@ Current Telegram behavior:
283
283
  - Telegram now supports remote workspace control with `/cwd`, `/use <path>`, `/desktop`, and `/new-project <name>`
284
284
  - shared projects now support `/room`, `/members`, `/invites`, `/tasks`, `/mode`, `/claim`, `/release`, `/invite`, `/accept-invite`, `/approve-invite`, `/reject-invite`, `/remove-member`, `/room-runtime`, and `/task ...` from Telegram
285
285
  - `/room` now includes pending invite count plus task ownership summaries
286
+ - local `/status` now includes `sharedRoom` with pending invites, task ownership summary, and recent task activity
287
+ - Telegram now posts a Roundtable ownership notice when shared task ownership changes via assign/claim/move
286
288
  - shared Telegram execution only runs when the shared room is in `execute` mode
287
289
  - room administration is owner-only, and only owners/editors can hold the operator lock
288
290
  - `/room` status now shows the active executor surface plus provider/model/runtime identity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.32",
3
+ "version": "0.16.33",
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/cli.js CHANGED
@@ -2652,10 +2652,23 @@ function buildRoomStatusPayload(project, runtime = null, currentSession = null)
2652
2652
  const assignee = String(task?.assignedTo || "").trim() || "unassigned";
2653
2653
  taskSummary.byAssignee[assignee] = (taskSummary.byAssignee[assignee] || 0) + 1;
2654
2654
  }
2655
+ const recentActivity = tasks
2656
+ .flatMap((task) => (Array.isArray(task.history) ? task.history.map((entry) => ({
2657
+ taskId: task.id,
2658
+ taskText: task.text,
2659
+ type: entry.type,
2660
+ text: entry.text,
2661
+ actorId: entry.actorId || "",
2662
+ actorName: entry.actorName || "",
2663
+ createdAt: entry.createdAt || ""
2664
+ })) : []))
2665
+ .sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)))
2666
+ .slice(0, 5);
2655
2667
  return {
2656
2668
  ...project,
2657
2669
  pendingInviteCount: Array.isArray(project.pendingInvites) ? project.pendingInvites.length : 0,
2658
2670
  taskSummary,
2671
+ recentActivity,
2659
2672
  executor: {
2660
2673
  surface: "local-tui",
2661
2674
  roomRuntimeProfile: project.runtimeProfile || "",
@@ -7557,7 +7570,8 @@ Be concrete about surfaces — name actual pages/flows. Choose the best stack fo
7557
7570
  sessionApprovals: approvals,
7558
7571
  sessionApprovalsSummary: formatSessionApprovalsSummary(approvals),
7559
7572
  messageCount: agent.getSessionMessages().length,
7560
- mcp: mcpStatus
7573
+ mcp: mcpStatus,
7574
+ sharedRoom: await loadSharedProject(context.cwd).then((project) => buildRoomStatusPayload(project, context.runtime, currentSession)).catch(() => ({ enabled: false }))
7561
7575
  },
7562
7576
  null,
7563
7577
  2
package/src/gateway.js CHANGED
@@ -411,6 +411,23 @@ function formatTelegramTaskHistoryMarkup(result) {
411
411
  return lines.join("\n");
412
412
  }
413
413
 
414
+ function buildTaskOwnershipNotice(task, { action = "updated", previousAssignee = "", previousState = "" } = {}) {
415
+ if (!task) return "";
416
+ const bits = [
417
+ "<b>Roundtable update</b>",
418
+ `<code>${escapeTelegramHtml(task.id || "")}</code> ${escapeTelegramHtml(task.text || "")}`
419
+ ];
420
+ if (action === "assign") {
421
+ bits.push(`owner: <code>${escapeTelegramHtml(previousAssignee || "unassigned")}</code> → <code>${escapeTelegramHtml(task.assignedTo || "unassigned")}</code>`);
422
+ } else if (action === "claim") {
423
+ bits.push(`claimed by <code>${escapeTelegramHtml(task.assignedTo || "unassigned")}</code>`);
424
+ } else if (action === "move") {
425
+ bits.push(`state: <code>${escapeTelegramHtml(previousState || "open")}</code> → <code>${escapeTelegramHtml(task.state || "open")}</code>`);
426
+ if (task.assignedTo) bits.push(`owner: <code>${escapeTelegramHtml(task.assignedTo)}</code>`);
427
+ }
428
+ return bits.join("\n");
429
+ }
430
+
414
431
  function classifyTelegramGroupIntent(text = "") {
415
432
  const normalized = String(text || "").trim();
416
433
  const lower = normalized.toLowerCase();
@@ -1395,8 +1412,11 @@ class TelegramGateway {
1395
1412
  return;
1396
1413
  }
1397
1414
  try {
1415
+ const before = (project?.tasks || []).find((task) => task.id === taskId) || null;
1398
1416
  const result = await moveSharedTask(session.cwd || this.cwd, taskId, state, { actorId: userId });
1399
1417
  await this.sendMessage(message.chat.id, `Moved shared task <code>${escapeTelegramHtml(result.task.id)}</code> to <code>${escapeTelegramHtml(result.task.state)}</code>`, message.message_id);
1418
+ const notice = buildTaskOwnershipNotice(result.task, { action: "move", previousAssignee: before?.assignedTo || "", previousState: before?.state || "" });
1419
+ if (notice) await this.sendMessage(message.chat.id, notice, null, { parseMode: "HTML" });
1400
1420
  } catch (error) {
1401
1421
  await this.sendMessage(message.chat.id, escapeTelegramHtml(error instanceof Error ? error.message : String(error)), message.message_id);
1402
1422
  }
@@ -1415,8 +1435,11 @@ class TelegramGateway {
1415
1435
  return;
1416
1436
  }
1417
1437
  try {
1438
+ const before = (project?.tasks || []).find((task) => task.id === taskId) || null;
1418
1439
  const result = await assignSharedTask(session.cwd || this.cwd, taskId, memberId, { actorId: userId });
1419
1440
  await this.sendMessage(message.chat.id, `Assigned shared task <code>${escapeTelegramHtml(result.task.id)}</code> to <code>${escapeTelegramHtml(memberId)}</code>`, message.message_id);
1441
+ const notice = buildTaskOwnershipNotice(result.task, { action: "assign", previousAssignee: before?.assignedTo || "" });
1442
+ if (notice) await this.sendMessage(message.chat.id, notice, null, { parseMode: "HTML" });
1420
1443
  } catch (error) {
1421
1444
  await this.sendMessage(message.chat.id, escapeTelegramHtml(error instanceof Error ? error.message : String(error)), message.message_id);
1422
1445
  }
@@ -1482,6 +1505,8 @@ class TelegramGateway {
1482
1505
  try {
1483
1506
  const result = await claimSharedTask(session.cwd || this.cwd, taskId, { actorId: userId });
1484
1507
  await this.sendMessage(message.chat.id, `Claimed shared task <code>${escapeTelegramHtml(result.task.id)}</code>`, message.message_id);
1508
+ const notice = buildTaskOwnershipNotice(result.task, { action: "claim" });
1509
+ if (notice) await this.sendMessage(message.chat.id, notice, null, { parseMode: "HTML" });
1485
1510
  } catch (error) {
1486
1511
  await this.sendMessage(message.chat.id, escapeTelegramHtml(error instanceof Error ? error.message : String(error)), message.message_id);
1487
1512
  }