From c2d25b31feb6bd3060d933a40f3557053c76a27c Mon Sep 17 00:00:00 2001 From: wassname <1103714+wassname@users.noreply.github.com> Date: Sun, 19 Apr 2026 16:08:23 +0800 Subject: [PATCH] wip --- CHANGELOG.md | 1 + README.md | 6 +++++- docs/architecture.md | 12 +++++++++--- index.ts | 13 +++++++++++++ lib/rendering.ts | 22 ++++++++-------------- package.json | 2 +- tests/rendering.test.ts | 6 ++++-- 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 477771d..7e607c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current +- `[Trace Visibility]` Added `/trace` command and status-menu inline button to toggle visibility of thinking and tool-call blocks in Telegram replies. When enabled (default), streaming previews show compact one-line trace summaries and final replies include quoted trace detail. Impact: operators can see what the model is thinking and which tools it calls without leaving Telegram. - `[Queue UI]` Marked liked high-priority queued Telegram turns with `⬆` in the pi status-bar queue preview. Impact: operators can now distinguish reaction-promoted turns from normal queued prompts at a glance. - `[Docs]` Added short responsibility header comments to every project `.ts` file. Impact: file boundaries are easier to understand while navigating the growing `/lib` split. - `[Naming]` Renamed extracted domain modules and mirrored regression suites to use repo-scoped bare domain filenames such as `api.ts`, `queue.ts`, and `queue.test.ts` instead of repeating `telegram-*` in every path. Impact: the internal topology is easier to scan and stays aligned with the repository-level Telegram scope. diff --git a/README.md b/README.md index 99067c3..fb0d5df 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ Telegram DM bridge for pi. This repository is a fork of the original [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008460b6c1ca036d92322f69d87f626be0fc`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially. +- then llblab did a bunch of work in llblab +- then wassname: + - show tool calls and thinking ## Start Here @@ -18,7 +21,7 @@ It started from upstream commit [`cb34008460b6c1ca036d92322f69d87f626be0fc`](htt Compared to upstream commit `cb34008`, this fork significantly extends and hardens the extension. -- Better Telegram control UI, including an improved `/status` view with inline buttons for model and thinking selection +- Better Telegram control UI, including an improved `/status` view with inline buttons for model and thinking selection, and trace visibility toggle for thinking/tool-call blocks - Interactive model selection improvements, including scoped model lists, thinking-level control for reasoning-capable models, and in-flight restart on a newly selected model for active Telegram-owned runs - Queueing and interaction upgrades, including queue previews, reaction-based prioritization/removal, media-group handling, high-priority control actions, and safer dispatch behavior - Markdown and reply rendering improvements, with richer formatting support, narrow-client-friendly table/list rendering, quote compatibility fixes, and multiple fixes for incorrect Telegram rendering and chunking edge cases @@ -112,6 +115,7 @@ Additional fork-specific controls: - `/status` now has a richer view with inline buttons for model and thinking controls, and joins the high-priority control queue when pi is busy - `/model` opens the interactive model selector, applies idle selections immediately, joins the high-priority control queue when pi is busy, and can restart the active Telegram-owned run on the newly selected model, waiting for the current tool call to finish when needed - `/compact` starts session compaction when pi and the Telegram queue are idle +- `/trace` toggles visibility of thinking and tool-call blocks in Telegram replies (on by default) - Queue reactions: `👍` prioritizes a waiting turn, `👎` removes it ### Send text diff --git a/docs/architecture.md b/docs/architecture.md index 189aa88..0f32c1d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -30,9 +30,9 @@ Current runtime areas include: - Telegram attachment queueing and delivery helpers in `/lib/attachments.ts` - Telegram tool, command, and lifecycle-hook registration helpers in `/lib/registration.ts` - Setup/token prompt helpers in `/lib/setup.ts` -- Markdown and Telegram message rendering helpers in `/lib/rendering.ts` -- Status rendering helpers in `/lib/status.ts` -- Menu/model-resolution, menu-state construction, pure menu-page derivation, pure menu render-payload builders, menu-message runtime, callback parsing, callback entry handling, callback mutation helpers, full model-callback planning and execution, interface-polished callback effect ports, status-thinking callback handling, and UI helpers in `/lib/menu.ts` +- Markdown, Telegram message rendering, and trace display block rendering helpers in `/lib/rendering.ts` +- Status rendering helpers (including trace visibility indicator) in `/lib/status.ts` +- Menu/model-resolution, menu-state construction, pure menu-page derivation, pure menu render-payload builders, menu-message runtime, callback parsing, callback entry handling, callback mutation helpers, full model-callback planning and execution, interface-polished callback effect ports, status-thinking callback handling, trace-toggle callback handling, and UI helpers in `/lib/menu.ts` - Model-switch guard, continuation, and restart helpers in `/lib/model-switch.ts` - Telegram API-bound reply transport wiring and broader event-side orchestration in `index.ts` - Additional domains can be extracted into `/lib/*.ts` as the bridge grows, while keeping `index.ts` as the single entrypoint @@ -103,6 +103,12 @@ Key rules: The renderer is a Telegram-specific formatter, not a general Markdown engine, so rendering changes should be treated as regression-prone. +### Trace Visibility + +When trace visibility is on (default), thinking blocks and tool-call blocks from the assistant are included in both streaming previews and final replies. During streaming, trace blocks appear as compact one-line summaries (e.g. `🧠 Thinking...`, `🔧 tool_name`). In the final transcript, they render as quoted Markdown blocks with more detail. + +Trace visibility is toggled per session via `/trace` or the inline button on the `/status` menu. The state is stored in `traceVisible` (boolean, default `true`) and flows through the rendering helpers in `/lib/rendering.ts`. + ## Streaming And Delivery During generation, the bridge streams previews back to Telegram. diff --git a/index.ts b/index.ts index fc00904..889d11f 100644 --- a/index.ts +++ b/index.ts @@ -2022,6 +2022,12 @@ export default function (pi: ExtensionAPI) { onMessageStart: async (event, _ctx) => { const nextEvent = event as { message: AgentMessage }; if (!activeTelegramTurn || !isAssistantMessage(nextEvent.message)) return; + { + const rawContent = (nextEvent.message as unknown as Record).content; + const rawBlocks = Array.isArray(rawContent) ? rawContent : []; + const blockTypes = rawBlocks.map((b: Record) => b?.type ?? "unknown"); + console.log(`${TELEGRAM_PREFIX} [trace-debug] messageStart role=${(nextEvent.message as unknown as Record).role} blockTypes=${JSON.stringify(blockTypes)}`); + } if (traceVisible) { if (activeTelegramMessageBlocks.length > 0) { activeTelegramTraceBlocks.push(...activeTelegramMessageBlocks); @@ -2056,6 +2062,13 @@ export default function (pi: ExtensionAPI) { previewState = createPreviewState(); } if (traceVisible) { + const rawContent = (nextEvent.message as unknown as Record).content; + const rawBlocks = Array.isArray(rawContent) ? rawContent : []; + const blockTypes = rawBlocks.map((b: Record) => b?.type ?? "unknown"); + if (blockTypes.some((t: string) => t !== "text")) { + console.log(`${TELEGRAM_PREFIX} [trace-debug] message block types: ${JSON.stringify(blockTypes)}`); + console.log(`${TELEGRAM_PREFIX} [trace-debug] non-text blocks: ${JSON.stringify(rawBlocks.filter((b: Record) => b?.type !== "text").map((b: Record) => ({ type: b?.type, keys: Object.keys(b ?? {}) })))}`); + } activeTelegramMessageBlocks = getMessageBlocks(nextEvent.message); previewState.pendingText = buildTelegramAssistantPreviewText( getActiveTracePreviewBlocks(), diff --git a/lib/rendering.ts b/lib/rendering.ts index d5ce24c..a83882f 100644 --- a/lib/rendering.ts +++ b/lib/rendering.ts @@ -55,21 +55,15 @@ export function buildTelegramAssistantPreviewText( traceVisible: boolean, ): string { const sections: string[] = []; - const text = blocks - .filter((block): block is Extract => block.type === "text") - .map((block) => block.text) - .join("") - .trim(); - if (text) { - sections.push(text); - } - if (traceVisible) { - const traceLines = blocks - .map(renderTracePreviewLine) - .filter((line): line is string => !!line); - if (traceLines.length > 0) { - sections.push(traceLines.join("\n")); + for (const block of blocks) { + if (block.type === "text") { + const trimmed = block.text.trim(); + if (trimmed) sections.push(trimmed); + continue; } + if (!traceVisible) continue; + const line = renderTracePreviewLine(block); + if (line) sections.push(line); } return sections.join("\n\n").trim(); } diff --git a/package.json b/package.json index f91d74a..cc11d10 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "url": "https://github.com/llblab/pi-telegram/issues" }, "scripts": { - "test": "node --experimental-strip-types --test tests/*.test.ts" + "test": "node --experimental-strip-types --test --test-force-exit tests/*.test.ts" }, "publishConfig": { "access": "public" diff --git a/tests/rendering.test.ts b/tests/rendering.test.ts index 53c72da..b6e8932 100644 --- a/tests/rendering.test.ts +++ b/tests/rendering.test.ts @@ -27,8 +27,10 @@ test("Assistant trace helpers build compact previews and fuller transcripts", () assert.equal( buildTelegramAssistantPreviewText(blocks as never, true), [ - "Answer intro\n\nFinal answer.", - '[thinking] Need to inspect the config first.\n[tool] read_config {"path":"config.json"}', + "Answer intro", + "[thinking] Need to inspect the config first.", + '[tool] read_config {"path":"config.json"}', + "Final answer.", ].join("\n\n"), ); assert.equal(