@tritard/waterbrother 0.16.72 → 0.16.74
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-state.js +1 -0
- package/src/gateway.js +98 -8
- package/src/shared-project.js +16 -0
package/package.json
CHANGED
package/src/gateway-state.js
CHANGED
|
@@ -35,6 +35,7 @@ function normalizeGatewayState(parsed = {}) {
|
|
|
35
35
|
lastPrompt: String(item?.lastPrompt || "").trim(),
|
|
36
36
|
kind: String(item?.kind || "follow-up").trim() || "follow-up",
|
|
37
37
|
source: String(item?.source || "assistant-question").trim() || "assistant-question",
|
|
38
|
+
context: item?.context && typeof item.context === "object" ? { ...item.context } : {},
|
|
38
39
|
expiresAt: String(item?.expiresAt || "").trim()
|
|
39
40
|
}
|
|
40
41
|
])
|
package/src/gateway.js
CHANGED
|
@@ -9,7 +9,7 @@ import { DEFAULT_PENDING_PAIRING_TTL_MINUTES, loadGatewayBridge, loadGatewayStat
|
|
|
9
9
|
import { getGatewayStatus, getChannelSpec } from "./channels.js";
|
|
10
10
|
import { getConfigPath, loadConfigLayers, saveConfig } from "./config.js";
|
|
11
11
|
import { canonicalizeLoosePath } from "./path-utils.js";
|
|
12
|
-
import { acceptSharedInvite, addSharedTask, approveSharedInvite, assignSharedTask, claimSharedOperator, claimSharedTask, commentSharedTask, createSharedInvite, enableSharedProject, getSharedTaskHistory, listSharedEvents, listSharedInvites, listSharedTasks, loadSharedProject, moveSharedTask, rejectSharedInvite, releaseSharedOperator, setSharedRoom, setSharedRoomMode, setSharedRuntimeProfile, removeSharedMember, upsertSharedAgent, upsertSharedMember } from "./shared-project.js";
|
|
12
|
+
import { acceptSharedInvite, addSharedRoomNote, addSharedTask, approveSharedInvite, assignSharedTask, claimSharedOperator, claimSharedTask, commentSharedTask, createSharedInvite, enableSharedProject, getSharedTaskHistory, listSharedEvents, listSharedInvites, listSharedTasks, loadSharedProject, moveSharedTask, rejectSharedInvite, releaseSharedOperator, setSharedRoom, setSharedRoomMode, setSharedRuntimeProfile, removeSharedMember, upsertSharedAgent, upsertSharedMember } from "./shared-project.js";
|
|
13
13
|
import { buildSelfAwarenessManifest, formatAboutWaterbrother, formatSelfState, resolveLocalConceptQuestion } from "./self-awareness.js";
|
|
14
14
|
|
|
15
15
|
const execFileAsync = promisify(execFile);
|
|
@@ -582,7 +582,9 @@ function parseTelegramAgentIntent(text = "") {
|
|
|
582
582
|
if (/^(how|what|why|when|where|who)\b/.test(lowered)) return null;
|
|
583
583
|
const reviewPatterns = [
|
|
584
584
|
/^(?:have|ask|use)\s+(.+?)['’]s\s+(?:bot|terminal)\s+(?:to\s+)?review(?:\s+this)?\s*$/i,
|
|
585
|
-
/^(?:have|ask|use)\s+(.+?)\s+(?:bot|terminal)\s+(?:to\s+)?review(?:\s+this)?\s*$/i
|
|
585
|
+
/^(?:have|ask|use)\s+(.+?)\s+(?:bot|terminal)\s+(?:to\s+)?review(?:\s+this)?\s*$/i,
|
|
586
|
+
/^(.+?),\s*review(?:\s+this)?\s+with\s+(?:your|ur)\s+(?:bot|terminal)\s*$/i,
|
|
587
|
+
/^(.+?)\s+should\s+review(?:\s+this)?\s+with\s+(?:their|his|her)\s+(?:bot|terminal)\s*$/i
|
|
586
588
|
];
|
|
587
589
|
for (const pattern of reviewPatterns) {
|
|
588
590
|
const match = value.match(pattern);
|
|
@@ -597,7 +599,9 @@ function parseTelegramAgentIntent(text = "") {
|
|
|
597
599
|
/^(?:have|ask|use)\s+(.+?)['’]s\s+(?:bot|terminal)\s+(?:to\s+)?(?:take execution|execute|handle execution|take this)\s*$/i,
|
|
598
600
|
/^(?:have|ask|use)\s+(.+?)\s+(?:bot|terminal)\s+(?:to\s+)?(?:take execution|execute|handle execution|take this)\s*$/i,
|
|
599
601
|
/^(.+?)['’]s\s+(?:bot|terminal)\s+should\s+(?:take execution|execute|handle execution)\s*$/i,
|
|
600
|
-
/^(.+?)\s+(?:bot|terminal)\s+should\s+(?:take execution|execute|handle execution)\s*$/i
|
|
602
|
+
/^(.+?)\s+(?:bot|terminal)\s+should\s+(?:take execution|execute|handle execution)\s*$/i,
|
|
603
|
+
/^(.+?),\s*(?:take execution|execute|handle execution|take this)\s+with\s+(?:your|ur)\s+(?:bot|terminal)\s*$/i,
|
|
604
|
+
/^(.+?)\s+should\s+(?:take execution|execute|handle execution)\s+with\s+(?:their|his|her)\s+(?:bot|terminal)\s*$/i
|
|
601
605
|
];
|
|
602
606
|
for (const pattern of executePatterns) {
|
|
603
607
|
const match = value.match(pattern);
|
|
@@ -1289,9 +1293,15 @@ class TelegramGateway {
|
|
|
1289
1293
|
}
|
|
1290
1294
|
|
|
1291
1295
|
async rememberContinuation(message, text = "") {
|
|
1296
|
+
return this.rememberContinuationWithOptions(message, text);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
async rememberContinuationWithOptions(message, text = "", extra = {}) {
|
|
1292
1300
|
const body = String(text || "").trim();
|
|
1293
1301
|
if (!body) return;
|
|
1294
|
-
const asksFollowUp =
|
|
1302
|
+
const asksFollowUp = extra.force === true
|
|
1303
|
+
? true
|
|
1304
|
+
:
|
|
1295
1305
|
/\?\s*$/.test(body)
|
|
1296
1306
|
|| /\b(would you like|do you want|which\b|what\b.*\?|how\b.*\?|who\b.*\?|where\b.*\?)\b/i.test(body);
|
|
1297
1307
|
if (!asksFollowUp) return;
|
|
@@ -1300,8 +1310,9 @@ class TelegramGateway {
|
|
|
1300
1310
|
chatId: String(message?.chat?.id || "").trim(),
|
|
1301
1311
|
userId: String(message?.from?.id || "").trim(),
|
|
1302
1312
|
lastPrompt: body.slice(0, 400),
|
|
1303
|
-
kind: "follow-up",
|
|
1304
|
-
source: "assistant-question",
|
|
1313
|
+
kind: String(extra.kind || "follow-up").trim() || "follow-up",
|
|
1314
|
+
source: String(extra.source || "assistant-question").trim() || "assistant-question",
|
|
1315
|
+
context: extra.context && typeof extra.context === "object" ? { ...extra.context } : {},
|
|
1305
1316
|
expiresAt: new Date(Date.now() + TELEGRAM_CONTINUATION_TTL_MS).toISOString()
|
|
1306
1317
|
};
|
|
1307
1318
|
await this.persistState();
|
|
@@ -1694,6 +1705,9 @@ class TelegramGateway {
|
|
|
1694
1705
|
: intent.action === "agent-execute"
|
|
1695
1706
|
? "This sets the room executor role. Claim/mode rules still control actual execution."
|
|
1696
1707
|
: "";
|
|
1708
|
+
const followUp = intent.action === "agent-review"
|
|
1709
|
+
? `Should ${targetAgent.ownerName || targetAgent.label || "that terminal"} review be advisory or blocking?`
|
|
1710
|
+
: "";
|
|
1697
1711
|
return {
|
|
1698
1712
|
kind: "agent",
|
|
1699
1713
|
project: nextProject,
|
|
@@ -1704,11 +1718,69 @@ class TelegramGateway {
|
|
|
1704
1718
|
`role: <code>${escapeTelegramHtml(intent.role)}</code>`,
|
|
1705
1719
|
`runtime: <code>${escapeTelegramHtml(runtimeLabel)}</code>`,
|
|
1706
1720
|
`project: <code>${escapeTelegramHtml(nextProject.projectName || path.basename(session.cwd || this.cwd))}</code>`,
|
|
1707
|
-
note
|
|
1708
|
-
|
|
1721
|
+
note,
|
|
1722
|
+
followUp
|
|
1723
|
+
].filter(Boolean).join("\n"),
|
|
1724
|
+
continuation: followUp
|
|
1725
|
+
? {
|
|
1726
|
+
text: followUp,
|
|
1727
|
+
kind: "agent-review-policy",
|
|
1728
|
+
source: "agent-delegation",
|
|
1729
|
+
context: {
|
|
1730
|
+
agentId: String(targetAgent.id || "").trim(),
|
|
1731
|
+
ownerName: String(targetAgent.ownerName || targetAgent.label || "").trim(),
|
|
1732
|
+
projectName: String(nextProject.projectName || "").trim()
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
: null
|
|
1709
1736
|
};
|
|
1710
1737
|
}
|
|
1711
1738
|
|
|
1739
|
+
async handleStructuredContinuation(message, sessionId, continuation, peer) {
|
|
1740
|
+
if (!continuation?.kind) return null;
|
|
1741
|
+
const reply = this.stripBotMention(String(message?.text || "").trim()).toLowerCase();
|
|
1742
|
+
if (continuation.kind === "agent-review-policy") {
|
|
1743
|
+
const policy = /\b(blocking|block|required|gate)\b/.test(reply)
|
|
1744
|
+
? "blocking"
|
|
1745
|
+
: /\b(advisory|advice|optional|non-blocking|nonblocking)\b/.test(reply)
|
|
1746
|
+
? "advisory"
|
|
1747
|
+
: "";
|
|
1748
|
+
if (!policy) {
|
|
1749
|
+
return {
|
|
1750
|
+
markup: `Reply with <code>advisory</code> or <code>blocking</code> for ${escapeTelegramHtml(continuation.context?.ownerName || "that review")}.`,
|
|
1751
|
+
remember: {
|
|
1752
|
+
text: String(continuation.lastPrompt || "").trim() || "Should this review be advisory or blocking?",
|
|
1753
|
+
kind: continuation.kind,
|
|
1754
|
+
source: continuation.source,
|
|
1755
|
+
context: continuation.context || {},
|
|
1756
|
+
force: true
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
const session = await loadSession(sessionId);
|
|
1761
|
+
const actorId = String(message?.from?.id || "").trim();
|
|
1762
|
+
const actorName = peer?.username || [message?.from?.first_name, message?.from?.last_name].filter(Boolean).join(" ").trim() || actorId;
|
|
1763
|
+
await addSharedRoomNote(session.cwd || this.cwd, `${continuation.context?.ownerName || "Assigned"} review should be ${policy}`, {
|
|
1764
|
+
actorId,
|
|
1765
|
+
actorName,
|
|
1766
|
+
type: "review-policy",
|
|
1767
|
+
meta: {
|
|
1768
|
+
agentId: String(continuation.context?.agentId || "").trim(),
|
|
1769
|
+
policy
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
return {
|
|
1773
|
+
markup: [
|
|
1774
|
+
"<b>Review policy noted</b>",
|
|
1775
|
+
`reviewer: <code>${escapeTelegramHtml(continuation.context?.ownerName || "unknown")}</code>`,
|
|
1776
|
+
`policy: <code>${escapeTelegramHtml(policy)}</code>`,
|
|
1777
|
+
"This is recorded in the room as coordination guidance. Automatic enforcement is not wired yet."
|
|
1778
|
+
].join("\n")
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
return null;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1712
1784
|
async handleStateIntent(message, sessionId, intent) {
|
|
1713
1785
|
const { session, project } = await this.bindSharedRoomForMessage(message, sessionId);
|
|
1714
1786
|
const cwd = session.cwd || this.cwd;
|
|
@@ -3191,6 +3263,21 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3191
3263
|
const isExplicitRun = text.startsWith("/run ");
|
|
3192
3264
|
const rawPromptText = this.stripBotMention(isExplicitRun ? text.replace("/run", "").trim() : text);
|
|
3193
3265
|
const promptText = continuation ? buildContinuationPrompt(rawPromptText, continuation) : rawPromptText;
|
|
3266
|
+
if (continuation?.kind && continuation.kind !== "follow-up") {
|
|
3267
|
+
try {
|
|
3268
|
+
const structured = await this.handleStructuredContinuation(message, sessionId, continuation, peer);
|
|
3269
|
+
if (structured?.markup) {
|
|
3270
|
+
await this.sendMarkup(message.chat.id, structured.markup, message.message_id);
|
|
3271
|
+
if (structured.remember) {
|
|
3272
|
+
await this.rememberContinuationWithOptions(message, structured.remember.text, structured.remember);
|
|
3273
|
+
}
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
} catch (error) {
|
|
3277
|
+
await this.sendMessage(message.chat.id, escapeTelegramHtml(error instanceof Error ? error.message : String(error)), message.message_id);
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3194
3281
|
const groupIntent = this.isGroupChat(message)
|
|
3195
3282
|
? (isExplicitRun
|
|
3196
3283
|
? { kind: "execution", reason: "explicit /run" }
|
|
@@ -3240,6 +3327,9 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3240
3327
|
try {
|
|
3241
3328
|
const result = await this.handleConversationalAgentIntent(message, sessionId, agentIntent);
|
|
3242
3329
|
await this.sendMarkup(message.chat.id, result.markup, message.message_id);
|
|
3330
|
+
if (result.continuation) {
|
|
3331
|
+
await this.rememberContinuationWithOptions(message, result.continuation.text, { ...result.continuation, force: true });
|
|
3332
|
+
}
|
|
3243
3333
|
} catch (error) {
|
|
3244
3334
|
await this.sendMessage(message.chat.id, escapeTelegramHtml(error instanceof Error ? error.message : String(error)), message.message_id);
|
|
3245
3335
|
}
|
package/src/shared-project.js
CHANGED
|
@@ -663,6 +663,22 @@ export async function upsertSharedAgent(cwd, agent = {}, options = {}) {
|
|
|
663
663
|
})).project;
|
|
664
664
|
}
|
|
665
665
|
|
|
666
|
+
export async function addSharedRoomNote(cwd, text = "", options = {}) {
|
|
667
|
+
const existing = await loadSharedProject(cwd);
|
|
668
|
+
requireSharedProject(existing);
|
|
669
|
+
const normalizedText = String(text || "").trim();
|
|
670
|
+
if (!normalizedText) throw new Error("room note text is required");
|
|
671
|
+
if (options.actorId) {
|
|
672
|
+
requireMember(existing, options.actorId);
|
|
673
|
+
}
|
|
674
|
+
return (await recordSharedProjectEvent(cwd, existing, normalizedText, {
|
|
675
|
+
type: String(options.type || "room-note").trim() || "room-note",
|
|
676
|
+
actorId: String(options.actorId || "").trim(),
|
|
677
|
+
actorName: String(options.actorName || "").trim(),
|
|
678
|
+
meta: options.meta && typeof options.meta === "object" ? options.meta : {}
|
|
679
|
+
})).project;
|
|
680
|
+
}
|
|
681
|
+
|
|
666
682
|
export async function createSharedInvite(cwd, member = {}, options = {}) {
|
|
667
683
|
const existing = await loadSharedProject(cwd);
|
|
668
684
|
requireOwner(existing, options.actorId);
|