@tritard/waterbrother 0.16.81 → 0.16.83

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 +62 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.81",
3
+ "version": "0.16.83",
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
@@ -483,6 +483,26 @@ function findLiveHostForAgent(hosts = [], agent = {}) {
483
483
  }) || null;
484
484
  }
485
485
 
486
+ function chooseReviewerAgent(project) {
487
+ const agents = listProjectAgents(project);
488
+ return agents.find((agent) => String(agent?.role || "").trim() === "reviewer") || null;
489
+ }
490
+
491
+ function parseReviewerOutcome(text = "") {
492
+ const value = String(text || "").trim();
493
+ const lower = value.toLowerCase();
494
+ if (/^\s*(approved|approve|pass|passed)\b/.test(lower) || /\boverall:\s*approved\b/.test(lower)) {
495
+ return "approved";
496
+ }
497
+ if (/^\s*(blocked|block|reject|rejected)\b/.test(lower) || /\boverall:\s*blocked\b/.test(lower)) {
498
+ return "blocked";
499
+ }
500
+ if (/^\s*(concerns|concern|needs changes|needs-change)\b/.test(lower) || /\boverall:\s*concerns\b/.test(lower)) {
501
+ return "concerns";
502
+ }
503
+ return "";
504
+ }
505
+
486
506
  function getLatestBlockingReviewPolicy(project) {
487
507
  const events = Array.isArray(project?.recentEvents) ? [...project.recentEvents] : [];
488
508
  const ordered = events
@@ -875,6 +895,11 @@ function formatTelegramRoomMarkup(project, options = {}) {
875
895
  : ["• none"];
876
896
  const runtimeConflict = summarizeRuntimeConflict(project, executor);
877
897
  const liveHosts = Array.isArray(options.liveHosts) ? options.liveHosts : [];
898
+ const selectedExecutor = chooseExecutorAgent(project, executor);
899
+ const selectedReviewer = chooseReviewerAgent(project);
900
+ const blockingReview = getLatestBlockingReviewPolicy(project);
901
+ const selectedExecutorLiveHost = selectedExecutor ? findLiveHostForAgent(liveHosts, selectedExecutor) : null;
902
+ const selectedReviewerLiveHost = selectedReviewer ? findLiveHostForAgent(liveHosts, selectedReviewer) : null;
878
903
  const executorBits = [
879
904
  `surface: <code>${escapeTelegramHtml(executor.surface || "telegram")}</code>`,
880
905
  `provider: <code>${escapeTelegramHtml(executor.provider || "unknown")}</code>`,
@@ -897,6 +922,12 @@ function formatTelegramRoomMarkup(project, options = {}) {
897
922
  `room: <code>${escapeTelegramHtml(roomLabel)}</code>`,
898
923
  `active operator: <code>${escapeTelegramHtml(active)}</code>`,
899
924
  `pending invites: <code>${pendingInviteCount}</code>`,
925
+ "<b>Coordination</b>",
926
+ `selected executor: <code>${escapeTelegramHtml(selectedExecutor ? (selectedExecutor.ownerName || selectedExecutor.label || selectedExecutor.ownerId || selectedExecutor.id || "unknown") : "none")}</code>`,
927
+ `executor live: <code>${escapeTelegramHtml(selectedExecutorLiveHost ? "yes" : "no")}</code>`,
928
+ `reviewer: <code>${escapeTelegramHtml(selectedReviewer ? (selectedReviewer.ownerName || selectedReviewer.label || selectedReviewer.ownerId || selectedReviewer.id || "none") : "none")}</code>`,
929
+ `reviewer live: <code>${escapeTelegramHtml(selectedReviewerLiveHost ? "yes" : "no")}</code>`,
930
+ `blocking review: <code>${escapeTelegramHtml(blockingReview ? "yes" : "no")}</code>`,
900
931
  "<b>Executor</b>",
901
932
  ...executorBits,
902
933
  "<b>Runtime Split</b>",
@@ -1931,12 +1962,41 @@ class TelegramGateway {
1931
1962
  targetHost,
1932
1963
  includeSource: true
1933
1964
  });
1965
+ const reviewContent = String(reviewResult?.content || "").trim() || "(no content)";
1966
+ const outcome = parseReviewerOutcome(reviewContent);
1967
+ if (outcome) {
1968
+ await addSharedRoomNote(session.cwd || this.cwd, `${continuation.context?.ownerName || "Assigned"} reviewer returned ${outcome}`, {
1969
+ actorId,
1970
+ actorName,
1971
+ type: "review-result",
1972
+ meta: {
1973
+ agentId: String(continuation.context?.agentId || "").trim(),
1974
+ outcome
1975
+ }
1976
+ });
1977
+ if (outcome === "approved") {
1978
+ await addSharedRoomNote(session.cwd || this.cwd, `${continuation.context?.ownerName || "Assigned"} blocking review marked complete`, {
1979
+ actorId,
1980
+ actorName,
1981
+ type: "review-policy-cleared",
1982
+ meta: {
1983
+ agentId: String(continuation.context?.agentId || "").trim()
1984
+ }
1985
+ });
1986
+ }
1987
+ }
1934
1988
  return {
1935
1989
  markup: [
1936
1990
  "<b>Reviewer response</b>",
1937
1991
  `reviewer: <code>${escapeTelegramHtml(continuation.context?.ownerName || "unknown")}</code>`,
1938
1992
  targetHost ? `terminal: <code>${escapeTelegramHtml(formatBridgeHostLabel(targetHost) || targetHost.sessionId || "live terminal")}</code>` : "",
1939
- renderTelegramChunks(reviewResult?.content || "(no content)").join("\n\n")
1993
+ outcome ? `outcome: <code>${escapeTelegramHtml(outcome)}</code>` : "",
1994
+ outcome === "approved"
1995
+ ? "Blocking review has been cleared."
1996
+ : outcome
1997
+ ? "Blocking review remains in effect until the room overrides or marks it reviewed."
1998
+ : "No structured reviewer outcome was detected. Blocking review remains in effect.",
1999
+ renderTelegramChunks(reviewContent).join("\n\n")
1940
2000
  ].join("\n\n")
1941
2001
  };
1942
2002
  }
@@ -3866,6 +3926,7 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
3866
3926
  const reviewerHost = reviewerAgent ? findLiveHostForAgent(liveHosts, reviewerAgent) : null;
3867
3927
  const reviewPrompt = [
3868
3928
  `Review this pending Telegram Roundtable request before execution.`,
3929
+ `Start your answer with one of: APPROVED, CONCERNS, or BLOCKED.`,
3869
3930
  `Requester: ${this.describeTelegramUser(message?.from || {}).displayName || userId}`,
3870
3931
  `Project: ${operatorGate.project.projectName || path.basename(operatorGate.session?.cwd || this.cwd)}`,
3871
3932
  "",