feat: show connection state, bot, message counters and last activity in the omp status line

This commit is contained in:
evandrodevbr committed 2026-10-01 11:21:14 -03:00
1 parent 60254b288b
commit ca05b58f38
7 files changed
+554 -269

No files matched your search

+159 -242
View File
@@ -111,248 +111,165 @@ test("Status runtime diagnostics scheduler coalesces snapshot persists", async (
assert.deepEqual(errors, []);
});
test("Status bar text renders bridge connection and queue states", () => {
const theme = {
fg: (token: string, text: string) => `<${token}>${text}</${token}>`,
const statusBarTheme = {
fg: (token: string, text: string) => `<${token}>${text}</${token}>`,
};
function plainStatusText(text: string): string {
return text.replace(/<\/?[a-z]+>/g, "");
}
function escapeRegExp(text: string): string {
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
test("Status bar text keeps the label, state color and queue for every connection state", () => {
const base = {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: false,
queuedStatus: "",
};
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: false,
pollingActive: false,
paired: false,
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <muted>not configured</muted>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: true,
queuedStatus: " +1",
}),
"<accent>telegram</accent> <warning>active</warning><success> +1</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: true,
processingStatus: "dispatching",
queuedStatus: " +1",
}),
"<accent>telegram</accent> <warning>active</warning><success> +1</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: true,
processingStatus: "active",
queuedStatus: "",
}),
"<accent>telegram</accent> <warning>active</warning>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: true,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <success>connected</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: true,
processing: true,
processingStatus: "active",
queuedStatus: "",
}),
"<accent>telegram</accent> <warning>active</warning>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
instanceThreadName: "Aurora",
compactionInProgress: false,
processing: true,
processingStatus: "queued",
queuedStatus: " +2",
}),
"<accent>telegram</accent> <muted>disconnected</muted><success> +2</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <success>follower</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
instanceThreadName: "Amber",
compactionInProgress: false,
processing: true,
processingStatus: "active",
queuedStatus: "",
}),
"<accent>Amber</accent> <warning>active</warning>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
busLifecyclePhase: "electing",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <warning>electing</warning>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busLifecyclePhase: "electing",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <warning>electing</warning>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
instanceThreadName: "Follower",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <success>follower</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
instanceThreadName: "Lname",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>Lname</accent> <success>follower</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
instanceSlot: "O",
instanceThreadName: "extensions Follower",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>extensions Follower</accent> <success>follower</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: false,
paired: true,
busRole: "follower",
instanceSlot: "O",
instanceThreadName: "Oname",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>Oname</accent> <success>follower</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
busRole: "leader",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>telegram</accent> <success>leader</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
busRole: "leader",
instanceThreadName: "🌙 A-identity",
compactionInProgress: false,
processing: false,
queuedStatus: "",
}),
"<accent>🌙 A-identity</accent> <success>leader</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: false,
compactionInProgress: false,
processing: true,
processingStatus: "queued",
queuedStatus: " +1",
}),
"<accent>telegram</accent> <warning>awaiting pairing</warning><success> +1</success>",
);
assert.equal(
buildTelegramStatusBarText(theme, {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: false,
queuedStatus: "",
const cases: Array<{
state: Partial<Parameters<typeof buildTelegramStatusBarText>[1]>;
label: string;
token: string;
word: string;
queued?: string;
}> = [
{ state: { hasBotToken: false, pollingActive: false, paired: false }, label: "telegram", token: "muted", word: "not configured" },
{ state: { processing: true, queuedStatus: " +1" }, label: "telegram", token: "warning", word: "active", queued: " +1" },
{ state: { processing: true, processingStatus: "dispatching", queuedStatus: " +1" }, label: "telegram", token: "warning", word: "active", queued: " +1" },
{ state: { processing: true, processingStatus: "active" }, label: "telegram", token: "warning", word: "active" },
{ state: { compactionInProgress: true }, label: "telegram", token: "success", word: "connected" },
{ state: { compactionInProgress: true, processing: true, processingStatus: "active" }, label: "telegram", token: "warning", word: "active" },
{ state: { pollingActive: false, instanceThreadName: "Aurora", processing: true, processingStatus: "queued", queuedStatus: " +2" }, label: "telegram", token: "muted", word: "disconnected", queued: " +2" },
{ state: { pollingActive: false, busRole: "follower" }, label: "telegram", token: "success", word: "follower" },
{ state: { pollingActive: false, busRole: "follower", instanceThreadName: "Amber", processing: true, processingStatus: "active" }, label: "Amber", token: "warning", word: "active" },
{ state: { pollingActive: false, busRole: "follower", busLifecyclePhase: "electing" }, label: "telegram", token: "warning", word: "electing" },
{ state: { pollingActive: false, busLifecyclePhase: "electing" }, label: "telegram", token: "warning", word: "electing" },
{ state: { pollingActive: false, busRole: "follower", instanceThreadName: "Follower" }, label: "telegram", token: "success", word: "follower" },
{ state: { pollingActive: false, busRole: "follower", instanceThreadName: "Lname" }, label: "Lname", token: "success", word: "follower" },
{ state: { pollingActive: false, busRole: "follower", instanceSlot: "O", instanceThreadName: "extensions Follower" }, label: "extensions Follower", token: "success", word: "follower" },
{ state: { pollingActive: false, busRole: "follower", instanceSlot: "O", instanceThreadName: "Oname" }, label: "Oname", token: "success", word: "follower" },
{ state: { busRole: "leader" }, label: "telegram", token: "success", word: "leader" },
{ state: { busRole: "leader", instanceThreadName: "🌙 A-identity" }, label: "🌙 A-identity", token: "success", word: "leader" },
{ state: { paired: false, processing: true, processingStatus: "queued", queuedStatus: " +1" }, label: "telegram", token: "warning", word: "awaiting pairing", queued: " +1" },
];
for (const { state, label, token, word, queued } of cases) {
const text = buildTelegramStatusBarText(statusBarTheme, { ...base, ...state });
const expected = new RegExp(
`^<accent>${escapeRegExp(label)}</accent> <${token}>\\S+ ${word}</${token}>${queued ? `<success>${escapeRegExp(queued)}</success>` : ""}`,
);
assert.match(text, expected, JSON.stringify(state));
}
assert.match(
buildTelegramStatusBarText(statusBarTheme, {
...base,
error: "typing failed",
}),
"<accent>telegram</accent> <error>error</error> <muted>typing failed</muted>",
/^<accent>telegram<\/accent> <error>\S+ error<\/error> <muted>typing failed<\/muted>$/,
);
});
test("Status bar shows bot, message counters and last activity only while the bridge is up", () => {
const lastAtMs = new Date(2026, 9, 1, 14, 32).getTime();
const connected = {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: false,
queuedStatus: "",
botUsername: "demo_bot",
traffic: { received: 12, sent: 11, lastAtMs },
};
const up = plainStatusText(buildTelegramStatusBarText(statusBarTheme, connected));
assert.match(up, /connected/);
assert.match(up, /@demo_bot/);
assert.match(up, /12 \S*11/);
assert.match(up, /14:32/);
const withoutActivity = plainStatusText(
buildTelegramStatusBarText(statusBarTheme, {
...connected,
traffic: { received: 0, sent: 0 },
}),
);
assert.match(withoutActivity, /0 \S*0/);
assert.doesNotMatch(withoutActivity, /\d\d:\d\d/);
const working = plainStatusText(
buildTelegramStatusBarText(statusBarTheme, {
...connected,
processing: true,
processingStatus: "active",
}),
);
assert.match(working, /active/);
assert.match(working, /@demo_bot/);
assert.match(working, /12 \S*11/);
const offline = plainStatusText(
buildTelegramStatusBarText(statusBarTheme, { ...connected, pollingActive: false }),
);
assert.doesNotMatch(offline, /@demo_bot|14:32/);
assert.match(offline, /disconnected/);
});
test("Status bar tells the operator how to leave a non-working state", () => {
const base = {
hasBotToken: true,
pollingActive: false,
paired: true,
compactionInProgress: false,
processing: false,
queuedStatus: "",
};
assert.match(
plainStatusText(buildTelegramStatusBarText(statusBarTheme, { ...base, hasBotToken: false, paired: false })),
/\/telegram-setup/,
);
const pairing = plainStatusText(
buildTelegramStatusBarText(statusBarTheme, { ...base, paired: false, botUsername: "demo_bot" }),
);
assert.match(pairing, /\/start/);
assert.match(pairing, /@demo_bot/);
assert.match(
plainStatusText(buildTelegramStatusBarText(statusBarTheme, base)),
/\/telegram-connect/,
);
});
test("Status bar follows the host symbol preset and falls back without one", () => {
const state = {
hasBotToken: true,
pollingActive: true,
paired: true,
compactionInProgress: false,
processing: false,
queuedStatus: "",
botUsername: "demo_bot",
traffic: { received: 3, sent: 2, lastAtMs: new Date(2026, 9, 1, 9, 5).getTime() },
};
const ascii = plainStatusText(
buildTelegramStatusBarText(
{
...statusBarTheme,
status: { enabled: "[x]" },
sep: { dot: " - " },
icon: { input: "in:", output: "out:", time: "t:" },
},
state,
),
);
assert.equal(ascii, "telegram [x] connected - @demo_bot - in:3 out:2 - t: 09:05");
const fallback = plainStatusText(buildTelegramStatusBarText(statusBarTheme, state));
assert.match(fallback, /^telegram \S+ connected · @demo_bot · \S*3 \S*2 · \S+ 09:05$/u);
});
test("Status runtime updates the status bar and exposes bridge lines", () => {
const events: string[] = [];
const ctx = {
@@ -390,9 +307,9 @@ test("Status runtime updates the status bar and exposes bridge lines", () => {
}),
});
runtime.updateStatus(ctx, "demo error");
assert.equal(
assert.match(
events[0],
"telegram:<accent>telegram</accent> <error>error</error> <muted>demo error</muted>",
/^telegram:<accent>telegram<\/accent> <error>\S+ error<\/error> <muted>demo error<\/muted>$/,
);
assert.deepEqual(runtime.getStatusLines().slice(0, 3), [
"connection:",
@@ -550,9 +467,9 @@ test("Bridge status runtime stays active while tools run after queue changes", (
},
},
});
assert.equal(
assert.match(
events[0],
"telegram:<accent>telegram</accent> <warning>active</warning>",
/^telegram:<accent>telegram<\/accent> <warning>\S+ active<\/warning>/,
);
});
@@ -589,9 +506,9 @@ test("Bridge status runtime builds status state from live ports", () => {
},
},
});
assert.equal(
assert.match(
events[0],
"telegram:<accent>telegram</accent> <warning>active</warning><success> +1</success>",
/^telegram:<accent>telegram<\/accent> <warning>\S+ active<\/warning><success> \+1<\/success>/,
);
assert.deepEqual(runtime.getStatusLines(), [
"connection:",
+121
View File
@@ -0,0 +1,121 @@
/**
* Regression tests for the Telegram traffic counters
* Covers what counts as a message, last-activity tracking, and the /telegram-status line.
*/
import assert from "node:assert/strict";
import test from "node:test";
import { buildTelegramBridgeStatusLines } from "../lib/status.ts";
import {
createTelegramTrafficCounters,
createTelegramTrafficStatusBridge,
isTelegramOutboundMessageMethod,
} from "../lib/traffic.ts";
test("Traffic counters count both directions and remember the latest activity", () => {
let nowMs = 1_000;
const counters = createTelegramTrafficCounters(() => nowMs);
assert.deepEqual(counters.snapshot(), { received: 0, sent: 0 });
counters.recordReceived();
nowMs = 2_000;
counters.recordSent();
counters.recordSent();
assert.deepEqual(counters.snapshot(), {
received: 1,
sent: 2,
lastAtMs: 2_000,
});
});
test("Only calls that deliver a readable message count as sent", () => {
for (const method of ["sendMessage", "sendRichMessage", "sendPhoto", "sendDocument", "sendVoice"]) {
assert.equal(isTelegramOutboundMessageMethod(method), true, method);
}
for (const method of [
"sendChatAction",
"sendMessageDraft",
"editMessageText",
"setMessageReaction",
"answerCallbackQuery",
"getUpdates",
"toString",
]) {
assert.equal(isTelegramOutboundMessageMethod(method), false, method);
}
});
test("Status lines report the message counters and the time of the last one", () => {
const baseState = {
botUsername: "demo_bot",
allowedUserId: 7,
pollingActive: true,
pendingDispatch: false,
compactionInProgress: false,
activeToolExecutions: 0,
pendingModelSwitch: false,
queuedItems: [],
recentRuntimeEvents: [],
};
const withTraffic = buildTelegramBridgeStatusLines({
...baseState,
traffic: { received: 12, sent: 11, lastAtMs: new Date(2026, 9, 1, 14, 32).getTime() },
});
assert.ok(
withTraffic.includes("- messages: 12 received, 11 sent, last 14:32"),
withTraffic.join("\n"),
);
const withoutTraffic = buildTelegramBridgeStatusLines(baseState);
assert.equal(withoutTraffic.some((line) => line.startsWith("- messages:")), false);
});
test("Traffic bridge counts delivered messages and refreshes the status with the current context", () => {
const counters = createTelegramTrafficCounters();
const refreshed: string[] = [];
let context: string | undefined = "session-a";
const bridge = createTelegramTrafficStatusBridge({
counters,
getContext: () => context,
updateStatus: (ctx: string) => {
refreshed.push(ctx);
},
recordRuntimeEvent: () => {},
});
bridge.onCallSucceeded("sendChatAction");
bridge.onCallSucceeded("editMessageText");
assert.deepEqual(counters.snapshot(), { received: 0, sent: 0 });
assert.deepEqual(refreshed, []);
bridge.onCallSucceeded("sendRichMessage");
assert.equal(counters.snapshot().sent, 1);
assert.deepEqual(refreshed, ["session-a"]);
context = undefined;
bridge.onCallSucceeded("sendMessage");
assert.equal(counters.snapshot().sent, 2);
assert.deepEqual(refreshed, ["session-a"]);
bridge.onAuthorizedMessage("session-b");
assert.equal(counters.snapshot().received, 1);
assert.deepEqual(refreshed, ["session-a", "session-b"]);
});
test("Traffic bridge records a failed status refresh instead of breaking the message path", () => {
const counters = createTelegramTrafficCounters();
const recorded: string[] = [];
const bridge = createTelegramTrafficStatusBridge({
counters,
getContext: () => "session",
updateStatus: () => {
throw new Error("stale ctx");
},
recordRuntimeEvent: (category, error, details) => {
recorded.push(`${category}:${(error as Error).message}:${details?.phase}`);
},
});
bridge.onCallSucceeded("sendMessage");
bridge.onAuthorizedMessage("session");
assert.deepEqual(counters.snapshot().received, 1);
assert.deepEqual(counters.snapshot().sent, 1);
assert.deepEqual(recorded, [
"status:stale ctx:traffic-refresh",
"status:stale ctx:traffic-refresh",
]);
});