@tritard/waterbrother 0.16.65 → 0.16.67
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/cli.js +90 -9
- package/src/gateway-state.js +8 -1
- package/src/gateway.js +81 -14
- package/src/shared-project.js +35 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
removeSharedMember,
|
|
93
93
|
setSharedRuntimeProfile,
|
|
94
94
|
setSharedRoomMode,
|
|
95
|
+
upsertSharedAgent,
|
|
95
96
|
upsertSharedMember
|
|
96
97
|
} from "./shared-project.js";
|
|
97
98
|
import { findRelevantRegressions, buildRegressionWarningBlock } from "./regressions.js";
|
|
@@ -3004,47 +3005,89 @@ async function stopTrackedGateway(service = "telegram", { io = console } = {}) {
|
|
|
3004
3005
|
return { stopped, pid };
|
|
3005
3006
|
}
|
|
3006
3007
|
|
|
3007
|
-
function createTelegramBridgeHostRecord({ sessionId, cwd }) {
|
|
3008
|
+
function createTelegramBridgeHostRecord({ sessionId, cwd, label = "", surface = "live-tui", ownerId = "", ownerName = "", provider = "", model = "", runtimeProfile = "" }) {
|
|
3008
3009
|
const now = new Date().toISOString();
|
|
3009
3010
|
return {
|
|
3010
3011
|
pid: process.pid,
|
|
3011
3012
|
sessionId: String(sessionId || "").trim(),
|
|
3012
3013
|
cwd: String(cwd || "").trim(),
|
|
3014
|
+
label: String(label || "").trim(),
|
|
3015
|
+
surface: String(surface || "live-tui").trim() || "live-tui",
|
|
3016
|
+
ownerId: String(ownerId || "").trim(),
|
|
3017
|
+
ownerName: String(ownerName || "").trim(),
|
|
3018
|
+
provider: String(provider || "").trim(),
|
|
3019
|
+
model: String(model || "").trim(),
|
|
3020
|
+
runtimeProfile: String(runtimeProfile || "").trim(),
|
|
3013
3021
|
startedAt: now,
|
|
3014
3022
|
updatedAt: now
|
|
3015
3023
|
};
|
|
3016
3024
|
}
|
|
3017
3025
|
|
|
3018
|
-
async function
|
|
3026
|
+
async function syncSharedTelegramBridgeAgent({ cwd, host = {}, actor = {} }) {
|
|
3027
|
+
try {
|
|
3028
|
+
const project = await loadSharedProject(cwd);
|
|
3029
|
+
if (!project?.enabled) return null;
|
|
3030
|
+
return await upsertSharedAgent(cwd, {
|
|
3031
|
+
id: `agent:telegram-bridge:${String(host.sessionId || process.pid).trim()}`,
|
|
3032
|
+
ownerId: String(actor.id || "").trim() || String(host.ownerId || "").trim(),
|
|
3033
|
+
ownerName: String(actor.name || "").trim() || String(host.ownerName || "").trim(),
|
|
3034
|
+
label: String(host.label || "").trim() || "Waterbrother live TUI",
|
|
3035
|
+
surface: String(host.surface || "").trim() || "live-tui",
|
|
3036
|
+
role: "executor",
|
|
3037
|
+
provider: String(host.provider || "").trim(),
|
|
3038
|
+
model: String(host.model || "").trim(),
|
|
3039
|
+
runtimeProfile: String(host.runtimeProfile || "").trim(),
|
|
3040
|
+
sessionId: String(host.sessionId || "").trim(),
|
|
3041
|
+
cwd: String(cwd || "").trim(),
|
|
3042
|
+
chatId: String(project.room?.chatId || "").trim()
|
|
3043
|
+
}, {
|
|
3044
|
+
actorId: String(actor.id || "").trim(),
|
|
3045
|
+
actorName: String(actor.name || "").trim()
|
|
3046
|
+
});
|
|
3047
|
+
} catch {
|
|
3048
|
+
return null;
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
async function registerTelegramBridgeHost({ sessionId, cwd, label = "", surface = "live-tui", ownerId = "", ownerName = "", provider = "", model = "", runtimeProfile = "", actor = null }) {
|
|
3019
3053
|
const bridge = await loadGatewayBridge(TELEGRAM_BRIDGE_SERVICE);
|
|
3020
|
-
bridge.activeHost = createTelegramBridgeHostRecord({ sessionId, cwd });
|
|
3054
|
+
bridge.activeHost = createTelegramBridgeHostRecord({ sessionId, cwd, label, surface, ownerId, ownerName, provider, model, runtimeProfile });
|
|
3021
3055
|
bridge.deliveredReplies = Array.isArray(bridge.deliveredReplies) ? bridge.deliveredReplies.slice(-50) : [];
|
|
3022
3056
|
bridge.pendingRequests = Array.isArray(bridge.pendingRequests) ? bridge.pendingRequests : [];
|
|
3023
3057
|
await saveGatewayBridge(TELEGRAM_BRIDGE_SERVICE, bridge);
|
|
3058
|
+
await syncSharedTelegramBridgeAgent({ cwd, host: bridge.activeHost, actor: actor || { id: ownerId, name: ownerName } });
|
|
3024
3059
|
return bridge.activeHost;
|
|
3025
3060
|
}
|
|
3026
3061
|
|
|
3027
|
-
async function touchTelegramBridgeHost({ sessionId, cwd }) {
|
|
3062
|
+
async function touchTelegramBridgeHost({ sessionId, cwd, label = "", surface = "live-tui", ownerId = "", ownerName = "", provider = "", model = "", runtimeProfile = "", actor = null }) {
|
|
3028
3063
|
const bridge = await loadGatewayBridge(TELEGRAM_BRIDGE_SERVICE);
|
|
3029
3064
|
const activeHost = bridge.activeHost || {};
|
|
3030
3065
|
if (Number(activeHost.pid || 0) !== process.pid) {
|
|
3031
|
-
bridge.activeHost = createTelegramBridgeHostRecord({ sessionId, cwd });
|
|
3066
|
+
bridge.activeHost = createTelegramBridgeHostRecord({ sessionId, cwd, label, surface, ownerId, ownerName, provider, model, runtimeProfile });
|
|
3032
3067
|
} else {
|
|
3033
3068
|
bridge.activeHost = {
|
|
3034
3069
|
...activeHost,
|
|
3035
3070
|
sessionId: String(sessionId || "").trim(),
|
|
3036
3071
|
cwd: String(cwd || "").trim(),
|
|
3072
|
+
label: String(label || activeHost.label || "").trim(),
|
|
3073
|
+
surface: String(surface || activeHost.surface || "live-tui").trim() || "live-tui",
|
|
3074
|
+
ownerId: String(ownerId || activeHost.ownerId || "").trim(),
|
|
3075
|
+
ownerName: String(ownerName || activeHost.ownerName || "").trim(),
|
|
3076
|
+
provider: String(provider || activeHost.provider || "").trim(),
|
|
3077
|
+
model: String(model || activeHost.model || "").trim(),
|
|
3078
|
+
runtimeProfile: String(runtimeProfile || activeHost.runtimeProfile || "").trim(),
|
|
3037
3079
|
updatedAt: new Date().toISOString()
|
|
3038
3080
|
};
|
|
3039
3081
|
}
|
|
3040
3082
|
await saveGatewayBridge(TELEGRAM_BRIDGE_SERVICE, bridge);
|
|
3083
|
+
await syncSharedTelegramBridgeAgent({ cwd, host: bridge.activeHost, actor: actor || { id: ownerId, name: ownerName } });
|
|
3041
3084
|
return bridge.activeHost;
|
|
3042
3085
|
}
|
|
3043
3086
|
|
|
3044
3087
|
async function clearTelegramBridgeHost() {
|
|
3045
3088
|
const bridge = await loadGatewayBridge(TELEGRAM_BRIDGE_SERVICE);
|
|
3046
3089
|
if (Number(bridge.activeHost?.pid || 0) === process.pid) {
|
|
3047
|
-
bridge.activeHost = { pid: 0, sessionId: "", cwd: "", startedAt: "", updatedAt: "" };
|
|
3090
|
+
bridge.activeHost = { pid: 0, sessionId: "", cwd: "", label: "", surface: "", ownerId: "", ownerName: "", provider: "", model: "", runtimeProfile: "", startedAt: "", updatedAt: "" };
|
|
3048
3091
|
await saveGatewayBridge(TELEGRAM_BRIDGE_SERVICE, bridge);
|
|
3049
3092
|
}
|
|
3050
3093
|
}
|
|
@@ -7458,7 +7501,23 @@ Be concrete about surfaces — name actual pages/flows. Choose the best stack fo
|
|
|
7458
7501
|
|
|
7459
7502
|
const gatewayHandle = await maybeAutostartGateway(context.runtime, { cwd: context.cwd });
|
|
7460
7503
|
let gatewayCleanupDone = false;
|
|
7461
|
-
|
|
7504
|
+
const telegramBridgeOperator = buildOperatorIdentity({
|
|
7505
|
+
mode: agent.getExperienceMode(),
|
|
7506
|
+
autonomy: agent.getAutonomyMode(),
|
|
7507
|
+
profile: agent.getProfile()
|
|
7508
|
+
});
|
|
7509
|
+
await registerTelegramBridgeHost({
|
|
7510
|
+
sessionId: currentSession.id,
|
|
7511
|
+
cwd: context.cwd,
|
|
7512
|
+
label: `${telegramBridgeOperator.name} terminal`,
|
|
7513
|
+
surface: "live-tui",
|
|
7514
|
+
ownerId: telegramBridgeOperator.id,
|
|
7515
|
+
ownerName: telegramBridgeOperator.name,
|
|
7516
|
+
provider: context.runtime.provider,
|
|
7517
|
+
model: context.runtime.model,
|
|
7518
|
+
runtimeProfile: currentSession.runtimeProfile || "",
|
|
7519
|
+
actor: { id: telegramBridgeOperator.id, name: telegramBridgeOperator.name }
|
|
7520
|
+
});
|
|
7462
7521
|
const cleanupManagedGateway = async () => {
|
|
7463
7522
|
if (gatewayCleanupDone) return;
|
|
7464
7523
|
gatewayCleanupDone = true;
|
|
@@ -7494,7 +7553,18 @@ Be concrete about surfaces — name actual pages/flows. Choose the best stack fo
|
|
|
7494
7553
|
if (sharedFeed.latestEventId) {
|
|
7495
7554
|
lastSeenSharedRoomEventId = sharedFeed.latestEventId;
|
|
7496
7555
|
}
|
|
7497
|
-
await touchTelegramBridgeHost({
|
|
7556
|
+
await touchTelegramBridgeHost({
|
|
7557
|
+
sessionId: currentSession.id,
|
|
7558
|
+
cwd: context.cwd,
|
|
7559
|
+
label: `${telegramBridgeOperator.name} terminal`,
|
|
7560
|
+
surface: "live-tui",
|
|
7561
|
+
ownerId: telegramBridgeOperator.id,
|
|
7562
|
+
ownerName: telegramBridgeOperator.name,
|
|
7563
|
+
provider: context.runtime.provider,
|
|
7564
|
+
model: context.runtime.model,
|
|
7565
|
+
runtimeProfile: currentSession.runtimeProfile || "",
|
|
7566
|
+
actor: { id: telegramBridgeOperator.id, name: telegramBridgeOperator.name }
|
|
7567
|
+
});
|
|
7498
7568
|
const nextInput = await readInteractiveLine({
|
|
7499
7569
|
getFooterText(inputBuffer) {
|
|
7500
7570
|
const footer = buildInteractiveFooter({
|
|
@@ -7574,7 +7644,18 @@ Be concrete about surfaces — name actual pages/flows. Choose the best stack fo
|
|
|
7574
7644
|
}
|
|
7575
7645
|
|
|
7576
7646
|
remoteSessionId = currentSession.id;
|
|
7577
|
-
await touchTelegramBridgeHost({
|
|
7647
|
+
await touchTelegramBridgeHost({
|
|
7648
|
+
sessionId: currentSession.id,
|
|
7649
|
+
cwd: context.cwd,
|
|
7650
|
+
label: `${telegramBridgeOperator.name} terminal`,
|
|
7651
|
+
surface: "live-tui",
|
|
7652
|
+
ownerId: telegramBridgeOperator.id,
|
|
7653
|
+
ownerName: telegramBridgeOperator.name,
|
|
7654
|
+
provider: context.runtime.provider,
|
|
7655
|
+
model: context.runtime.model,
|
|
7656
|
+
runtimeProfile: currentSession.runtimeProfile || "",
|
|
7657
|
+
actor: { id: telegramBridgeOperator.id, name: telegramBridgeOperator.name }
|
|
7658
|
+
});
|
|
7578
7659
|
if (remoteRequest.runtimeProfile && remoteRequest.runtimeProfile !== currentSession.runtimeProfile) {
|
|
7579
7660
|
const { config } = await loadConfigLayers(context.cwd);
|
|
7580
7661
|
const loaded = await loadNamedRuntimeProfile(remoteRequest.runtimeProfile, {
|
package/src/gateway-state.js
CHANGED
|
@@ -83,10 +83,17 @@ function normalizeGatewayBridge(parsed = {}) {
|
|
|
83
83
|
pid: Number.isFinite(Number(parsed.activeHost.pid)) ? Math.floor(Number(parsed.activeHost.pid)) : 0,
|
|
84
84
|
sessionId: String(parsed.activeHost.sessionId || "").trim(),
|
|
85
85
|
cwd: String(parsed.activeHost.cwd || "").trim(),
|
|
86
|
+
label: String(parsed.activeHost.label || "").trim(),
|
|
87
|
+
surface: String(parsed.activeHost.surface || "").trim(),
|
|
88
|
+
ownerId: String(parsed.activeHost.ownerId || "").trim(),
|
|
89
|
+
ownerName: String(parsed.activeHost.ownerName || "").trim(),
|
|
90
|
+
provider: String(parsed.activeHost.provider || "").trim(),
|
|
91
|
+
model: String(parsed.activeHost.model || "").trim(),
|
|
92
|
+
runtimeProfile: String(parsed.activeHost.runtimeProfile || "").trim(),
|
|
86
93
|
startedAt: String(parsed.activeHost.startedAt || "").trim(),
|
|
87
94
|
updatedAt: String(parsed.activeHost.updatedAt || "").trim()
|
|
88
95
|
}
|
|
89
|
-
: { pid: 0, sessionId: "", cwd: "", startedAt: "", updatedAt: "" },
|
|
96
|
+
: { pid: 0, sessionId: "", cwd: "", label: "", surface: "", ownerId: "", ownerName: "", provider: "", model: "", runtimeProfile: "", startedAt: "", updatedAt: "" },
|
|
90
97
|
pendingRequests,
|
|
91
98
|
deliveredReplies
|
|
92
99
|
};
|
package/src/gateway.js
CHANGED
|
@@ -285,6 +285,32 @@ function formatGatewaySessionStatus({ sessionId, userId, username, cwd, runtimeP
|
|
|
285
285
|
return bits.join("\n");
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
function listProjectParticipants(project) {
|
|
289
|
+
return Array.isArray(project?.participants) ? project.participants : [];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function listProjectAgents(project) {
|
|
293
|
+
return Array.isArray(project?.agents) ? project.agents : [];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function findProjectParticipant(project, memberId = "") {
|
|
297
|
+
const normalizedId = String(memberId || "").trim();
|
|
298
|
+
if (!normalizedId) return null;
|
|
299
|
+
return listProjectParticipants(project).find((participant) => String(participant?.memberId || participant?.id || "").trim() === normalizedId) || null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function formatAgentLabel(agent = {}) {
|
|
303
|
+
const label = String(agent?.label || agent?.name || "").trim();
|
|
304
|
+
const role = String(agent?.role || "").trim();
|
|
305
|
+
const provider = String(agent?.provider || "").trim();
|
|
306
|
+
const model = String(agent?.model || "").trim();
|
|
307
|
+
const bits = [];
|
|
308
|
+
if (label) bits.push(label);
|
|
309
|
+
if (role) bits.push(`(${role})`);
|
|
310
|
+
if (provider && model) bits.push(`[${provider}/${model}]`);
|
|
311
|
+
return bits.join(" ").trim();
|
|
312
|
+
}
|
|
313
|
+
|
|
288
314
|
function formatTelegramSummaryMarkup({ cwd, project, chatId = "", title = "", executor = {} }) {
|
|
289
315
|
const roomLabel = project?.room?.chatId
|
|
290
316
|
? `${project.room.provider || "telegram"} ${project.room.chatId}${project.room.title ? ` (${project.room.title})` : ""}`
|
|
@@ -299,10 +325,15 @@ function formatTelegramSummaryMarkup({ cwd, project, chatId = "", title = "", ex
|
|
|
299
325
|
`shared: <code>${project?.enabled ? "yes" : "no"}</code>`
|
|
300
326
|
];
|
|
301
327
|
if (project?.enabled) {
|
|
328
|
+
const participants = listProjectParticipants(project);
|
|
329
|
+
const agents = listProjectAgents(project);
|
|
302
330
|
lines.push(`room mode: <code>${escapeTelegramHtml(project.roomMode || "chat")}</code>`);
|
|
303
331
|
lines.push(`bound chat: <code>${escapeTelegramHtml(roomLabel)}</code>`);
|
|
304
332
|
lines.push(`active operator: <code>${escapeTelegramHtml(activeOperator)}</code>`);
|
|
305
|
-
lines.push(`
|
|
333
|
+
lines.push(`participants: <code>${escapeTelegramHtml(String(participants.length || (project.members || []).length))}</code>`);
|
|
334
|
+
if (agents.length) {
|
|
335
|
+
lines.push(`agents: <code>${escapeTelegramHtml(String(agents.length))}</code>`);
|
|
336
|
+
}
|
|
306
337
|
if (executor?.surface) lines.push(`executor: <code>${escapeTelegramHtml(executor.surface)}</code>`);
|
|
307
338
|
if (executor?.provider && executor?.model) {
|
|
308
339
|
lines.push(`runtime: <code>${escapeTelegramHtml(`${executor.provider}/${executor.model}`)}</code>`);
|
|
@@ -489,6 +520,8 @@ function formatTelegramRoomMarkup(project, options = {}) {
|
|
|
489
520
|
return "<b>Shared room</b>\nThis project is not shared.";
|
|
490
521
|
}
|
|
491
522
|
const members = Array.isArray(project.members) ? project.members : [];
|
|
523
|
+
const participants = listProjectParticipants(project);
|
|
524
|
+
const agents = listProjectAgents(project);
|
|
492
525
|
const active = project.activeOperator?.id
|
|
493
526
|
? `${project.activeOperator.name || project.activeOperator.id} (${project.activeOperator.id})`
|
|
494
527
|
: "none";
|
|
@@ -513,6 +546,20 @@ function formatTelegramRoomMarkup(project, options = {}) {
|
|
|
513
546
|
.sort((a, b) => b[1] - a[1])
|
|
514
547
|
.slice(0, 4)
|
|
515
548
|
.map(([assignee, count]) => `• <code>${escapeTelegramHtml(assignee)}</code> ${count}`);
|
|
549
|
+
const participantLines = participants.length
|
|
550
|
+
? participants.map((participant) => {
|
|
551
|
+
const channelBits = [];
|
|
552
|
+
if (participant?.channels?.telegram?.userId) channelBits.push(`tg:${participant.channels.telegram.userId}`);
|
|
553
|
+
if (participant?.channels?.local?.username) channelBits.push(`local:${participant.channels.local.username}`);
|
|
554
|
+
const label = participant.displayName || participant.memberId || participant.id || "unknown";
|
|
555
|
+
return `• ${escapeTelegramHtml(label)} <i>(${escapeTelegramHtml(participant.role || "editor")})</i>${channelBits.length ? ` <code>${escapeTelegramHtml(channelBits.join(" "))}</code>` : ""}`;
|
|
556
|
+
})
|
|
557
|
+
: members.length
|
|
558
|
+
? members.map((member) => `• ${escapeTelegramHtml(member.name || member.id)} <i>(${escapeTelegramHtml(member.role || "editor")})</i>`)
|
|
559
|
+
: ["• none"];
|
|
560
|
+
const agentLines = agents.length
|
|
561
|
+
? agents.map((agent) => `• ${escapeTelegramHtml(formatAgentLabel(agent) || agent.id || "agent")} <code>${escapeTelegramHtml(agent.surface || "unknown")}</code>`)
|
|
562
|
+
: ["• none"];
|
|
516
563
|
const executorBits = [
|
|
517
564
|
`surface: <code>${escapeTelegramHtml(executor.surface || "telegram")}</code>`,
|
|
518
565
|
`provider: <code>${escapeTelegramHtml(executor.provider || "unknown")}</code>`,
|
|
@@ -542,8 +589,10 @@ function formatTelegramRoomMarkup(project, options = {}) {
|
|
|
542
589
|
...[...byState.entries()].map(([state, count]) => `${escapeTelegramHtml(state)}: <code>${count}</code>`),
|
|
543
590
|
"<b>Ownership</b>",
|
|
544
591
|
...(ownershipLines.length ? ownershipLines : ["• none"]),
|
|
545
|
-
"<b>
|
|
546
|
-
|
|
592
|
+
"<b>Participants</b>",
|
|
593
|
+
...participantLines,
|
|
594
|
+
"<b>Agents</b>",
|
|
595
|
+
...agentLines
|
|
547
596
|
].join("\n");
|
|
548
597
|
}
|
|
549
598
|
|
|
@@ -576,13 +625,24 @@ function formatTelegramMembersMarkup(project) {
|
|
|
576
625
|
if (!project?.enabled) {
|
|
577
626
|
return "This project is not shared.";
|
|
578
627
|
}
|
|
579
|
-
const
|
|
580
|
-
if (!
|
|
581
|
-
|
|
628
|
+
const participants = listProjectParticipants(project);
|
|
629
|
+
if (!participants.length) {
|
|
630
|
+
const members = Array.isArray(project.members) ? project.members : [];
|
|
631
|
+
if (!members.length) {
|
|
632
|
+
return "<b>Shared participants</b>\n• none";
|
|
633
|
+
}
|
|
634
|
+
return [
|
|
635
|
+
"<b>Shared participants</b>",
|
|
636
|
+
...members.map((member) => `• ${escapeTelegramHtml(member.name || member.id)} <i>(${escapeTelegramHtml(member.role || "editor")})</i> <code>${escapeTelegramHtml(member.id || "")}</code>`)
|
|
637
|
+
].join("\n");
|
|
582
638
|
}
|
|
583
639
|
return [
|
|
584
|
-
"<b>Shared
|
|
585
|
-
...
|
|
640
|
+
"<b>Shared participants</b>",
|
|
641
|
+
...participants.map((participant) => {
|
|
642
|
+
const id = participant.memberId || participant.id || "";
|
|
643
|
+
const telegramId = participant?.channels?.telegram?.userId ? ` tg:${participant.channels.telegram.userId}` : "";
|
|
644
|
+
return `• ${escapeTelegramHtml(participant.displayName || id)} <i>(${escapeTelegramHtml(participant.role || "editor")})</i> <code>${escapeTelegramHtml(`${id}${telegramId}`.trim())}</code>`;
|
|
645
|
+
})
|
|
586
646
|
].join("\n");
|
|
587
647
|
}
|
|
588
648
|
|
|
@@ -656,11 +716,13 @@ function formatTelegramPeopleMarkup({ people = [], project = null }) {
|
|
|
656
716
|
"<b>Recent Telegram people</b>",
|
|
657
717
|
...people.map((person) => {
|
|
658
718
|
const member = members.find((entry) => String(entry?.id || "").trim() === person.userId);
|
|
719
|
+
const participant = findProjectParticipant(project, person.userId);
|
|
659
720
|
const pendingInvite = pendingInvites.find((entry) => String(entry?.memberId || "").trim() === person.userId);
|
|
660
721
|
const bits = [`<code>${escapeTelegramHtml(person.userId)}</code>`];
|
|
661
722
|
if (person.usernameHandle) bits.push(`@${escapeTelegramHtml(person.usernameHandle)}`);
|
|
662
723
|
bits.push(escapeTelegramHtml(person.displayName || person.userId));
|
|
663
|
-
if (
|
|
724
|
+
if (participant?.role) bits.push(`<i>participant:${escapeTelegramHtml(participant.role)}</i>`);
|
|
725
|
+
else if (member?.role) bits.push(`<i>member:${escapeTelegramHtml(member.role)}</i>`);
|
|
664
726
|
else if (pendingInvite?.id) bits.push(`<i>pending:${escapeTelegramHtml(pendingInvite.id)}</i>`);
|
|
665
727
|
else if (person.paired) bits.push("<i>paired</i>");
|
|
666
728
|
return `• ${bits.join(" ")}`;
|
|
@@ -1116,6 +1178,9 @@ class TelegramGateway {
|
|
|
1116
1178
|
}
|
|
1117
1179
|
|
|
1118
1180
|
buildTrustedRoomIntroMarkup({ project, actor }) {
|
|
1181
|
+
const participants = listProjectParticipants(project);
|
|
1182
|
+
const agents = listProjectAgents(project);
|
|
1183
|
+
const executorAgent = agents.find((agent) => String(agent?.role || "").trim() === "executor") || null;
|
|
1119
1184
|
return [
|
|
1120
1185
|
"<b>Roundtable room</b>",
|
|
1121
1186
|
`${escapeTelegramHtml(actor.displayName || actor.userId)} is now in <code>${escapeTelegramHtml(project.projectName || "this project")}</code> as <code>observer</code>.`,
|
|
@@ -1123,10 +1188,12 @@ class TelegramGateway {
|
|
|
1123
1188
|
project.activeOperator?.id
|
|
1124
1189
|
? `active operator: <code>${escapeTelegramHtml(project.activeOperator.name || project.activeOperator.id)}</code>`
|
|
1125
1190
|
: "active operator: <code>none</code>",
|
|
1191
|
+
`participants: <code>${escapeTelegramHtml(String(participants.length || (project.members || []).length))}</code>`,
|
|
1192
|
+
executorAgent ? `active terminal: <code>${escapeTelegramHtml(formatAgentLabel(executorAgent) || executorAgent.id)}</code>` : "",
|
|
1126
1193
|
"As observer, you can ask questions, discuss plans, and review work here.",
|
|
1127
1194
|
"An owner can promote you to editor or owner conversationally.",
|
|
1128
1195
|
"Examples: <code>what project is this chat bound to?</code>, <code>who is in the room?</code>, <code>what can I do here?</code>"
|
|
1129
|
-
].join("\n");
|
|
1196
|
+
].filter(Boolean).join("\n");
|
|
1130
1197
|
}
|
|
1131
1198
|
|
|
1132
1199
|
describeTelegramUser(from = {}) {
|
|
@@ -1744,10 +1811,10 @@ class TelegramGateway {
|
|
|
1744
1811
|
const { project } = await this.bindSharedRoomForMessage(message, sessionId);
|
|
1745
1812
|
const host = await this.getLiveBridgeHost();
|
|
1746
1813
|
const executor = {
|
|
1747
|
-
surface: host ? "live-tui" : "telegram-fallback",
|
|
1748
|
-
provider: this.runtime.provider,
|
|
1749
|
-
model: this.runtime.model,
|
|
1750
|
-
runtimeProfile: project?.runtimeProfile || this.channel.defaultRuntimeProfile || this.gateway.defaultRuntimeProfile || "",
|
|
1814
|
+
surface: host?.surface || (host ? "live-tui" : "telegram-fallback"),
|
|
1815
|
+
provider: host?.provider || this.runtime.provider,
|
|
1816
|
+
model: host?.model || this.runtime.model,
|
|
1817
|
+
runtimeProfile: host?.runtimeProfile || project?.runtimeProfile || this.channel.defaultRuntimeProfile || this.gateway.defaultRuntimeProfile || "",
|
|
1751
1818
|
hostSessionId: host?.sessionId || ""
|
|
1752
1819
|
};
|
|
1753
1820
|
return project?.enabled
|
package/src/shared-project.js
CHANGED
|
@@ -57,6 +57,7 @@ function normalizeAgent(agent = {}) {
|
|
|
57
57
|
return {
|
|
58
58
|
id: String(agent.id || "").trim(),
|
|
59
59
|
ownerId: String(agent.ownerId || "").trim(),
|
|
60
|
+
ownerName: String(agent.ownerName || "").trim(),
|
|
60
61
|
label: String(agent.label || agent.name || "").trim(),
|
|
61
62
|
surface: String(agent.surface || "").trim(),
|
|
62
63
|
role: AGENT_ROLES.includes(String(agent.role || "").trim()) ? String(agent.role).trim() : "standby",
|
|
@@ -64,6 +65,8 @@ function normalizeAgent(agent = {}) {
|
|
|
64
65
|
model: String(agent.model || "").trim(),
|
|
65
66
|
runtimeProfile: String(agent.runtimeProfile || "").trim(),
|
|
66
67
|
sessionId: String(agent.sessionId || "").trim(),
|
|
68
|
+
cwd: String(agent.cwd || "").trim(),
|
|
69
|
+
chatId: String(agent.chatId || "").trim(),
|
|
67
70
|
updatedAt: String(agent.updatedAt || new Date().toISOString()).trim()
|
|
68
71
|
};
|
|
69
72
|
}
|
|
@@ -602,6 +605,38 @@ export async function upsertSharedMember(cwd, member = {}, options = {}) {
|
|
|
602
605
|
})).project;
|
|
603
606
|
}
|
|
604
607
|
|
|
608
|
+
export async function upsertSharedAgent(cwd, agent = {}, options = {}) {
|
|
609
|
+
const existing = await loadSharedProject(cwd);
|
|
610
|
+
requireSharedProject(existing);
|
|
611
|
+
const nextAgent = normalizeAgent(agent);
|
|
612
|
+
if (!nextAgent.id) throw new Error("agent id is required");
|
|
613
|
+
if (options.actorId) {
|
|
614
|
+
requireMember(existing, options.actorId);
|
|
615
|
+
}
|
|
616
|
+
const agents = Array.isArray(existing.agents) ? [...existing.agents] : [];
|
|
617
|
+
const index = agents.findIndex((item) => item.id === nextAgent.id);
|
|
618
|
+
if (index >= 0) {
|
|
619
|
+
agents[index] = normalizeAgent({ ...agents[index], ...nextAgent });
|
|
620
|
+
} else {
|
|
621
|
+
agents.push(nextAgent);
|
|
622
|
+
}
|
|
623
|
+
const next = await saveSharedProject(cwd, {
|
|
624
|
+
...existing,
|
|
625
|
+
agents
|
|
626
|
+
});
|
|
627
|
+
return (await recordSharedProjectEvent(cwd, next, `agent ${nextAgent.label || nextAgent.id} registered as ${nextAgent.role}`, {
|
|
628
|
+
type: "agent-upsert",
|
|
629
|
+
actorId: String(options.actorId || "").trim(),
|
|
630
|
+
actorName: String(options.actorName || "").trim(),
|
|
631
|
+
meta: {
|
|
632
|
+
agentId: nextAgent.id,
|
|
633
|
+
ownerId: nextAgent.ownerId,
|
|
634
|
+
role: nextAgent.role,
|
|
635
|
+
surface: nextAgent.surface
|
|
636
|
+
}
|
|
637
|
+
})).project;
|
|
638
|
+
}
|
|
639
|
+
|
|
605
640
|
export async function createSharedInvite(cwd, member = {}, options = {}) {
|
|
606
641
|
const existing = await loadSharedProject(cwd);
|
|
607
642
|
requireOwner(existing, options.actorId);
|