@tritard/waterbrother 0.16.78 → 0.16.80
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/cli.js +38 -12
- package/src/gateway-state.js +3 -0
- package/src/gateway.js +95 -6
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -3107,23 +3107,49 @@ async function clearTelegramBridgeHost() {
|
|
|
3107
3107
|
async function dequeueTelegramBridgeRequest({ cwd }) {
|
|
3108
3108
|
const bridge = await loadGatewayBridge(TELEGRAM_BRIDGE_SERVICE);
|
|
3109
3109
|
const activeHost = bridge.activeHost || {};
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
if (activeHost.cwd && String(activeHost.cwd) !== String(cwd || "")) {
|
|
3114
|
-
return null;
|
|
3115
|
-
}
|
|
3110
|
+
const currentHost = Number(activeHost.pid || 0) === process.pid
|
|
3111
|
+
? activeHost
|
|
3112
|
+
: (Array.isArray(bridge.hosts) ? bridge.hosts.find((host) => Number(host?.pid || 0) === process.pid) : null) || {};
|
|
3116
3113
|
const pending = Array.isArray(bridge.pendingRequests) ? bridge.pendingRequests : [];
|
|
3117
3114
|
if (!pending.length) {
|
|
3118
3115
|
return null;
|
|
3119
3116
|
}
|
|
3120
|
-
const
|
|
3117
|
+
const nextIndex = pending.findIndex((request) => {
|
|
3118
|
+
const targetPid = Number(request?.targetPid || 0);
|
|
3119
|
+
const targetSessionId = String(request?.targetSessionId || "").trim();
|
|
3120
|
+
if (targetPid > 0 && targetPid !== process.pid) {
|
|
3121
|
+
return false;
|
|
3122
|
+
}
|
|
3123
|
+
if (targetSessionId && targetSessionId !== String(currentHost.sessionId || "").trim()) {
|
|
3124
|
+
return false;
|
|
3125
|
+
}
|
|
3126
|
+
if (currentHost.cwd && String(currentHost.cwd) !== String(cwd || "")) {
|
|
3127
|
+
return false;
|
|
3128
|
+
}
|
|
3129
|
+
return true;
|
|
3130
|
+
});
|
|
3131
|
+
if (nextIndex < 0) {
|
|
3132
|
+
return null;
|
|
3133
|
+
}
|
|
3134
|
+
const next = pending[nextIndex];
|
|
3135
|
+
const rest = pending.filter((_, index) => index !== nextIndex);
|
|
3121
3136
|
bridge.pendingRequests = rest;
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3137
|
+
if (Number(currentHost.pid || 0) === process.pid) {
|
|
3138
|
+
const updatedHost = {
|
|
3139
|
+
...currentHost,
|
|
3140
|
+
updatedAt: new Date().toISOString()
|
|
3141
|
+
};
|
|
3142
|
+
if (Number(activeHost.pid || 0) === process.pid) {
|
|
3143
|
+
bridge.activeHost = updatedHost;
|
|
3144
|
+
}
|
|
3145
|
+
bridge.hosts = upsertTelegramBridgeHostEntry(bridge.hosts, updatedHost);
|
|
3146
|
+
} else if (Number(activeHost.pid || 0) === process.pid) {
|
|
3147
|
+
bridge.activeHost = {
|
|
3148
|
+
...activeHost,
|
|
3149
|
+
updatedAt: new Date().toISOString()
|
|
3150
|
+
};
|
|
3151
|
+
bridge.hosts = upsertTelegramBridgeHostEntry(bridge.hosts, bridge.activeHost);
|
|
3152
|
+
}
|
|
3127
3153
|
await saveGatewayBridge(TELEGRAM_BRIDGE_SERVICE, bridge);
|
|
3128
3154
|
return next;
|
|
3129
3155
|
}
|
package/src/gateway-state.js
CHANGED
|
@@ -54,6 +54,9 @@ function normalizeBridgeRequest(parsed = {}) {
|
|
|
54
54
|
sessionId: String(parsed?.sessionId || "").trim(),
|
|
55
55
|
text: String(parsed?.text || "").trim(),
|
|
56
56
|
explicitExecution: parsed?.explicitExecution === true,
|
|
57
|
+
targetPid: Number.isFinite(Number(parsed?.targetPid)) ? Math.floor(Number(parsed.targetPid)) : 0,
|
|
58
|
+
targetSessionId: String(parsed?.targetSessionId || "").trim(),
|
|
59
|
+
targetOwnerId: String(parsed?.targetOwnerId || "").trim(),
|
|
57
60
|
runtimeProfile: String(parsed?.runtimeProfile || "").trim(),
|
|
58
61
|
replyToMessageId: Number.isFinite(Number(parsed?.replyToMessageId)) ? Math.floor(Number(parsed.replyToMessageId)) : 0,
|
|
59
62
|
requestedAt: String(parsed?.requestedAt || "").trim(),
|
package/src/gateway.js
CHANGED
|
@@ -473,6 +473,16 @@ function formatBridgeHostLabel(host = {}) {
|
|
|
473
473
|
return [owner || label, label && label !== owner ? `(${label})` : "", runtime ? `[${runtime}]` : ""].filter(Boolean).join(" ").trim();
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
+
function findLiveHostForAgent(hosts = [], agent = {}) {
|
|
477
|
+
const ownerId = String(agent?.ownerId || "").trim();
|
|
478
|
+
const sessionId = String(agent?.sessionId || "").trim();
|
|
479
|
+
return (Array.isArray(hosts) ? hosts : []).find((host) => {
|
|
480
|
+
const hostOwnerId = String(host?.ownerId || "").trim();
|
|
481
|
+
const hostSessionId = String(host?.sessionId || "").trim();
|
|
482
|
+
return (ownerId && hostOwnerId === ownerId) || (sessionId && hostSessionId === sessionId);
|
|
483
|
+
}) || null;
|
|
484
|
+
}
|
|
485
|
+
|
|
476
486
|
function getLatestBlockingReviewPolicy(project) {
|
|
477
487
|
const events = Array.isArray(project?.recentEvents) ? [...project.recentEvents] : [];
|
|
478
488
|
const ordered = events
|
|
@@ -1890,6 +1900,44 @@ class TelegramGateway {
|
|
|
1890
1900
|
const actorId = String(message?.from?.id || "").trim();
|
|
1891
1901
|
const actorName = peer?.username || [message?.from?.first_name, message?.from?.last_name].filter(Boolean).join(" ").trim() || actorId;
|
|
1892
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 content = await this.runPromptViaBridge(message, sessionId, reviewPrompt, {
|
|
1930
|
+
explicitExecution: true,
|
|
1931
|
+
targetHost
|
|
1932
|
+
});
|
|
1933
|
+
return {
|
|
1934
|
+
markup: [
|
|
1935
|
+
"<b>Reviewer response</b>",
|
|
1936
|
+
`reviewer: <code>${escapeTelegramHtml(continuation.context?.ownerName || "unknown")}</code>`,
|
|
1937
|
+
renderTelegramChunks(content || "(no content)").join("\n\n")
|
|
1938
|
+
].join("\n\n")
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1893
1941
|
if (/\boverride\b/.test(reply)) {
|
|
1894
1942
|
await addSharedRoomNote(session.cwd || this.cwd, `${continuation.context?.ownerName || "Assigned"} blocking review overridden for now`, {
|
|
1895
1943
|
actorId,
|
|
@@ -1925,7 +1973,7 @@ class TelegramGateway {
|
|
|
1925
1973
|
};
|
|
1926
1974
|
}
|
|
1927
1975
|
return {
|
|
1928
|
-
markup: "Reply with <code>
|
|
1976
|
+
markup: "Reply with <code>send to reviewer</code>, <code>override</code>, or <code>reviewed</code> after the blocking review is complete.",
|
|
1929
1977
|
remember: {
|
|
1930
1978
|
text: String(continuation.lastPrompt || "").trim() || "Reply with override or reviewed.",
|
|
1931
1979
|
kind: continuation.kind,
|
|
@@ -2631,7 +2679,7 @@ class TelegramGateway {
|
|
|
2631
2679
|
}
|
|
2632
2680
|
|
|
2633
2681
|
async runPromptViaBridge(message, sessionId, promptText, options = {}) {
|
|
2634
|
-
const host = await this.getLiveBridgeHost();
|
|
2682
|
+
const host = options.targetHost || await this.getLiveBridgeHost();
|
|
2635
2683
|
if (!host) {
|
|
2636
2684
|
return null;
|
|
2637
2685
|
}
|
|
@@ -2650,6 +2698,9 @@ class TelegramGateway {
|
|
|
2650
2698
|
sessionId: String(sessionId || "").trim(),
|
|
2651
2699
|
text: String(promptText || "").trim(),
|
|
2652
2700
|
explicitExecution: options.explicitExecution === true,
|
|
2701
|
+
targetPid: Number(host?.pid || 0),
|
|
2702
|
+
targetSessionId: String(host?.sessionId || "").trim(),
|
|
2703
|
+
targetOwnerId: String(host?.ownerId || "").trim(),
|
|
2653
2704
|
runtimeProfile: String(project?.runtimeProfile || "").trim(),
|
|
2654
2705
|
replyToMessageId: message.message_id,
|
|
2655
2706
|
requestedAt: new Date().toISOString(),
|
|
@@ -2678,7 +2729,11 @@ class TelegramGateway {
|
|
|
2678
2729
|
}
|
|
2679
2730
|
|
|
2680
2731
|
const activeHost = nextBridge.activeHost || {};
|
|
2681
|
-
|
|
2732
|
+
const knownHosts = Array.isArray(nextBridge.hosts) ? nextBridge.hosts : [];
|
|
2733
|
+
if (Number(host?.pid || 0) > 0 && !knownHosts.some((item) => Number(item?.pid || 0) === Number(host.pid || 0))) {
|
|
2734
|
+
break;
|
|
2735
|
+
}
|
|
2736
|
+
if (Number(activeHost.pid || 0) <= 0 && !(Number(host?.pid || 0) > 0)) {
|
|
2682
2737
|
break;
|
|
2683
2738
|
}
|
|
2684
2739
|
}
|
|
@@ -3797,12 +3852,26 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3797
3852
|
if (blockingReview) {
|
|
3798
3853
|
const meta = blockingReview.meta && typeof blockingReview.meta === "object" ? blockingReview.meta : {};
|
|
3799
3854
|
const reviewerName = String(meta.ownerName || meta.agentId || "assigned reviewer").trim();
|
|
3800
|
-
const
|
|
3855
|
+
const reviewerAgent = listProjectAgents(operatorGate.project).find((agent) => String(agent?.id || "").trim() === String(meta.agentId || "").trim()) || null;
|
|
3856
|
+
const liveHosts = await this.getLiveBridgeHosts({ cwd: operatorGate.session?.cwd || this.cwd });
|
|
3857
|
+
const reviewerHost = reviewerAgent ? findLiveHostForAgent(liveHosts, reviewerAgent) : null;
|
|
3858
|
+
const reviewPrompt = [
|
|
3859
|
+
`Review this pending Telegram Roundtable request before execution.`,
|
|
3860
|
+
`Requester: ${this.describeTelegramUser(message?.from || {}).displayName || userId}`,
|
|
3861
|
+
`Project: ${operatorGate.project.projectName || path.basename(operatorGate.session?.cwd || this.cwd)}`,
|
|
3862
|
+
"",
|
|
3863
|
+
`Pending request:`,
|
|
3864
|
+
promptText
|
|
3865
|
+
].join("\n");
|
|
3866
|
+
const followUp = reviewerHost
|
|
3867
|
+
? `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.`
|
|
3868
|
+
: `Blocking review is assigned to ${reviewerName}. Reply with override to proceed anyway, or reviewed when the review is done.`;
|
|
3801
3869
|
await this.sendMarkup(
|
|
3802
3870
|
message.chat.id,
|
|
3803
3871
|
[
|
|
3804
3872
|
"<b>Blocking review in effect</b>",
|
|
3805
3873
|
`reviewer: <code>${escapeTelegramHtml(reviewerName)}</code>`,
|
|
3874
|
+
reviewerHost ? `reviewer terminal: <code>${escapeTelegramHtml(formatBridgeHostLabel(reviewerHost) || reviewerHost.sessionId || "live terminal")}</code>` : "",
|
|
3806
3875
|
"Execution is paused until the blocking review is cleared or overridden.",
|
|
3807
3876
|
followUp
|
|
3808
3877
|
].join("\n"),
|
|
@@ -3813,12 +3882,27 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3813
3882
|
source: "review-policy-gate",
|
|
3814
3883
|
context: {
|
|
3815
3884
|
agentId: String(meta.agentId || "").trim(),
|
|
3816
|
-
ownerName: reviewerName
|
|
3885
|
+
ownerName: reviewerName,
|
|
3886
|
+
reviewPrompt,
|
|
3887
|
+
reviewerHost: reviewerHost
|
|
3888
|
+
? {
|
|
3889
|
+
pid: Number(reviewerHost.pid || 0),
|
|
3890
|
+
sessionId: String(reviewerHost.sessionId || "").trim(),
|
|
3891
|
+
ownerId: String(reviewerHost.ownerId || "").trim(),
|
|
3892
|
+
ownerName: String(reviewerHost.ownerName || "").trim(),
|
|
3893
|
+
label: String(reviewerHost.label || "").trim(),
|
|
3894
|
+
surface: String(reviewerHost.surface || "").trim(),
|
|
3895
|
+
provider: String(reviewerHost.provider || "").trim(),
|
|
3896
|
+
model: String(reviewerHost.model || "").trim(),
|
|
3897
|
+
runtimeProfile: String(reviewerHost.runtimeProfile || "").trim()
|
|
3898
|
+
}
|
|
3899
|
+
: null
|
|
3817
3900
|
},
|
|
3818
3901
|
force: true
|
|
3819
3902
|
});
|
|
3820
3903
|
return;
|
|
3821
3904
|
}
|
|
3905
|
+
const liveHosts = await this.getLiveBridgeHosts({ cwd: operatorGate.session?.cwd || this.cwd });
|
|
3822
3906
|
const host = await this.getLiveBridgeHost();
|
|
3823
3907
|
const activeExecutor = {
|
|
3824
3908
|
surface: host?.surface || (host ? "live-tui" : "telegram-fallback"),
|
|
@@ -3831,10 +3915,12 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3831
3915
|
label: host?.label || ""
|
|
3832
3916
|
};
|
|
3833
3917
|
const selectedExecutor = chooseExecutorAgent(operatorGate.project, activeExecutor);
|
|
3918
|
+
const selectedLiveHost = selectedExecutor ? findLiveHostForAgent(liveHosts, selectedExecutor) : null;
|
|
3834
3919
|
if (
|
|
3835
3920
|
selectedExecutor?.ownerId
|
|
3836
3921
|
&& activeExecutor.ownerId
|
|
3837
3922
|
&& String(selectedExecutor.ownerId).trim() !== String(activeExecutor.ownerId).trim()
|
|
3923
|
+
&& !selectedLiveHost
|
|
3838
3924
|
) {
|
|
3839
3925
|
const selectedName = String(selectedExecutor.ownerName || selectedExecutor.label || selectedExecutor.ownerId || "selected executor").trim();
|
|
3840
3926
|
const currentName = String(activeExecutor.ownerName || activeExecutor.label || activeExecutor.ownerId || "current terminal").trim();
|
|
@@ -3871,7 +3957,10 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3871
3957
|
return;
|
|
3872
3958
|
}
|
|
3873
3959
|
previewMessage = await this.sendProgressMessage(message.chat.id, message.message_id);
|
|
3874
|
-
const content = (await this.runPromptViaBridge(message, sessionId, promptText, {
|
|
3960
|
+
const content = (await this.runPromptViaBridge(message, sessionId, promptText, {
|
|
3961
|
+
explicitExecution: shouldExecutePrompt,
|
|
3962
|
+
targetHost: selectedLiveHost || host || null
|
|
3963
|
+
}))
|
|
3875
3964
|
?? (await this.runPromptFallback(sessionId, promptText));
|
|
3876
3965
|
await this.deliverPromptResult(message.chat.id, message.message_id, previewMessage, content);
|
|
3877
3966
|
await this.rememberContinuation(message, content);
|