@tritard/waterbrother 0.16.76 → 0.16.77
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 +114 -0
package/package.json
CHANGED
package/src/gateway.js
CHANGED
|
@@ -419,6 +419,53 @@ function chooseExecutorAgent(project, fallbackExecutor = {}) {
|
|
|
419
419
|
return null;
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
+
function getAgentRuntimeKey(agent = {}) {
|
|
423
|
+
const provider = String(agent?.provider || "").trim();
|
|
424
|
+
const model = String(agent?.model || "").trim();
|
|
425
|
+
return provider && model ? `${provider}/${model}` : "";
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function listRuntimeGroups(project, fallbackExecutor = {}) {
|
|
429
|
+
const groups = new Map();
|
|
430
|
+
for (const agent of listProjectAgents(project)) {
|
|
431
|
+
const runtime = getAgentRuntimeKey(agent);
|
|
432
|
+
if (!runtime) continue;
|
|
433
|
+
if (!groups.has(runtime)) {
|
|
434
|
+
groups.set(runtime, []);
|
|
435
|
+
}
|
|
436
|
+
groups.get(runtime).push(agent);
|
|
437
|
+
}
|
|
438
|
+
if (!groups.size && fallbackExecutor?.provider && fallbackExecutor?.model) {
|
|
439
|
+
const runtime = `${fallbackExecutor.provider}/${fallbackExecutor.model}`;
|
|
440
|
+
groups.set(runtime, [{
|
|
441
|
+
id: "",
|
|
442
|
+
ownerId: String(project?.activeOperator?.id || "").trim(),
|
|
443
|
+
ownerName: String(project?.activeOperator?.name || project?.activeOperator?.id || "").trim(),
|
|
444
|
+
label: String(project?.activeOperator?.name || project?.activeOperator?.id || "active terminal").trim(),
|
|
445
|
+
surface: String(fallbackExecutor.surface || "").trim(),
|
|
446
|
+
role: "executor",
|
|
447
|
+
provider: String(fallbackExecutor.provider || "").trim(),
|
|
448
|
+
model: String(fallbackExecutor.model || "").trim(),
|
|
449
|
+
runtimeProfile: String(fallbackExecutor.runtimeProfile || "").trim(),
|
|
450
|
+
sessionId: String(fallbackExecutor.hostSessionId || "").trim()
|
|
451
|
+
}]);
|
|
452
|
+
}
|
|
453
|
+
return [...groups.entries()].map(([runtime, agents]) => ({ runtime, agents }));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function summarizeRuntimeConflict(project, fallbackExecutor = {}) {
|
|
457
|
+
const groups = listRuntimeGroups(project, fallbackExecutor).sort((left, right) => right.agents.length - left.agents.length);
|
|
458
|
+
const uniqueRuntimes = groups.filter((group) => String(group.runtime || "").trim());
|
|
459
|
+
if (!uniqueRuntimes.length) {
|
|
460
|
+
return { hasConflict: false, runtimeCount: 0, groups: [] };
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
hasConflict: uniqueRuntimes.length > 1,
|
|
464
|
+
runtimeCount: uniqueRuntimes.length,
|
|
465
|
+
groups: uniqueRuntimes
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
422
469
|
function getLatestBlockingReviewPolicy(project) {
|
|
423
470
|
const events = Array.isArray(project?.recentEvents) ? [...project.recentEvents] : [];
|
|
424
471
|
const ordered = events
|
|
@@ -486,6 +533,7 @@ function formatTelegramSummaryMarkup({ cwd, project, chatId = "", title = "", ex
|
|
|
486
533
|
if (project?.enabled) {
|
|
487
534
|
const participants = listProjectParticipants(project);
|
|
488
535
|
const agents = listProjectAgents(project);
|
|
536
|
+
const runtimeConflict = summarizeRuntimeConflict(project, executor);
|
|
489
537
|
lines.push(`room mode: <code>${escapeTelegramHtml(project.roomMode || "chat")}</code>`);
|
|
490
538
|
lines.push(`bound chat: <code>${escapeTelegramHtml(roomLabel)}</code>`);
|
|
491
539
|
lines.push(`active operator: <code>${escapeTelegramHtml(activeOperator)}</code>`);
|
|
@@ -493,6 +541,9 @@ function formatTelegramSummaryMarkup({ cwd, project, chatId = "", title = "", ex
|
|
|
493
541
|
if (agents.length) {
|
|
494
542
|
lines.push(`agents: <code>${escapeTelegramHtml(String(agents.length))}</code>`);
|
|
495
543
|
}
|
|
544
|
+
if (runtimeConflict.runtimeCount) {
|
|
545
|
+
lines.push(`runtime split: <code>${escapeTelegramHtml(runtimeConflict.hasConflict ? `yes (${runtimeConflict.runtimeCount})` : "no")}</code>`);
|
|
546
|
+
}
|
|
496
547
|
if (executor?.surface) lines.push(`executor: <code>${escapeTelegramHtml(executor.surface)}</code>`);
|
|
497
548
|
if (executor?.provider && executor?.model) {
|
|
498
549
|
lines.push(`runtime: <code>${escapeTelegramHtml(`${executor.provider}/${executor.model}`)}</code>`);
|
|
@@ -679,9 +730,16 @@ function parseTelegramStateIntent(text = "") {
|
|
|
679
730
|
if (/\bwho are the bots\b/.test(lower) || /\bwho are the agents\b/.test(lower) || /\bwhat bots are here\b/.test(lower) || /\bwhat agents are here\b/.test(lower)) {
|
|
680
731
|
return { action: "agent-list" };
|
|
681
732
|
}
|
|
733
|
+
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)) {
|
|
734
|
+
return { action: "model-conflict" };
|
|
735
|
+
}
|
|
682
736
|
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)) {
|
|
683
737
|
return { action: "executor-recommendation" };
|
|
684
738
|
}
|
|
739
|
+
const agentPerspectiveMatch = value.match(/^(?:what does|how does)\s+(.+?)['’]s\s+model\s+think\??$/i);
|
|
740
|
+
if (agentPerspectiveMatch?.[1]) {
|
|
741
|
+
return { action: "agent-perspective", target: agentPerspectiveMatch[1].trim() };
|
|
742
|
+
}
|
|
685
743
|
const agentModelMatch = value.match(/^(?:what model is|which model is|what runtime is)\s+(.+?)['’]s\s+(?:bot|terminal)(?:\s+on)?\??$/i);
|
|
686
744
|
if (agentModelMatch?.[1]) {
|
|
687
745
|
return { action: "agent-model", target: agentModelMatch[1].trim() };
|
|
@@ -795,6 +853,7 @@ function formatTelegramRoomMarkup(project, options = {}) {
|
|
|
795
853
|
const agentLines = agents.length
|
|
796
854
|
? agents.map((agent) => `• ${escapeTelegramHtml(formatAgentLabel(agent) || agent.id || "agent")} <code>${escapeTelegramHtml(agent.surface || "unknown")}</code>`)
|
|
797
855
|
: ["• none"];
|
|
856
|
+
const runtimeConflict = summarizeRuntimeConflict(project, executor);
|
|
798
857
|
const executorBits = [
|
|
799
858
|
`surface: <code>${escapeTelegramHtml(executor.surface || "telegram")}</code>`,
|
|
800
859
|
`provider: <code>${escapeTelegramHtml(executor.provider || "unknown")}</code>`,
|
|
@@ -819,6 +878,16 @@ function formatTelegramRoomMarkup(project, options = {}) {
|
|
|
819
878
|
`pending invites: <code>${pendingInviteCount}</code>`,
|
|
820
879
|
"<b>Executor</b>",
|
|
821
880
|
...executorBits,
|
|
881
|
+
"<b>Runtime Split</b>",
|
|
882
|
+
runtimeConflict.runtimeCount
|
|
883
|
+
? `status: <code>${escapeTelegramHtml(runtimeConflict.hasConflict ? `split (${runtimeConflict.runtimeCount})` : "aligned")}</code>`
|
|
884
|
+
: "status: <code>unknown</code>",
|
|
885
|
+
...runtimeConflict.groups.map((group) => {
|
|
886
|
+
const owners = group.agents
|
|
887
|
+
.map((agent) => String(agent.ownerName || agent.label || agent.ownerId || agent.id || "").trim())
|
|
888
|
+
.filter(Boolean);
|
|
889
|
+
return `• <code>${escapeTelegramHtml(group.runtime)}</code>${owners.length ? ` — ${escapeTelegramHtml(owners.join(", "))}` : ""}`;
|
|
890
|
+
}),
|
|
822
891
|
"<b>Task Summary</b>",
|
|
823
892
|
`total: <code>${tasks.length}</code>`,
|
|
824
893
|
...[...byState.entries()].map(([state, count]) => `${escapeTelegramHtml(state)}: <code>${count}</code>`),
|
|
@@ -1998,6 +2067,51 @@ class TelegramGateway {
|
|
|
1998
2067
|
].join("\n");
|
|
1999
2068
|
}
|
|
2000
2069
|
|
|
2070
|
+
if (intent.action === "model-conflict") {
|
|
2071
|
+
if (!project?.enabled) {
|
|
2072
|
+
return "This project is not shared.";
|
|
2073
|
+
}
|
|
2074
|
+
const summary = summarizeRuntimeConflict(project, executor);
|
|
2075
|
+
if (!summary.runtimeCount) {
|
|
2076
|
+
return "<b>Model conflict check</b>\nNo registered terminal runtime is known yet.";
|
|
2077
|
+
}
|
|
2078
|
+
const lines = [
|
|
2079
|
+
"<b>Model conflict check</b>",
|
|
2080
|
+
summary.hasConflict
|
|
2081
|
+
? `This room has a runtime split across <code>${escapeTelegramHtml(String(summary.runtimeCount))}</code> models.`
|
|
2082
|
+
: "This room is currently aligned on one runtime."
|
|
2083
|
+
];
|
|
2084
|
+
for (const group of summary.groups) {
|
|
2085
|
+
const owners = group.agents
|
|
2086
|
+
.map((agent) => String(agent.ownerName || agent.label || agent.ownerId || agent.id || "").trim())
|
|
2087
|
+
.filter(Boolean);
|
|
2088
|
+
lines.push(`• <code>${escapeTelegramHtml(group.runtime)}</code>${owners.length ? ` — ${escapeTelegramHtml(owners.join(", "))}` : ""}`);
|
|
2089
|
+
}
|
|
2090
|
+
if (summary.hasConflict) {
|
|
2091
|
+
lines.push("If you want a second opinion, assign a reviewer or ask whose bot should handle the work.");
|
|
2092
|
+
}
|
|
2093
|
+
return lines.join("\n");
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
if (intent.action === "agent-perspective") {
|
|
2097
|
+
if (!project?.enabled) {
|
|
2098
|
+
return "This project is not shared.";
|
|
2099
|
+
}
|
|
2100
|
+
const agent = resolveProjectAgent(project, intent.target);
|
|
2101
|
+
if (!agent) {
|
|
2102
|
+
return `No terminal found for ${escapeTelegramHtml(intent.target || "that person")} in this room yet.`;
|
|
2103
|
+
}
|
|
2104
|
+
const runtime = agent.provider && agent.model ? `${agent.provider}/${agent.model}` : "unknown";
|
|
2105
|
+
return [
|
|
2106
|
+
"<b>Model perspective</b>",
|
|
2107
|
+
`person: <code>${escapeTelegramHtml(agent.ownerName || agent.ownerId || agent.label || agent.id || "-")}</code>`,
|
|
2108
|
+
`terminal: <code>${escapeTelegramHtml(agent.label || agent.id || "unknown")}</code>`,
|
|
2109
|
+
`runtime: <code>${escapeTelegramHtml(runtime)}</code>`,
|
|
2110
|
+
`role: <code>${escapeTelegramHtml(agent.role || "standby")}</code>`,
|
|
2111
|
+
"This shows which model would speak for that terminal. To get its perspective in the room, assign it as reviewer or executor."
|
|
2112
|
+
].join("\n");
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2001
2115
|
if (intent.action === "agent-model") {
|
|
2002
2116
|
if (!project?.enabled) {
|
|
2003
2117
|
return "This project is not shared.";
|