@tritard/waterbrother 0.16.52 → 0.16.54

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/gateway.js +34 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.52",
3
+ "version": "0.16.54",
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": {
package/src/gateway.js CHANGED
@@ -1043,17 +1043,50 @@ class TelegramGateway {
1043
1043
  if (!value) {
1044
1044
  throw new Error("member target is required");
1045
1045
  }
1046
+ const known = this.listKnownChatPeople(message);
1047
+ const replied = this.describeTelegramUser(message?.reply_to_message?.from || {});
1048
+ const normalizedPronoun = value.toLowerCase().replace(/[?.!]+$/g, "").trim();
1049
+ if (["him", "her", "them", "this person", "this user", "that person", "that user"].includes(normalizedPronoun)) {
1050
+ if (replied.userId) {
1051
+ const knownReply = known.find((person) => person.userId === replied.userId) || replied;
1052
+ return { userId: replied.userId, displayName: knownReply.displayName || replied.displayName || replied.userId };
1053
+ }
1054
+ const senderId = String(message?.from?.id || "").trim();
1055
+ const fallbackCandidates = known.filter((person) => {
1056
+ const userId = String(person.userId || "").trim();
1057
+ if (!userId || userId === senderId) return false;
1058
+ if (String(person.usernameHandle || "").trim().toLowerCase() === String(this.me?.username || "").trim().toLowerCase()) return false;
1059
+ return true;
1060
+ });
1061
+ if (fallbackCandidates.length === 1) {
1062
+ return {
1063
+ userId: fallbackCandidates[0].userId,
1064
+ displayName: fallbackCandidates[0].displayName || fallbackCandidates[0].userId
1065
+ };
1066
+ }
1067
+ throw new Error("Reply to the person's message first, or use /people to choose them by name or id.");
1068
+ }
1046
1069
  if (/^\d+$/.test(value) || value.startsWith("@")) {
1047
1070
  const match = this.resolveInviteTarget(message, value);
1048
1071
  return { userId: match.userId, displayName: match.displayName };
1049
1072
  }
1050
1073
 
1051
1074
  const normalized = value.toLowerCase();
1052
- const known = this.listKnownChatPeople(message);
1053
1075
  const exact = known.find((person) => String(person.displayName || "").trim().toLowerCase() === normalized);
1054
1076
  if (exact) {
1055
1077
  return { userId: exact.userId, displayName: exact.displayName || exact.userId };
1056
1078
  }
1079
+ const byFirstName = known.filter((person) => {
1080
+ const display = String(person.displayName || "").trim().toLowerCase();
1081
+ const first = display.split(/\s+/).filter(Boolean)[0] || "";
1082
+ return first === normalized;
1083
+ });
1084
+ if (byFirstName.length === 1) {
1085
+ return { userId: byFirstName[0].userId, displayName: byFirstName[0].displayName || byFirstName[0].userId };
1086
+ }
1087
+ if (byFirstName.length > 1) {
1088
+ throw new Error(`Multiple Telegram users matched first name ${value}. Use /people and choose by id or full name.`);
1089
+ }
1057
1090
  const partial = known.filter((person) => {
1058
1091
  const display = String(person.displayName || "").trim().toLowerCase();
1059
1092
  const handle = String(person.usernameHandle || "").trim().toLowerCase();