@tritard/waterbrother 0.16.79 → 0.16.81

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 +89 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.79",
3
+ "version": "0.16.81",
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
@@ -1900,6 +1900,46 @@ class TelegramGateway {
1900
1900
  const actorId = String(message?.from?.id || "").trim();
1901
1901
  const actorName = peer?.username || [message?.from?.first_name, message?.from?.last_name].filter(Boolean).join(" ").trim() || actorId;
1902
1902
  const session = await loadSession(sessionId);
1903
+ if (/\bsend to reviewer\b/.test(reply) || /\breviewer\b/.test(reply) || /\bsend review\b/.test(reply)) {
1904
+ const targetHost = continuation.context?.reviewerHost && typeof continuation.context.reviewerHost === "object"
1905
+ ? continuation.context.reviewerHost
1906
+ : null;
1907
+ if (!targetHost?.pid) {
1908
+ return {
1909
+ markup: "The assigned reviewer is not live right now. Reply with <code>override</code> or <code>reviewed</code> instead.",
1910
+ remember: {
1911
+ text: String(continuation.lastPrompt || "").trim() || "Reply with override or reviewed.",
1912
+ kind: continuation.kind,
1913
+ source: continuation.source,
1914
+ context: continuation.context || {},
1915
+ force: true
1916
+ }
1917
+ };
1918
+ }
1919
+ const reviewPrompt = String(continuation.context?.reviewPrompt || "").trim();
1920
+ await addSharedRoomNote(session.cwd || this.cwd, `${continuation.context?.ownerName || "Assigned"} blocking review dispatched to reviewer terminal`, {
1921
+ actorId,
1922
+ actorName,
1923
+ type: "review-policy-dispatched",
1924
+ meta: {
1925
+ agentId: String(continuation.context?.agentId || "").trim(),
1926
+ reviewerPid: Number(targetHost.pid || 0)
1927
+ }
1928
+ });
1929
+ const reviewResult = await this.runPromptViaBridge(message, sessionId, reviewPrompt, {
1930
+ explicitExecution: true,
1931
+ targetHost,
1932
+ includeSource: true
1933
+ });
1934
+ return {
1935
+ markup: [
1936
+ "<b>Reviewer response</b>",
1937
+ `reviewer: <code>${escapeTelegramHtml(continuation.context?.ownerName || "unknown")}</code>`,
1938
+ targetHost ? `terminal: <code>${escapeTelegramHtml(formatBridgeHostLabel(targetHost) || targetHost.sessionId || "live terminal")}</code>` : "",
1939
+ renderTelegramChunks(reviewResult?.content || "(no content)").join("\n\n")
1940
+ ].join("\n\n")
1941
+ };
1942
+ }
1903
1943
  if (/\boverride\b/.test(reply)) {
1904
1944
  await addSharedRoomNote(session.cwd || this.cwd, `${continuation.context?.ownerName || "Assigned"} blocking review overridden for now`, {
1905
1945
  actorId,
@@ -1935,7 +1975,7 @@ class TelegramGateway {
1935
1975
  };
1936
1976
  }
1937
1977
  return {
1938
- markup: "Reply with <code>override</code> to proceed anyway, or <code>reviewed</code> after the blocking review is complete.",
1978
+ markup: "Reply with <code>send to reviewer</code>, <code>override</code>, or <code>reviewed</code> after the blocking review is complete.",
1939
1979
  remember: {
1940
1980
  text: String(continuation.lastPrompt || "").trim() || "Reply with override or reviewed.",
1941
1981
  kind: continuation.kind,
@@ -2687,7 +2727,10 @@ class TelegramGateway {
2687
2727
  if (reply.error) {
2688
2728
  throw new Error(reply.error);
2689
2729
  }
2690
- return String(reply.content || "").trim() || "(no content)";
2730
+ const content = String(reply.content || "").trim() || "(no content)";
2731
+ return options.includeSource
2732
+ ? { content, sourceHost: host }
2733
+ : content;
2691
2734
  }
2692
2735
 
2693
2736
  const activeHost = nextBridge.activeHost || {};
@@ -2798,8 +2841,12 @@ class TelegramGateway {
2798
2841
  return message || null;
2799
2842
  }
2800
2843
 
2801
- async deliverPromptResult(chatId, replyToMessageId, previewMessage, content) {
2802
- const chunks = renderTelegramChunks(content);
2844
+ async deliverPromptResult(chatId, replyToMessageId, previewMessage, content, options = {}) {
2845
+ const sourceHost = options.sourceHost && typeof options.sourceHost === "object" ? options.sourceHost : null;
2846
+ const sourcePrefix = sourceHost
2847
+ ? `<b>Handled by</b>\n<code>${escapeTelegramHtml(formatBridgeHostLabel(sourceHost) || sourceHost.sessionId || "live terminal")}</code>\n\n`
2848
+ : "";
2849
+ const chunks = renderTelegramChunks(`${sourcePrefix}${String(content || "")}`);
2803
2850
  if (!chunks.length) {
2804
2851
  if (previewMessage?.message_id) {
2805
2852
  await this.editMessage(chatId, previewMessage.message_id, "(no content)");
@@ -3814,12 +3861,26 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
3814
3861
  if (blockingReview) {
3815
3862
  const meta = blockingReview.meta && typeof blockingReview.meta === "object" ? blockingReview.meta : {};
3816
3863
  const reviewerName = String(meta.ownerName || meta.agentId || "assigned reviewer").trim();
3817
- const followUp = `Blocking review is assigned to ${reviewerName}. Reply with override to proceed anyway, or reviewed when the review is done.`;
3864
+ const reviewerAgent = listProjectAgents(operatorGate.project).find((agent) => String(agent?.id || "").trim() === String(meta.agentId || "").trim()) || null;
3865
+ const liveHosts = await this.getLiveBridgeHosts({ cwd: operatorGate.session?.cwd || this.cwd });
3866
+ const reviewerHost = reviewerAgent ? findLiveHostForAgent(liveHosts, reviewerAgent) : null;
3867
+ const reviewPrompt = [
3868
+ `Review this pending Telegram Roundtable request before execution.`,
3869
+ `Requester: ${this.describeTelegramUser(message?.from || {}).displayName || userId}`,
3870
+ `Project: ${operatorGate.project.projectName || path.basename(operatorGate.session?.cwd || this.cwd)}`,
3871
+ "",
3872
+ `Pending request:`,
3873
+ promptText
3874
+ ].join("\n");
3875
+ const followUp = reviewerHost
3876
+ ? `Blocking review is assigned to ${reviewerName}. Reply with send to reviewer to route it there, override to proceed anyway, or reviewed when the review is done.`
3877
+ : `Blocking review is assigned to ${reviewerName}. Reply with override to proceed anyway, or reviewed when the review is done.`;
3818
3878
  await this.sendMarkup(
3819
3879
  message.chat.id,
3820
3880
  [
3821
3881
  "<b>Blocking review in effect</b>",
3822
3882
  `reviewer: <code>${escapeTelegramHtml(reviewerName)}</code>`,
3883
+ reviewerHost ? `reviewer terminal: <code>${escapeTelegramHtml(formatBridgeHostLabel(reviewerHost) || reviewerHost.sessionId || "live terminal")}</code>` : "",
3823
3884
  "Execution is paused until the blocking review is cleared or overridden.",
3824
3885
  followUp
3825
3886
  ].join("\n"),
@@ -3830,7 +3891,21 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
3830
3891
  source: "review-policy-gate",
3831
3892
  context: {
3832
3893
  agentId: String(meta.agentId || "").trim(),
3833
- ownerName: reviewerName
3894
+ ownerName: reviewerName,
3895
+ reviewPrompt,
3896
+ reviewerHost: reviewerHost
3897
+ ? {
3898
+ pid: Number(reviewerHost.pid || 0),
3899
+ sessionId: String(reviewerHost.sessionId || "").trim(),
3900
+ ownerId: String(reviewerHost.ownerId || "").trim(),
3901
+ ownerName: String(reviewerHost.ownerName || "").trim(),
3902
+ label: String(reviewerHost.label || "").trim(),
3903
+ surface: String(reviewerHost.surface || "").trim(),
3904
+ provider: String(reviewerHost.provider || "").trim(),
3905
+ model: String(reviewerHost.model || "").trim(),
3906
+ runtimeProfile: String(reviewerHost.runtimeProfile || "").trim()
3907
+ }
3908
+ : null
3834
3909
  },
3835
3910
  force: true
3836
3911
  });
@@ -3891,13 +3966,17 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
3891
3966
  return;
3892
3967
  }
3893
3968
  previewMessage = await this.sendProgressMessage(message.chat.id, message.message_id);
3894
- const content = (await this.runPromptViaBridge(message, sessionId, promptText, {
3969
+ const result = (await this.runPromptViaBridge(message, sessionId, promptText, {
3895
3970
  explicitExecution: shouldExecutePrompt,
3896
- targetHost: selectedLiveHost || host || null
3971
+ targetHost: selectedLiveHost || host || null,
3972
+ includeSource: true
3897
3973
  }))
3898
3974
  ?? (await this.runPromptFallback(sessionId, promptText));
3899
- await this.deliverPromptResult(message.chat.id, message.message_id, previewMessage, content);
3900
- await this.rememberContinuation(message, content);
3975
+ const finalContent = typeof result === "string" ? result : String(result?.content || "").trim();
3976
+ await this.deliverPromptResult(message.chat.id, message.message_id, previewMessage, finalContent, {
3977
+ sourceHost: typeof result === "string" ? null : (result?.sourceHost || null)
3978
+ });
3979
+ await this.rememberContinuation(message, finalContent);
3901
3980
  } catch (error) {
3902
3981
  await this.deliverPromptFailure(message.chat.id, message.message_id, previewMessage, error);
3903
3982
  } finally {