/** * Regression tests for Telegram status helpers * Covers runtime diagnostics lines and recent-event redaction/ring-buffer behavior */ import assert from "node:assert/strict"; import test from "node:test"; import { buildTelegramBridgeStatusLines, buildTelegramRuntimeEventLines, buildTelegramStatusBarText, buildTelegramStatusBarLines, clearTelegramStatusLineProviders, createTelegramBridgeStatusRuntime, createTelegramRuntimeDiagnosticsSnapshotScheduler, createTelegramRuntimeEventRecorder, createTelegramRuntimeLogScope, createTelegramStatusHtmlBuilder, createTelegramStatusSnapshot, createTelegramStatusRuntime, getTelegramStatusBarProcessingStatus, formatTelegramWorkingMessage, sanitizeTelegramStatusText, summarizeTelegramStatusError, recordStructuredTelegramRuntimeEvent, recordTelegramRuntimeEvent, registerTelegramStatusLineProvider, type TelegramRuntimeEvent, } from "../lib/status.ts"; test("Status helpers build runtime log scope and persisted snapshot projections", () => { const state = { busRole: "leader" as const, botThreadMode: "enabled" as const, botThreadModeUpdatedAtMs: 10, botThreadModeAction: "probe", instanceSlot: "A", instanceThreadName: "Axial", pollingActive: true, lockState: "active-here", pendingDispatch: true, compactionInProgress: false, activeToolExecutions: 1, pendingModelSwitch: false, queuedItems: [], busFollowers: [{ instanceId: "follower", lastHeartbeatMs: 5 }], topicTargets: [{ instanceId: "leader", status: "active" }], threadReservations: [{ slot: "B", reason: "startup" }], topicSyncObservations: [{ syncStatus: "open", observedAtMs: 9 }], syncState: { pairing: { status: "fresh" } }, recentRuntimeEvents: [], }; assert.deepEqual( createTelegramRuntimeLogScope({ state, instanceId: "instance-1" }), { instanceId: "instance-1", role: "leader", slot: "A", threadName: "Axial", lockState: "active-here", }, ); assert.deepEqual(createTelegramStatusSnapshot(state), { runtime: { busRole: "leader", botThreadMode: "enabled", botThreadModeUpdatedAtMs: 10, botThreadModeAction: "probe", instanceSlot: "A", instanceThreadName: "Axial", pollingActive: true, lockState: "active-here", }, liveRoster: { busFollowers: [{ instanceId: "follower", lastHeartbeatMs: 5 }], topicTargets: [{ instanceId: "leader", status: "active" }], reservations: [{ slot: "B", reason: "startup" }], syncObservations: [{ syncStatus: "open", observedAtMs: 9 }], }, diagnostics: { pendingDispatch: true, compactionInProgress: false, activeToolExecutions: 1, pendingModelSwitch: false, syncState: { pairing: { status: "fresh" } }, threadReconciliation: undefined, recentRuntimeEvents: [], }, }); }); test("Status runtime diagnostics scheduler coalesces snapshot persists", async () => { let scheduled: (() => void) | undefined; let persistCount = 0; const errors: unknown[] = []; const schedule = createTelegramRuntimeDiagnosticsSnapshotScheduler({ persistSnapshot: async () => { persistCount += 1; }, recordError: (error) => errors.push(error), setTimer(callback) { scheduled = callback as () => void; return { unref() {} } as ReturnType; }, }); schedule(); schedule(); scheduled?.(); await Promise.resolve(); assert.equal(persistCount, 1); assert.deepEqual(errors, []); }); const statusBarTheme = { fg: (token: string, text: string) => `<${token}>${text}`, }; 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: "", }; const cases: Array<{ state: Partial[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( `^${escapeRegExp(label)} <${token}>\\S+ ${word}${queued ? `${escapeRegExp(queued)}` : ""}`, ); assert.match(text, expected, JSON.stringify(state)); } assert.match( buildTelegramStatusBarText(statusBarTheme, { ...base, error: "typing failed", }), /^telegram<\/accent> \S+ error<\/error> 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 = { ui: { theme: { fg: (token: string, text: string) => `<${token}>${text}`, }, setStatus: (key: string, text: string) => { events.push(`${key}:${text}`); }, }, }; const runtime = createTelegramStatusRuntime({ getStatusBarState: (_ctx, error) => ({ hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: false, queuedStatus: "", error, }), getBridgeStatusLineState: () => ({ botUsername: "demo_bot", allowedUserId: 7, lockState: "active here", pollingActive: true, lastUpdateId: 10, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }), }); runtime.updateStatus(ctx, "demo error"); assert.match( events[0], /^telegram:telegram<\/accent> \S+ error<\/error> demo error<\/muted>$/, ); assert.deepEqual(runtime.getStatusLines().slice(0, 3), [ "connection:", "- bot: @demo_bot", "- user: 7", ]); }); test("Status runtime keeps theme colors in the TUI through a widget and stays plain elsewhere", () => { const createContext = (mode: string | undefined) => { const events: string[] = []; return { events, ctx: { mode, ui: { theme: statusBarTheme, setStatus: (key: string, text: string | undefined) => { events.push(`status:${key}:${text}`); }, setWidget: ( key: string, content: string[] | undefined, options?: { placement?: string }, ) => { events.push(`widget:${key}:${options?.placement}:${content?.join("|")}`); }, }, }, }; }; const runtime = createTelegramStatusRuntime({ getStatusBarState: () => ({ hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: false, queuedStatus: "", }), getBridgeStatusLineState: () => { throw new Error("not used"); }, }); const tui = createContext("tui"); runtime.updateStatus(tui.ctx); assert.equal(tui.events.length, 2); assert.match(tui.events[0], /^widget:telegram:belowEditor:telegram<\/accent> \S+ connected<\/success>$/); assert.equal(tui.events[1], "status:telegram:undefined"); for (const mode of ["rpc", "json", "print", undefined]) { const other = createContext(mode); runtime.updateStatus(other.ctx); assert.equal(other.events.length, 1, String(mode)); assert.match(other.events[0], /^status:telegram:telegram<\/accent> \S+ connected<\/success>$/); } const noWidgetApi = createContext("tui"); delete (noWidgetApi.ctx.ui as { setWidget?: unknown }).setWidget; runtime.updateStatus(noWidgetApi.ctx); assert.equal(noWidgetApi.events.length, 1); assert.match(noWidgetApi.events[0], /^status:telegram:/); }); test("Error summaries turn transport failures into a short state and next step", () => { const cases: Array<[string, string | undefined, string | undefined, string | undefined]> = [ ["Telegram API getUpdates failed: HTTP 401: Unauthorized", "fatal", "invalid token", "/telegram-setup"], ["Telegram API sendMessage failed: HTTP 429: Too Many Requests: retry after 12", "retrying", "rate limited 12s", "retrying"], ["Telegram API sendMessage failed: HTTP 429", "retrying", "rate limited", "retrying"], ["Conflict: terminated by other getUpdates request; make sure that only one bot instance is running", "attention", "another poller is active", "/telegram-status"], ["Telegram API getUpdates failed: HTTP 502: Bad Gateway", "retrying", "Telegram unavailable", "retrying"], ["Unable to connect. Is the computer able to access the url?", "retrying", "offline", "retrying"], ["fetch failed", "retrying", "offline", "retrying"], ["Telegram API sendRichMessage may have committed before transport failed.", "attention", "delivery unconfirmed", undefined], ["typing failed", undefined, undefined, undefined], ["settingsManager.reload is not a function", undefined, undefined, undefined], ]; for (const [message, kind, label, hint] of cases) { const summary = summarizeTelegramStatusError(message); assert.equal(summary?.kind, kind, message); assert.equal(summary?.label, label, message); assert.equal(summary?.hint, hint, message); } }); test("Status bar shows classified errors with their next step and keeps unknown ones verbatim", () => { const base = { hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: false, queuedStatus: "", }; const fatal = buildTelegramStatusBarText(statusBarTheme, { ...base, error: "Telegram API getUpdates failed: HTTP 401: Unauthorized", }); assert.match(fatal, /\S+ invalid token<\/error>/); assert.match(plainStatusText(fatal), /\/telegram-setup/); const retrying = buildTelegramStatusBarText(statusBarTheme, { ...base, error: "Unable to connect. Is the computer able to access the url?", }); assert.match(retrying, /\S+ offline<\/warning>/); assert.match(plainStatusText(retrying), /retrying/); const attention = buildTelegramStatusBarText(statusBarTheme, { ...base, error: "HTTP 409 Conflict", }); assert.match(attention, /\S+ another poller is active<\/warning>/); assert.match(plainStatusText(attention), /\/telegram-status/); assert.match( buildTelegramStatusBarText(statusBarTheme, { ...base, error: "typing failed" }), /\S+ error<\/error> typing failed<\/muted>/, ); const long = plainStatusText( buildTelegramStatusBarText(statusBarTheme, { ...base, error: "x".repeat(200) }), ); assert.ok(long.includes("…")); assert.equal(long.includes("x".repeat(101)), false); }); test("Status bar lines carry no terminal control sequences from Telegram-controlled text", () => { const hostile = "\u001b]0;pwned\u0007\u001b[31mred\u001b[0m\u202e"; const base = { hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: true, queuedStatus: " +1", instanceThreadName: `thread${hostile}`, botUsername: `bot${hostile}`, queuedPreview: [`queued${hostile}`], }; const unsafe = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e]/; for (const state of [ base, { ...base, error: `boom${hostile}` }, { ...base, paired: false }, ]) { for (const line of buildTelegramStatusBarLines(statusBarTheme, state)) { assert.doesNotMatch(line, unsafe, JSON.stringify(line)); } } assert.equal(sanitizeTelegramStatusText(" a\u001b[0m b\u0000 "), "a [0m b"); }); test("Status bar previews the next queued turns only while the bridge is up", () => { const base = { hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: true, queuedStatus: " +3", }; const three = buildTelegramStatusBarLines(statusBarTheme, { ...base, queuedPreview: ["one", "two", "three"], }); assert.equal(three.length, 2); assert.match(plainStatusText(three[1]), /next: "one" · "two" · \+1 more/); const two = buildTelegramStatusBarLines(statusBarTheme, { ...base, queuedPreview: ["one", "two"], }); assert.match(plainStatusText(two[1]), /next: "one" · "two"$/); for (const state of [ { ...base, queuedPreview: [] }, { ...base, queuedPreview: ["one"], pollingActive: false }, { ...base, queuedPreview: ["one"], paired: false }, { ...base, queuedPreview: ["one"], error: "typing failed" }, ]) { assert.equal(buildTelegramStatusBarLines(statusBarTheme, state).length, 1, JSON.stringify(state)); } }); test("Status bar surfaces failed deliveries only when there are some", () => { const state = { hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: false, queuedStatus: "", traffic: { received: 12, sent: 11, failed: 2 }, }; assert.match(plainStatusText(buildTelegramStatusBarText(statusBarTheme, state)), /12 \S*11 \S*2/); const none = plainStatusText( buildTelegramStatusBarText(statusBarTheme, { ...state, traffic: { received: 12, sent: 11, failed: 0 }, }), ); assert.equal(none.includes("✘"), false); }); test("Status runtime puts the queue preview in the widget and keeps the plain status to one line", () => { const runtime = createTelegramStatusRuntime({ getStatusBarState: () => ({ hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: true, queuedStatus: " +1", queuedPreview: ["one"], }), getBridgeStatusLineState: () => { throw new Error("not used"); }, }); const widgets: string[][] = []; const statuses: Array = []; const ui = { theme: statusBarTheme, setStatus: (_key: string, text: string | undefined) => { statuses.push(text); }, setWidget: (_key: string, content: string[] | undefined) => { widgets.push(content ?? []); }, }; runtime.updateStatus({ mode: "tui", ui }); assert.equal(widgets[0].length, 2); assert.match(plainStatusText(widgets[0][1]), /next: "one"/); runtime.updateStatus({ mode: "rpc", ui }); assert.equal(statuses.at(-1)?.includes("next:"), false); }); test("Working message names the Telegram prompt and ignores control characters", () => { assert.equal(formatTelegramWorkingMessage("say pong"), 'Answering Telegram · "say pong"'); assert.equal(formatTelegramWorkingMessage(" "), "Answering Telegram"); assert.equal(formatTelegramWorkingMessage("a\u001b[0mb"), 'Answering Telegram · "a [0mb"'); }); test("Status lines expose thread reconciliation state", () => { const lines = buildTelegramBridgeStatusLines({ botUsername: "demo_bot", allowedUserId: 7, pollingActive: true, lastUpdateId: 10, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], threadReconciliation: { phase: "cleanup-required", event: "cleanup-required", atMs: 1000, leaderEpoch: 3, pendingProvisionCount: 1, syncActionCount: 2, cleanupActionCount: 1, }, recentRuntimeEvents: [], }); assert.ok(lines.includes("reconciliation:")); assert.ok( lines.includes("- phase: cleanup-required event=cleanup-required epoch=3"), ); assert.ok(lines.includes("- counts: pending=1, sync=2, cleanup=1")); }); test("Status runtime propagates status update failures to safety wrappers", () => { const runtime = createTelegramStatusRuntime({ getStatusBarState: () => ({ hasBotToken: true, pollingActive: true, paired: true, compactionInProgress: false, processing: false, queuedStatus: "", }), getBridgeStatusLineState: () => ({ botUsername: undefined, allowedUserId: undefined, pollingActive: false, lastUpdateId: undefined, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }), }); assert.throws( () => runtime.updateStatus({ ui: { theme: { fg: (_token: string, text: string) => text }, setStatus: () => { throw new Error("ctx is stale after session reload"); }, }, }), /stale after session/, ); }); test("Status bar processing labels prefer the most specific live state", () => { assert.equal( getTelegramStatusBarProcessingStatus({ hasActiveTurn: true, hasPendingDispatch: true, hasPendingModelSwitch: true, activeToolExecutions: 1, queuedItems: 1, }), "model", ); assert.equal( getTelegramStatusBarProcessingStatus({ hasActiveTurn: true, hasPendingDispatch: false, hasPendingModelSwitch: false, activeToolExecutions: 1, queuedItems: 1, }), "active", ); assert.equal( getTelegramStatusBarProcessingStatus({ hasActiveTurn: false, hasPendingDispatch: false, hasPendingModelSwitch: false, activeToolExecutions: 1, queuedItems: 1, }), "active", ); assert.equal( getTelegramStatusBarProcessingStatus({ hasActiveTurn: false, hasPendingDispatch: true, hasPendingModelSwitch: false, activeToolExecutions: 0, queuedItems: 1, }), "dispatching", ); assert.equal( getTelegramStatusBarProcessingStatus({ hasActiveTurn: false, hasPendingDispatch: false, hasPendingModelSwitch: false, activeToolExecutions: 0, queuedItems: 1, }), "queued", ); }); test("Bridge status runtime stays active while tools run after queue changes", () => { const events: string[] = []; const runtime = createTelegramBridgeStatusRuntime({ getConfig: () => ({ botToken: "token", botUsername: "demo_bot", allowedUserId: 7, }), isPollingActive: () => true, getActiveSourceMessageIds: () => undefined, hasActiveTurn: () => false, hasDispatchPending: () => false, isCompactionInProgress: () => false, getActiveToolExecutions: () => 1, hasPendingModelSwitch: () => false, getQueuedItems: () => [], formatQueuedStatus: () => "", getRecentRuntimeEvents: () => [], }); runtime.updateStatus({ ui: { theme: { fg: (token: string, text: string) => `<${token}>${text}`, }, setStatus: (key: string, text: string) => { events.push(`${key}:${text}`); }, }, }); assert.match( events[0], /^telegram:telegram<\/accent> \S+ active<\/warning>/, ); }); test("Bridge status runtime builds status state from live ports", () => { const events: string[] = []; const runtime = createTelegramBridgeStatusRuntime({ getConfig: () => ({ botToken: "token", botUsername: "demo_bot", allowedUserId: 7, lastUpdateId: 99, }), isPollingActive: () => true, getActiveSourceMessageIds: () => [1, 2], hasActiveTurn: () => false, hasDispatchPending: () => true, isCompactionInProgress: () => false, getActiveToolExecutions: () => 3, hasPendingModelSwitch: () => true, getQueuedItems: () => [{ queueLane: "control" as const }], formatQueuedStatus: () => " +1", getRecentRuntimeEvents: () => [ { at: 1000, category: "api", message: "ok" }, ], getRuntimeLockState: () => "active here", }); runtime.updateStatus({ ui: { theme: { fg: (token: string, text: string) => `<${token}>${text}`, }, setStatus: (key: string, text: string) => { events.push(`${key}:${text}`); }, }, }); assert.match( events[0], /^telegram:telegram<\/accent> \S+ active<\/warning> \+1<\/success>/, ); assert.deepEqual(runtime.getStatusLines(), [ "connection:", "- bot: @demo_bot", "- user: 7", "- owner: active here", "", "health:", "- polling: running", "- state: pending dispatch", "- queued turns: 1 (control=1, priority=0, default=0)", "- active tools: 3", "- pending model switch: yes", "", "diagnostics:", "- state: ~/.pi/agent/tmp/telegram/state.json", "- logs: ~/.pi/agent/tmp/telegram/logs.jsonl", "- full dump: /telegram-status --debug", ]); }); test("Bridge status lines render named-profile diagnostic paths", () => { const lines = buildTelegramBridgeStatusLines({ activeProfileName: "work", botUsername: "work_bot", pollingActive: false, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }); assert.ok( lines.includes("- state: ~/.pi/agent/tmp/telegram/state.work.json"), ); assert.ok( lines.includes("- logs: ~/.pi/agent/tmp/telegram/logs.work.jsonl"), ); }); test("Bridge status lines distinguish unknown bot identity from missing config", () => { const base = { allowedUserId: 42, pollingActive: true, lastUpdateId: 100, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }; assert.equal( buildTelegramBridgeStatusLines({ ...base, hasBotToken: true })[1], "- bot: unknown", ); assert.equal( buildTelegramBridgeStatusLines({ ...base, hasBotToken: false })[1], "- bot: not configured", ); }); test("Bridge status lines include role and instance thread name", () => { const lines = buildTelegramBridgeStatusLines({ botUsername: "demo_bot", allowedUserId: 42, busRole: "leader", instanceSlot: "A", instanceThreadName: "A-identity", pollingActive: true, lastUpdateId: 100, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }); assert.deepEqual(lines.slice(0, 5), [ "connection:", "- bot: @demo_bot", "- user: 42", "- role: leader", "- instance: A-identity", ]); }); test("Bridge status lines include sync slice diagnostics", () => { const lines = buildTelegramBridgeStatusLines( { botUsername: "demo_bot", allowedUserId: 42, pollingActive: true, lastUpdateId: 100, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], syncState: { "topic-state": { status: "fresh", updatedAtMs: 2000, lastReconcileAction: "topic-lifecycle", }, "transport-health": { status: "suspect", suspectAtMs: 3000, reason: "rate limited", }, }, recentRuntimeEvents: [], }, { verbose: true }, ); assert.ok(lines.includes("sync:")); assert.ok(lines.includes("- topic-state: fresh reconcile=topic-lifecycle")); assert.ok(lines.includes("- transport-health: suspect reason=rate limited")); }); test("Bridge status lines include bot thread capability diagnostics", () => { const lines = buildTelegramBridgeStatusLines( { botUsername: "demo_bot", allowedUserId: 42, botThreadMode: "disabled", botThreadModeAction: "thread-mode-unavailable", pollingActive: true, lastUpdateId: 100, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }, { verbose: true }, ); assert.ok( lines.includes("- thread mode: disabled reconcile=thread-mode-unavailable"), ); }); test("Bridge status lines include topic binding diagnostics", () => { const lines = buildTelegramBridgeStatusLines( { botUsername: "demo_bot", allowedUserId: 42, pollingActive: true, lastUpdateId: 100, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], topicTargets: [ { instanceId: "inst-a", status: "active", target: { chatId: 42, threadId: 10 }, slot: "B", threadName: "Beacon", syncStatus: "open", lastSyncObservedAtMs: 1000, lastSyncProbeAtMs: 2000, lastReconcileAction: "leader-startup-probe", }, { instanceId: "inst-a", status: "starting", target: { chatId: 42, threadId: 11 }, slot: "C", threadName: "Cedar", }, { instanceId: "inst-b", status: "offline", slot: "D" }, ], threadReservations: [ { instanceId: "old-leader", target: { chatId: 42, threadId: 9 }, slot: "A", reason: "previous-process-still-probes-alive", lastReconcileAction: "leader-topic-previous-instance-still-live", }, ], topicSyncObservations: [ { instanceId: "closed-inst", target: { chatId: 42, threadId: 8 }, slot: "D", syncStatus: "closed", observedAtMs: 3000, lastReconcileAction: "mark-stale", }, ], recentRuntimeEvents: [], }, { verbose: true }, ); assert.deepEqual(lines.slice(18, 21), [ "topics:", "- active bindings: instances=1, targets=2", "- duplicate inst-a: 2 active threads Beacon target 42:10, Cedar target 42:11", ]); assert.ok( lines.includes( "- Beacon target 42:10 sync=open observed=1970-01-01T00:00:01.000Z probed=1970-01-01T00:00:02.000Z reconcile=leader-startup-probe", ), ); assert.ok(lines.includes("- Cedar target 42:11")); assert.ok( lines.includes( "- reservation [A] target 42:9 reason=previous-process-still-probes-alive instance=old-leader reconcile=leader-topic-previous-instance-still-live", ), ); assert.ok( lines.includes( "- sync [D] target 42:8 sync=closed observed=1970-01-01T00:00:03.000Z instance=closed-inst reconcile=mark-stale", ), ); }); test("Bridge status lines include bus follower diagnostics when present", () => { const lines = buildTelegramBridgeStatusLines( { botUsername: "demo_bot", allowedUserId: 42, pollingActive: true, lastUpdateId: 100, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], busNowMs: 20_000, busFollowers: [ { instanceId: "inst-a", cwd: "/repo/a", lastHeartbeatMs: 18_600, target: { chatId: -1007, threadId: 42 }, threadName: "Ember", }, { instanceId: "inst-b", lastHeartbeatMs: 12_000 }, ], recentRuntimeEvents: [], }, { verbose: true }, ); assert.deepEqual(lines.slice(19, 24), [ "bus:", "- followers: 2", "- inst-a: Ember heartbeat 1s ago target -1007:42 /repo/a", "- inst-b: heartbeat 8s ago", "", ]); }); test("Bridge status lines include local bus diagnostics", () => { const lines = buildTelegramBridgeStatusLines( { botUsername: "demo_bot", allowedUserId: 42, pollingActive: false, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], localBus: { leaderSocketPath: "\\\\.\\pipe\\pi-telegram-demo-bus", leaderTransport: "pipe", followerSocketPath: "\\\\.\\pipe\\pi-telegram-demo-follower", followerTransport: "pipe", followerRegistered: true, followerTarget: { chatId: 42, threadId: 9 }, followerThreadName: "Boreal", }, recentRuntimeEvents: [], }, { verbose: true }, ); assert.ok(lines.includes("local bus:")); assert.ok(lines.includes("- follower registered: yes Boreal target 42:9")); assert.ok( lines.includes( "- leader endpoint [pipe]: \\\\.\\pipe\\pi-telegram-demo-bus", ), ); assert.ok( lines.includes( "- follower endpoint [pipe]: \\\\.\\pipe\\pi-telegram-demo-follower", ), ); }); test("Bridge status lines include queue lanes and recent runtime events", () => { const lines = buildTelegramBridgeStatusLines( { botUsername: "demo_bot", allowedUserId: 42, pollingActive: true, lastUpdateId: 100, activeSourceMessageIds: [7, 8], pendingDispatch: true, compactionInProgress: false, activeToolExecutions: 2, pendingModelSwitch: true, queuedItems: [ { queueLane: "control" }, { queueLane: "priority" }, { queueLane: "default" }, { queueLane: "default" }, ], recentRuntimeEvents: [ { at: 1, category: "api:sendMessage", message: "rate limited" }, ], }, { verbose: true }, ); assert.deepEqual(lines, [ "connection:", "- bot: @demo_bot", "- allowed user: 42", "", "polling:", "- state: running", "- last update id: 100", "", "execution:", "- active turn: 7,8", "- pending dispatch: yes", "- compaction: idle", "- active tools: 2", "- pending model switch: yes", "", "queue:", "- queued turns: 4", "- lanes: control=1, priority=1, default=2", "", "recent runtime events:", "- summary: api:sendMessage=1", "- 1970-01-01T00:00:00.001Z api:sendMessage: rate limited", ]); }); test("Status HTML builder binds active model lookup", () => { const model = { provider: "openai", id: "gpt-5", contextWindow: 1000 }; const buildStatusHtml = createTelegramStatusHtmlBuilder({ getActiveModel: () => model, }); const html = buildStatusHtml({ sessionManager: { getEntries: () => [] }, getContextUsage: () => ({ percent: 0, contextWindow: undefined }), isIdle: () => true, modelRegistry: { isUsingOAuth: () => false }, }); assert.match(html, /Status.*idle/s); assert.match(html, /Context.*0\.0%\/1\.0k/s); }); test("Status HTML builder appends Threaded Mode bus role to status row", () => { const buildStatusHtml = createTelegramStatusHtmlBuilder({ getActiveModel: () => undefined, getBridgeStatusLineState: () => ({ hasBotToken: true, botThreadMode: "enabled", busRole: "leader", instanceThreadName: "Dune", pollingActive: true, pendingDispatch: false, compactionInProgress: false, activeToolExecutions: 0, pendingModelSwitch: false, queuedItems: [], recentRuntimeEvents: [], }), }); const html = buildStatusHtml({ sessionManager: { getEntries: () => [] }, getContextUsage: () => ({ percent: 0, contextWindow: 1000 }), isIdle: () => true, modelRegistry: { isUsingOAuth: () => false }, }); assert.match(html, /Status.*idle @leader/s); assert.doesNotMatch(html, /Telegram/s); }); test("Status HTML builder includes extension-provided status lines", () => { clearTelegramStatusLineProviders(); const unregisterCodex = registerTelegramStatusLineProvider( ({ activeModel }) => activeModel?.contextWindow === 1000 ? { label: "codex", value: "████ 23.7h" } : undefined, { id: "@scope/codex" }, ); const unregisterBroken = registerTelegramStatusLineProvider( () => { throw new Error("optional provider failed"); }, { id: "@scope/broken" }, ); try { const buildStatusHtml = createTelegramStatusHtmlBuilder({ getActiveModel: () => ({ contextWindow: 1000 }), }); const html = buildStatusHtml({ sessionManager: { getEntries: () => [] }, getContextUsage: () => ({ percent: 0, contextWindow: undefined }), isIdle: () => true, modelRegistry: { isUsingOAuth: () => false }, }); assert.match(html, /Context.*0\.0%\/1\.0k/s); assert.match(html, /Codex.*████ 23\.7h/s); } finally { unregisterCodex(); unregisterBroken(); clearTelegramStatusLineProviders(); } }); test("Status HTML builder leaves compaction lifecycle to Pi", () => { const buildStatusHtml = createTelegramStatusHtmlBuilder({ getActiveModel: () => undefined, isCompactionInProgress: () => true, }); const html = buildStatusHtml({ sessionManager: { getEntries: () => [] }, getContextUsage: () => ({ percent: 0, contextWindow: 1000 }), isIdle: () => true, modelRegistry: { isUsingOAuth: () => false }, }); assert.match(html, /Status.*idle/s); assert.doesNotMatch(html, /compacting/); }); test("Runtime event lines render the recent-event ring newest first", () => { assert.deepEqual(buildTelegramRuntimeEventLines([]), [ "recent runtime events: none", ]); assert.deepEqual( buildTelegramRuntimeEventLines([ { at: 0, category: "poll", message: "started" }, { at: 1000, category: "api:sendMessage", message: "rate limited" }, ]), [ "recent runtime events:", "- summary: api:sendMessage=1, poll=1", "- 1970-01-01T00:00:01.000Z api:sendMessage: rate limited", "- 1970-01-01T00:00:00.000Z poll: started", ], ); }); test("Structured runtime event recording redacts messages and details", () => { const events: TelegramRuntimeEvent[] = []; recordStructuredTelegramRuntimeEvent( events, { category: "api", error: new Error("token 123:abc failed"), details: { method: "sendMessage", token: "123:abc", retryable: true }, }, { botToken: "123:abc", maxEvents: 3, now: 1000 }, ); assert.deepEqual(events, [ { at: 1000, category: "api", message: "token failed", details: { method: "sendMessage", token: "", retryable: true, }, }, ]); assert.deepEqual(buildTelegramRuntimeEventLines(events), [ "recent runtime events:", "- summary: api:sendMessage=1", '- 1970-01-01T00:00:01.000Z api:sendMessage: token failed (token="", retryable=true)', ]); }); test("Runtime event recording bounds messages and string details", () => { const events: TelegramRuntimeEvent[] = []; recordStructuredTelegramRuntimeEvent( events, { category: "handler", error: new Error("x".repeat(1200)), details: { output: "y".repeat(1200) }, }, { maxEvents: 3, now: 1000 }, ); assert.equal(events[0]?.message.length, 1023); assert.match(events[0]?.message ?? "", /truncated 200 chars/); assert.equal(String(events[0]?.details?.output).length, 1023); assert.match(String(events[0]?.details?.output), /truncated 200 chars/); }); test("Runtime event recorder owns redacted bounded event state", () => { const recorder = createTelegramRuntimeEventRecorder({ getBotToken: () => "123:abc", maxEvents: 1, now: () => 1000, }); recorder.record("api", new Error("token 123:abc failed"), { method: "sendMessage", }); recorder.record("poll", "ok"); assert.deepEqual(recorder.getEvents(), [ { at: 1000, category: "poll", message: "ok" }, ]); recorder.clear(); assert.deepEqual(recorder.getEvents(), []); }); test("Runtime event recording redacts bot tokens and keeps a bounded ring", () => { const events: TelegramRuntimeEvent[] = []; recordTelegramRuntimeEvent(events, "one", new Error("token 123:abc failed"), { botToken: "123:abc", maxEvents: 3, now: 1, }); assert.deepEqual(events, [ { at: 1, category: "one", message: "token failed" }, ]); recordTelegramRuntimeEvent(events, "two", "plain", { botToken: "123:abc", maxEvents: 3, now: 2, }); recordTelegramRuntimeEvent(events, "three", "last", { botToken: "123:abc", maxEvents: 2, now: 3, }); assert.deepEqual(events, [ { at: 2, category: "two", message: "plain" }, { at: 3, category: "three", message: "last" }, ]); });