@tritard/waterbrother 0.16.73 → 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 +92 -6
- 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);
|
|
@@ -1293,9 +1293,15 @@ class TelegramGateway {
|
|
|
1293
1293
|
}
|
|
1294
1294
|
|
|
1295
1295
|
async rememberContinuation(message, text = "") {
|
|
1296
|
+
return this.rememberContinuationWithOptions(message, text);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
async rememberContinuationWithOptions(message, text = "", extra = {}) {
|
|
1296
1300
|
const body = String(text || "").trim();
|
|
1297
1301
|
if (!body) return;
|
|
1298
|
-
const asksFollowUp =
|
|
1302
|
+
const asksFollowUp = extra.force === true
|
|
1303
|
+
? true
|
|
1304
|
+
:
|
|
1299
1305
|
/\?\s*$/.test(body)
|
|
1300
1306
|
|| /\b(would you like|do you want|which\b|what\b.*\?|how\b.*\?|who\b.*\?|where\b.*\?)\b/i.test(body);
|
|
1301
1307
|
if (!asksFollowUp) return;
|
|
@@ -1304,8 +1310,9 @@ class TelegramGateway {
|
|
|
1304
1310
|
chatId: String(message?.chat?.id || "").trim(),
|
|
1305
1311
|
userId: String(message?.from?.id || "").trim(),
|
|
1306
1312
|
lastPrompt: body.slice(0, 400),
|
|
1307
|
-
kind: "follow-up",
|
|
1308
|
-
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 } : {},
|
|
1309
1316
|
expiresAt: new Date(Date.now() + TELEGRAM_CONTINUATION_TTL_MS).toISOString()
|
|
1310
1317
|
};
|
|
1311
1318
|
await this.persistState();
|
|
@@ -1698,6 +1705,9 @@ class TelegramGateway {
|
|
|
1698
1705
|
: intent.action === "agent-execute"
|
|
1699
1706
|
? "This sets the room executor role. Claim/mode rules still control actual execution."
|
|
1700
1707
|
: "";
|
|
1708
|
+
const followUp = intent.action === "agent-review"
|
|
1709
|
+
? `Should ${targetAgent.ownerName || targetAgent.label || "that terminal"} review be advisory or blocking?`
|
|
1710
|
+
: "";
|
|
1701
1711
|
return {
|
|
1702
1712
|
kind: "agent",
|
|
1703
1713
|
project: nextProject,
|
|
@@ -1708,11 +1718,69 @@ class TelegramGateway {
|
|
|
1708
1718
|
`role: <code>${escapeTelegramHtml(intent.role)}</code>`,
|
|
1709
1719
|
`runtime: <code>${escapeTelegramHtml(runtimeLabel)}</code>`,
|
|
1710
1720
|
`project: <code>${escapeTelegramHtml(nextProject.projectName || path.basename(session.cwd || this.cwd))}</code>`,
|
|
1711
|
-
note
|
|
1712
|
-
|
|
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
|
|
1713
1736
|
};
|
|
1714
1737
|
}
|
|
1715
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
|
+
|
|
1716
1784
|
async handleStateIntent(message, sessionId, intent) {
|
|
1717
1785
|
const { session, project } = await this.bindSharedRoomForMessage(message, sessionId);
|
|
1718
1786
|
const cwd = session.cwd || this.cwd;
|
|
@@ -3195,6 +3263,21 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3195
3263
|
const isExplicitRun = text.startsWith("/run ");
|
|
3196
3264
|
const rawPromptText = this.stripBotMention(isExplicitRun ? text.replace("/run", "").trim() : text);
|
|
3197
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
|
+
}
|
|
3198
3281
|
const groupIntent = this.isGroupChat(message)
|
|
3199
3282
|
? (isExplicitRun
|
|
3200
3283
|
? { kind: "execution", reason: "explicit /run" }
|
|
@@ -3244,6 +3327,9 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
|
|
|
3244
3327
|
try {
|
|
3245
3328
|
const result = await this.handleConversationalAgentIntent(message, sessionId, agentIntent);
|
|
3246
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
|
+
}
|
|
3247
3333
|
} catch (error) {
|
|
3248
3334
|
await this.sendMessage(message.chat.id, escapeTelegramHtml(error instanceof Error ? error.message : String(error)), message.message_id);
|
|
3249
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);
|