@tritard/waterbrother 0.16.83 → 0.16.85
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/package.json +1 -1
- package/src/gateway.js +151 -0
package/package.json
CHANGED
package/src/gateway.js
CHANGED
|
@@ -488,6 +488,21 @@ function chooseReviewerAgent(project) {
|
|
|
488
488
|
return agents.find((agent) => String(agent?.role || "").trim() === "reviewer") || null;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
function summarizeExecutorReviewerArbitration(project, fallbackExecutor = {}) {
|
|
492
|
+
const executorAgent = chooseExecutorAgent(project, fallbackExecutor);
|
|
493
|
+
const reviewerAgent = chooseReviewerAgent(project);
|
|
494
|
+
const executorRuntime = getAgentRuntimeKey(executorAgent || fallbackExecutor);
|
|
495
|
+
const reviewerRuntime = getAgentRuntimeKey(reviewerAgent);
|
|
496
|
+
return {
|
|
497
|
+
executorAgent,
|
|
498
|
+
reviewerAgent,
|
|
499
|
+
executorRuntime,
|
|
500
|
+
reviewerRuntime,
|
|
501
|
+
aligned: Boolean(executorRuntime && reviewerRuntime && executorRuntime === reviewerRuntime),
|
|
502
|
+
split: Boolean(executorRuntime && reviewerRuntime && executorRuntime !== reviewerRuntime)
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
491
506
|
function parseReviewerOutcome(text = "") {
|
|
492
507
|
const value = String(text || "").trim();
|
|
493
508
|
const lower = value.toLowerCase();
|
|
@@ -525,6 +540,14 @@ function getLatestBlockingReviewPolicy(project) {
|
|
|
525
540
|
return null;
|
|
526
541
|
}
|
|
527
542
|
|
|
543
|
+
function getLatestReviewResult(project) {
|
|
544
|
+
const events = Array.isArray(project?.recentEvents) ? [...project.recentEvents] : [];
|
|
545
|
+
const ordered = events
|
|
546
|
+
.filter((event) => event?.createdAt)
|
|
547
|
+
.sort((a, b) => String(b.createdAt || "").localeCompare(String(a.createdAt || "")));
|
|
548
|
+
return ordered.find((event) => String(event?.type || "").trim() === "review-result") || null;
|
|
549
|
+
}
|
|
550
|
+
|
|
528
551
|
function memberRoleWeight(role = "") {
|
|
529
552
|
const normalized = String(role || "").trim().toLowerCase();
|
|
530
553
|
if (normalized === "owner") return 3;
|
|
@@ -773,6 +796,9 @@ function parseTelegramStateIntent(text = "") {
|
|
|
773
796
|
if (/\bmodel conflict\b/.test(lower) || /\bruntime conflict\b/.test(lower) || /\bmodel split\b/.test(lower) || /\bruntime split\b/.test(lower) || /\bcompare models\b/.test(lower) || /\bcompare bots\b/.test(lower)) {
|
|
774
797
|
return { action: "model-conflict" };
|
|
775
798
|
}
|
|
799
|
+
if (/\bcompare (?:executor|reviewer)\b/.test(lower) || /\bcompare reviewer and executor\b/.test(lower) || /\bcompare executor and reviewer\b/.test(lower) || /\bdo the reviewer and executor agree\b/.test(lower)) {
|
|
800
|
+
return { action: "executor-reviewer-compare" };
|
|
801
|
+
}
|
|
776
802
|
if (/\bwhich (?:bot|agent|terminal) should take this\b/.test(lower) || /\bwho should take this\b/.test(lower) || /\bwho should handle this\b/.test(lower)) {
|
|
777
803
|
return { action: "executor-recommendation" };
|
|
778
804
|
}
|
|
@@ -799,6 +825,16 @@ function parseTelegramStateIntent(text = "") {
|
|
|
799
825
|
return { action: "member-status", target: memberMatch[1].trim() };
|
|
800
826
|
}
|
|
801
827
|
|
|
828
|
+
if (/\baccept reviewer concerns\b/.test(lower) || /\baccept the reviewer'?s concerns\b/.test(lower)) {
|
|
829
|
+
return { action: "accept-reviewer-concerns" };
|
|
830
|
+
}
|
|
831
|
+
if (/\boverride reviewer\b/.test(lower) || /\boverride the reviewer\b/.test(lower)) {
|
|
832
|
+
return { action: "override-reviewer" };
|
|
833
|
+
}
|
|
834
|
+
if (/\bswitch executor to reviewer\b/.test(lower) || /\bmake reviewer the executor\b/.test(lower) || /\buse reviewer as executor\b/.test(lower)) {
|
|
835
|
+
return { action: "switch-executor-to-reviewer" };
|
|
836
|
+
}
|
|
837
|
+
|
|
802
838
|
return null;
|
|
803
839
|
}
|
|
804
840
|
|
|
@@ -2120,6 +2156,8 @@ class TelegramGateway {
|
|
|
2120
2156
|
const { session, project } = await this.bindSharedRoomForMessage(message, sessionId);
|
|
2121
2157
|
const cwd = session.cwd || this.cwd;
|
|
2122
2158
|
const known = this.listKnownChatPeople(message);
|
|
2159
|
+
const actorId = String(message?.from?.id || "").trim();
|
|
2160
|
+
const actorName = this.describeTelegramUser(message?.from || {}).displayName || actorId;
|
|
2123
2161
|
const host = await this.getLiveBridgeHost();
|
|
2124
2162
|
const executor = {
|
|
2125
2163
|
surface: host?.surface || (host ? "live-tui" : "telegram-fallback"),
|
|
@@ -2129,6 +2167,90 @@ class TelegramGateway {
|
|
|
2129
2167
|
hostSessionId: host?.sessionId || ""
|
|
2130
2168
|
};
|
|
2131
2169
|
|
|
2170
|
+
if (intent.action === "accept-reviewer-concerns") {
|
|
2171
|
+
if (!project?.enabled) {
|
|
2172
|
+
return "This project is not shared.";
|
|
2173
|
+
}
|
|
2174
|
+
const result = getLatestReviewResult(project);
|
|
2175
|
+
if (!result) {
|
|
2176
|
+
return "<b>Reviewer concerns</b>\nNo reviewer result is on record yet.";
|
|
2177
|
+
}
|
|
2178
|
+
const meta = result.meta && typeof result.meta === "object" ? result.meta : {};
|
|
2179
|
+
if (String(meta.outcome || "").trim() !== "concerns") {
|
|
2180
|
+
return "<b>Reviewer concerns</b>\nThe latest reviewer result is not concerns.";
|
|
2181
|
+
}
|
|
2182
|
+
await addSharedRoomNote(cwd, "Reviewer concerns accepted; revise before continuing", {
|
|
2183
|
+
actorId,
|
|
2184
|
+
actorName,
|
|
2185
|
+
type: "review-concerns-accepted",
|
|
2186
|
+
meta: {
|
|
2187
|
+
agentId: String(meta.agentId || "").trim()
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
await addSharedRoomNote(cwd, "Blocking review cleared after reviewer concerns were accepted", {
|
|
2191
|
+
actorId,
|
|
2192
|
+
actorName,
|
|
2193
|
+
type: "review-policy-cleared",
|
|
2194
|
+
meta: {
|
|
2195
|
+
agentId: String(meta.agentId || "").trim()
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
return [
|
|
2199
|
+
"<b>Reviewer concerns accepted</b>",
|
|
2200
|
+
"The room accepted the reviewer concerns.",
|
|
2201
|
+
"Blocking review has been cleared so the team can revise and continue."
|
|
2202
|
+
].join("\n");
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
if (intent.action === "override-reviewer") {
|
|
2206
|
+
if (!project?.enabled) {
|
|
2207
|
+
return "This project is not shared.";
|
|
2208
|
+
}
|
|
2209
|
+
const result = getLatestReviewResult(project);
|
|
2210
|
+
const meta = result?.meta && typeof result.meta === "object" ? result.meta : {};
|
|
2211
|
+
await addSharedRoomNote(cwd, "Reviewer outcome overridden by the room", {
|
|
2212
|
+
actorId,
|
|
2213
|
+
actorName,
|
|
2214
|
+
type: "review-policy-override",
|
|
2215
|
+
meta: {
|
|
2216
|
+
agentId: String(meta.agentId || "").trim()
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
return [
|
|
2220
|
+
"<b>Reviewer overridden</b>",
|
|
2221
|
+
"The room overrode the reviewer outcome.",
|
|
2222
|
+
"Execution may proceed."
|
|
2223
|
+
].join("\n");
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
if (intent.action === "switch-executor-to-reviewer") {
|
|
2227
|
+
if (!project?.enabled) {
|
|
2228
|
+
return "This project is not shared.";
|
|
2229
|
+
}
|
|
2230
|
+
const reviewer = chooseReviewerAgent(project);
|
|
2231
|
+
if (!reviewer) {
|
|
2232
|
+
return "<b>Executor switch</b>\nNo reviewer is assigned.";
|
|
2233
|
+
}
|
|
2234
|
+
const currentExecutor = chooseExecutorAgent(project, executor);
|
|
2235
|
+
if (currentExecutor?.id && currentExecutor.id !== reviewer.id) {
|
|
2236
|
+
await upsertSharedAgent(cwd, { ...currentExecutor, role: "standby" }, { actorId, actorName });
|
|
2237
|
+
}
|
|
2238
|
+
await upsertSharedAgent(cwd, { ...reviewer, role: "executor" }, { actorId, actorName });
|
|
2239
|
+
await addSharedRoomNote(cwd, `${reviewer.ownerName || reviewer.label || reviewer.ownerId || "Reviewer"} is now the executor`, {
|
|
2240
|
+
actorId,
|
|
2241
|
+
actorName,
|
|
2242
|
+
type: "executor-handoff-reassigned",
|
|
2243
|
+
meta: {
|
|
2244
|
+
currentAgentId: String(reviewer.id || "").trim()
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
return [
|
|
2248
|
+
"<b>Executor switched</b>",
|
|
2249
|
+
`executor: <code>${escapeTelegramHtml(reviewer.ownerName || reviewer.label || reviewer.ownerId || reviewer.id || "reviewer")}</code>`,
|
|
2250
|
+
"The reviewer is now the selected executor."
|
|
2251
|
+
].join("\n");
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2132
2254
|
if (intent.action === "project-status") {
|
|
2133
2255
|
return formatTelegramSummaryMarkup({
|
|
2134
2256
|
cwd,
|
|
@@ -2233,6 +2355,35 @@ class TelegramGateway {
|
|
|
2233
2355
|
return lines.join("\n");
|
|
2234
2356
|
}
|
|
2235
2357
|
|
|
2358
|
+
if (intent.action === "executor-reviewer-compare") {
|
|
2359
|
+
if (!project?.enabled) {
|
|
2360
|
+
return "This project is not shared.";
|
|
2361
|
+
}
|
|
2362
|
+
const comparison = summarizeExecutorReviewerArbitration(project, executor);
|
|
2363
|
+
if (!comparison.executorAgent && !comparison.reviewerAgent) {
|
|
2364
|
+
return "<b>Executor vs reviewer</b>\nNo executor or reviewer is assigned yet.";
|
|
2365
|
+
}
|
|
2366
|
+
if (!comparison.executorAgent) {
|
|
2367
|
+
return "<b>Executor vs reviewer</b>\nNo executor is assigned yet.";
|
|
2368
|
+
}
|
|
2369
|
+
if (!comparison.reviewerAgent) {
|
|
2370
|
+
return "<b>Executor vs reviewer</b>\nNo reviewer is assigned yet.";
|
|
2371
|
+
}
|
|
2372
|
+
return [
|
|
2373
|
+
"<b>Executor vs reviewer</b>",
|
|
2374
|
+
`executor: <code>${escapeTelegramHtml(comparison.executorAgent.ownerName || comparison.executorAgent.label || comparison.executorAgent.ownerId || comparison.executorAgent.id || "unknown")}</code>`,
|
|
2375
|
+
`executor runtime: <code>${escapeTelegramHtml(comparison.executorRuntime || "unknown")}</code>`,
|
|
2376
|
+
`reviewer: <code>${escapeTelegramHtml(comparison.reviewerAgent.ownerName || comparison.reviewerAgent.label || comparison.reviewerAgent.ownerId || comparison.reviewerAgent.id || "unknown")}</code>`,
|
|
2377
|
+
`reviewer runtime: <code>${escapeTelegramHtml(comparison.reviewerRuntime || "unknown")}</code>`,
|
|
2378
|
+
`alignment: <code>${escapeTelegramHtml(comparison.aligned ? "aligned" : comparison.split ? "split" : "unknown")}</code>`,
|
|
2379
|
+
comparison.split
|
|
2380
|
+
? "These terminals are on different runtimes. Expect different opinions and use the reviewer outcome to arbitrate."
|
|
2381
|
+
: comparison.aligned
|
|
2382
|
+
? "These terminals are on the same runtime. Disagreement is still possible, but it is less likely to come from model differences alone."
|
|
2383
|
+
: "One side is missing runtime identity, so Waterbrother cannot compare them yet."
|
|
2384
|
+
].join("\n");
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2236
2387
|
if (intent.action === "agent-perspective") {
|
|
2237
2388
|
if (!project?.enabled) {
|
|
2238
2389
|
return "This project is not shared.";
|