@tritard/waterbrother 0.16.95 → 0.16.97
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.js +26 -4
- package/src/shared-project.js +48 -9
package/package.json
CHANGED
package/src/gateway.js
CHANGED
|
@@ -385,7 +385,27 @@ function listProjectParticipants(project) {
|
|
|
385
385
|
}
|
|
386
386
|
|
|
387
387
|
function listProjectAgents(project) {
|
|
388
|
-
|
|
388
|
+
const agents = Array.isArray(project?.agents) ? project.agents : [];
|
|
389
|
+
const seen = new Set();
|
|
390
|
+
return agents.filter((agent) => {
|
|
391
|
+
const ownerId = String(agent?.ownerId || "").trim();
|
|
392
|
+
const ownerName = String(agent?.ownerName || "").trim();
|
|
393
|
+
const label = String(agent?.label || "").trim();
|
|
394
|
+
const runtime = agent?.provider && agent?.model ? `${agent.provider}/${agent.model}` : "";
|
|
395
|
+
if (!String(agent?.id || "").trim()) return false;
|
|
396
|
+
if (!ownerId && !ownerName && (!label || /\bundefined\b/i.test(label))) return false;
|
|
397
|
+
const key = [
|
|
398
|
+
ownerId,
|
|
399
|
+
ownerName.toLowerCase(),
|
|
400
|
+
label.replace(/\bundefined\b/ig, "").trim().toLowerCase(),
|
|
401
|
+
String(agent?.role || "").trim().toLowerCase(),
|
|
402
|
+
String(agent?.surface || "").trim().toLowerCase(),
|
|
403
|
+
runtime.toLowerCase()
|
|
404
|
+
].join("|");
|
|
405
|
+
if (seen.has(key)) return false;
|
|
406
|
+
seen.add(key);
|
|
407
|
+
return true;
|
|
408
|
+
});
|
|
389
409
|
}
|
|
390
410
|
|
|
391
411
|
function findProjectParticipant(project, memberId = "") {
|
|
@@ -486,7 +506,9 @@ function formatBridgeHostLabel(host = {}) {
|
|
|
486
506
|
const owner = String(host?.ownerName || host?.ownerId || "").trim();
|
|
487
507
|
const label = String(host?.label || "").trim();
|
|
488
508
|
const runtime = host?.provider && host?.model ? `${host.provider}/${host.model}` : "";
|
|
489
|
-
|
|
509
|
+
const safeLabel = label && !/\bundefined\b/i.test(label) ? label : "";
|
|
510
|
+
const primary = owner || safeLabel || "live terminal";
|
|
511
|
+
return [primary, safeLabel && safeLabel !== owner ? `(${safeLabel})` : "", runtime ? `[${runtime}]` : ""].filter(Boolean).join(" ").trim();
|
|
490
512
|
}
|
|
491
513
|
|
|
492
514
|
function findLiveHostForAgent(hosts = [], agent = {}) {
|
|
@@ -800,7 +822,7 @@ function parseTelegramStateIntent(text = "") {
|
|
|
800
822
|
if (/\bwhat project\b/.test(lower) || /\bwhich project\b/.test(lower) || /\bproject is this chat\b/.test(lower) || /\bchat bound to\b/.test(lower)) {
|
|
801
823
|
return { action: "project-status" };
|
|
802
824
|
}
|
|
803
|
-
if (/\bwho is in the room\b/.test(lower) || /\bwho(?:'s| is) in the project\b/.test(lower) || /\bwho are the members\b/.test(lower) || /\bshow members\b/.test(lower)) {
|
|
825
|
+
if (/\bwho(?:'s| is)? in (?:the )?room\b/.test(lower) || /\bwho(?:'s| is)? in (?:the )?project\b/.test(lower) || /\bwho are the members\b/.test(lower) || /\bshow members\b/.test(lower)) {
|
|
804
826
|
return { action: "room-members" };
|
|
805
827
|
}
|
|
806
828
|
if (/\bwhat mode\b/.test(lower) || /\broom mode\b/.test(lower) || /\bmode are we in\b/.test(lower)) {
|
|
@@ -812,7 +834,7 @@ function parseTelegramStateIntent(text = "") {
|
|
|
812
834
|
if (/\bwhat can i do here\b/.test(lower) || /\bhow do i use this room\b/.test(lower) || /\bhow do i use this chat\b/.test(lower) || /\bwhat do i do here\b/.test(lower)) {
|
|
813
835
|
return { action: "room-guidance" };
|
|
814
836
|
}
|
|
815
|
-
if (/\bwho are the bots\b/.test(lower) || /\bwho are the agents\b/.test(lower) || /\bwhat bots are here\b/.test(lower) || /\bwhat agents are here\b/.test(lower)) {
|
|
837
|
+
if (/\bwho are the (?:bots|boys)\b/.test(lower) || /\bwho are the agents\b/.test(lower) || /\bwhat (?:bots|boys) are here\b/.test(lower) || /\bwhat agents are here\b/.test(lower)) {
|
|
816
838
|
return { action: "agent-list" };
|
|
817
839
|
}
|
|
818
840
|
if (/\bwhich terminals are live\b/.test(lower) || /\bwhich bots are live\b/.test(lower) || /\bwho is live\b/.test(lower) || /\bwhat terminals are live\b/.test(lower)) {
|
package/src/shared-project.js
CHANGED
|
@@ -54,11 +54,13 @@ function normalizeParticipant(participant = {}) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function normalizeAgent(agent = {}) {
|
|
57
|
+
const rawLabel = String(agent.label || agent.name || "").trim();
|
|
58
|
+
const cleanLabel = rawLabel.replace(/\bundefined\b/ig, "").replace(/\s+/g, " ").trim();
|
|
57
59
|
return {
|
|
58
60
|
id: String(agent.id || "").trim(),
|
|
59
61
|
ownerId: String(agent.ownerId || "").trim(),
|
|
60
62
|
ownerName: String(agent.ownerName || "").trim(),
|
|
61
|
-
label:
|
|
63
|
+
label: cleanLabel,
|
|
62
64
|
surface: String(agent.surface || "").trim(),
|
|
63
65
|
role: AGENT_ROLES.includes(String(agent.role || "").trim()) ? String(agent.role).trim() : "standby",
|
|
64
66
|
provider: String(agent.provider || "").trim(),
|
|
@@ -71,6 +73,42 @@ function normalizeAgent(agent = {}) {
|
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
function isBrokenAgent(agent = {}) {
|
|
77
|
+
const ownerId = String(agent.ownerId || "").trim();
|
|
78
|
+
const ownerName = String(agent.ownerName || "").trim();
|
|
79
|
+
const label = String(agent.label || "").trim();
|
|
80
|
+
return !ownerId && !ownerName && !label;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function dedupeAgents(agents = []) {
|
|
84
|
+
const bySemanticKey = new Map();
|
|
85
|
+
for (const agent of agents) {
|
|
86
|
+
const normalized = normalizeAgent(agent);
|
|
87
|
+
if (!normalized.id || isBrokenAgent(normalized)) continue;
|
|
88
|
+
const semanticKey = [
|
|
89
|
+
String(normalized.ownerId || "").trim(),
|
|
90
|
+
String(normalized.ownerName || "").trim().toLowerCase(),
|
|
91
|
+
String(normalized.label || "").trim().toLowerCase(),
|
|
92
|
+
String(normalized.surface || "").trim().toLowerCase(),
|
|
93
|
+
String(normalized.role || "").trim().toLowerCase(),
|
|
94
|
+
String(normalized.provider || "").trim().toLowerCase(),
|
|
95
|
+
String(normalized.model || "").trim().toLowerCase(),
|
|
96
|
+
String(normalized.chatId || "").trim()
|
|
97
|
+
].join("|");
|
|
98
|
+
const prior = bySemanticKey.get(semanticKey);
|
|
99
|
+
if (!prior) {
|
|
100
|
+
bySemanticKey.set(semanticKey, normalized);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const priorUpdated = Date.parse(String(prior.updatedAt || "").trim()) || 0;
|
|
104
|
+
const nextUpdated = Date.parse(String(normalized.updatedAt || "").trim()) || 0;
|
|
105
|
+
if (nextUpdated >= priorUpdated) {
|
|
106
|
+
bySemanticKey.set(semanticKey, normalized);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return [...bySemanticKey.values()];
|
|
110
|
+
}
|
|
111
|
+
|
|
74
112
|
function areAgentsEquivalent(left = {}, right = {}) {
|
|
75
113
|
return [
|
|
76
114
|
"ownerId",
|
|
@@ -119,12 +157,8 @@ function buildProjectParticipants(project = {}) {
|
|
|
119
157
|
}
|
|
120
158
|
|
|
121
159
|
function buildProjectAgents(project = {}) {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
.map((agent) => normalizeAgent(agent))
|
|
125
|
-
.filter((agent) => agent.id)
|
|
126
|
-
.map((agent) => [agent.id, agent])
|
|
127
|
-
);
|
|
160
|
+
const existingAgents = dedupeAgents(Array.isArray(project.agents) ? project.agents : []);
|
|
161
|
+
const existing = new Map(existingAgents.map((agent) => [agent.id, agent]));
|
|
128
162
|
const next = [];
|
|
129
163
|
const activeOperatorId = String(project.activeOperator?.id || "").trim();
|
|
130
164
|
if (activeOperatorId) {
|
|
@@ -145,7 +179,7 @@ function buildProjectAgents(project = {}) {
|
|
|
145
179
|
if (next.some((item) => item.id === agent.id)) continue;
|
|
146
180
|
next.push(normalizeAgent(agent));
|
|
147
181
|
}
|
|
148
|
-
return next;
|
|
182
|
+
return dedupeAgents(next);
|
|
149
183
|
}
|
|
150
184
|
|
|
151
185
|
function memberRoleWeight(role = "") {
|
|
@@ -433,7 +467,12 @@ async function recordSharedProjectEvent(cwd, project, text, { type = "note", act
|
|
|
433
467
|
export async function loadSharedProject(cwd) {
|
|
434
468
|
try {
|
|
435
469
|
const raw = await fs.readFile(sharedFilePath(cwd), "utf8");
|
|
436
|
-
|
|
470
|
+
const parsed = JSON.parse(raw);
|
|
471
|
+
const normalized = normalizeSharedProject(parsed, cwd);
|
|
472
|
+
if (JSON.stringify(parsed) !== JSON.stringify(normalized)) {
|
|
473
|
+
await writeJsonAtomically(sharedFilePath(cwd), normalized);
|
|
474
|
+
}
|
|
475
|
+
return normalized;
|
|
437
476
|
} catch (error) {
|
|
438
477
|
if (error?.code === "ENOENT") return null;
|
|
439
478
|
throw error;
|