@tritard/waterbrother 0.16.63 → 0.16.64

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.63",
3
+ "version": "0.16.64",
4
4
  "description": "Waterbrother: bring-your-own-model coding CLI with local tools, sessions, operator modes, and approval controls",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,11 +18,24 @@ function gatewayBridgePath(serviceId) {
18
18
  }
19
19
 
20
20
  function normalizeGatewayState(parsed = {}) {
21
+ const continuations = parsed?.continuations && typeof parsed.continuations === "object" ? parsed.continuations : {};
21
22
  return {
22
23
  offset: Number.isFinite(Number(parsed?.offset)) ? Math.max(0, Math.floor(Number(parsed.offset))) : 0,
23
24
  peers: parsed?.peers && typeof parsed.peers === "object" ? parsed.peers : {},
24
25
  pendingPairings: parsed?.pendingPairings && typeof parsed.pendingPairings === "object" ? parsed.pendingPairings : {},
25
- continuations: parsed?.continuations && typeof parsed.continuations === "object" ? parsed.continuations : {}
26
+ continuations: Object.fromEntries(
27
+ Object.entries(continuations).map(([key, item]) => [
28
+ key,
29
+ {
30
+ chatId: String(item?.chatId || "").trim(),
31
+ userId: String(item?.userId || "").trim(),
32
+ lastPrompt: String(item?.lastPrompt || "").trim(),
33
+ kind: String(item?.kind || "follow-up").trim() || "follow-up",
34
+ source: String(item?.source || "assistant-question").trim() || "assistant-question",
35
+ expiresAt: String(item?.expiresAt || "").trim()
36
+ }
37
+ ])
38
+ )
26
39
  };
27
40
  }
28
41
 
package/src/gateway.js CHANGED
@@ -754,6 +754,16 @@ function classifyTelegramGroupIntent(text = "") {
754
754
  return { kind: "chat", reason: "default non-execution" };
755
755
  }
756
756
 
757
+ function buildContinuationPrompt(text = "", continuation = null) {
758
+ const body = String(text || "").trim();
759
+ if (!continuation?.lastPrompt) return body;
760
+ return [
761
+ `Continue the active Telegram follow-up.`,
762
+ `Your last question/request to this user was: ${continuation.lastPrompt}`,
763
+ `User reply: ${body}`
764
+ ].join("\n");
765
+ }
766
+
757
767
  function suggestTaskText(text = "") {
758
768
  return String(text || "")
759
769
  .replace(/^@[\w_]+\s*/g, "")
@@ -991,6 +1001,15 @@ class TelegramGateway {
991
1001
  return Number.isFinite(expiresAtMs) && expiresAtMs > Date.now();
992
1002
  }
993
1003
 
1004
+ getPendingContinuation(message) {
1005
+ const key = continuationKey(message?.chat?.id, message?.from?.id);
1006
+ const item = this.state?.continuations?.[key];
1007
+ if (!item) return null;
1008
+ const expiresAtMs = Date.parse(String(item.expiresAt || ""));
1009
+ if (!Number.isFinite(expiresAtMs) || expiresAtMs <= Date.now()) return null;
1010
+ return item;
1011
+ }
1012
+
994
1013
  clearContinuation(message) {
995
1014
  const key = continuationKey(message?.chat?.id, message?.from?.id);
996
1015
  if (this.state?.continuations?.[key]) {
@@ -1010,6 +1029,8 @@ class TelegramGateway {
1010
1029
  chatId: String(message?.chat?.id || "").trim(),
1011
1030
  userId: String(message?.from?.id || "").trim(),
1012
1031
  lastPrompt: body.slice(0, 400),
1032
+ kind: "follow-up",
1033
+ source: "assistant-question",
1013
1034
  expiresAt: new Date(Date.now() + TELEGRAM_CONTINUATION_TTL_MS).toISOString()
1014
1035
  };
1015
1036
  await this.persistState();
@@ -1102,7 +1123,8 @@ class TelegramGateway {
1102
1123
  project.activeOperator?.id
1103
1124
  ? `active operator: <code>${escapeTelegramHtml(project.activeOperator.name || project.activeOperator.id)}</code>`
1104
1125
  : "active operator: <code>none</code>",
1105
- "Ask naturally here.",
1126
+ "As observer, you can ask questions, discuss plans, and review work here.",
1127
+ "An owner can promote you to editor or owner conversationally.",
1106
1128
  "Examples: <code>what project is this chat bound to?</code>, <code>who is in the room?</code>, <code>what can I do here?</code>"
1107
1129
  ].join("\n");
1108
1130
  }
@@ -1985,9 +2007,10 @@ class TelegramGateway {
1985
2007
  if (trustedOnboarding) {
1986
2008
  await this.sendMarkup(message.chat.id, this.buildTrustedRoomIntroMarkup(trustedOnboarding), message.message_id);
1987
2009
  }
1988
- this.clearContinuation(message);
1989
2010
 
1990
2011
  const text = this.normalizeTelegramCommandText(String(message.text || "").trim());
2012
+ const continuation = this.getPendingContinuation(message);
2013
+ this.clearContinuation(message);
1991
2014
  const userId = String(message.from.id);
1992
2015
 
1993
2016
  if (text === "/help" || text === "/start") {
@@ -2706,9 +2729,14 @@ Ask them to run <code>/whoami</code> and then <code>/accept-invite ${escapeTeleg
2706
2729
  }
2707
2730
 
2708
2731
  const isExplicitRun = text.startsWith("/run ");
2709
- const promptText = this.stripBotMention(isExplicitRun ? text.replace("/run", "").trim() : text);
2732
+ const rawPromptText = this.stripBotMention(isExplicitRun ? text.replace("/run", "").trim() : text);
2733
+ const promptText = continuation ? buildContinuationPrompt(rawPromptText, continuation) : rawPromptText;
2710
2734
  const groupIntent = this.isGroupChat(message)
2711
- ? (isExplicitRun ? { kind: "execution", reason: "explicit /run" } : classifyTelegramGroupIntent(promptText))
2735
+ ? (isExplicitRun
2736
+ ? { kind: "execution", reason: "explicit /run" }
2737
+ : continuation
2738
+ ? { kind: "execution", reason: "follow-up continuation" }
2739
+ : classifyTelegramGroupIntent(rawPromptText))
2712
2740
  : { kind: "execution", reason: "private chat" };
2713
2741
  const shouldExecutePrompt = !this.isGroupChat(message) || groupIntent.kind === "execution";
2714
2742
  const sharedBinding = await this.bindSharedRoomForMessage(message, sessionId);