@llblab/pi-telegram 0.26.0 β†’ 0.26.1

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/AGENTS.md CHANGED
@@ -89,7 +89,7 @@ The core product loop is mobile continuation: start or supervise work in the ter
89
89
  - Instance thread names are provisioner-authored identity metadata, not model-authored output. Fresh Threaded Mode threads select one of five curated compact 4-6 letter Latin names for the assigned slot letter using provisioning timestamp entropy, then create the Telegram thread with that thread name immediately. The title is the thread name itself, not `{slot} {threadName}` or `{slot} β€” {threadName}`. Do not register or reintroduce an agent-facing thread rename tool; removing the extra rename prompt/tool turn is an intentional simplicity constraint. Thread names must mirror into terminal status, live diagnostics, and `[telegram|thread:name]` through one target-aware current-instance identity resolver. Registered follower/leader metadata takes precedence over a stale shared record for the same target, with the record used only as fallback; do not hide a valid baked/custom thread name behind old slot-prefix validation. A follower that later becomes leader keeps its existing name; leadership changes are transport role changes, not identity resets.
90
90
  - Telegram private-chat Threaded Mode treats `All` as an aggregate/control surface, not a durable `General` thread or process launcher, and leader activation proactively creates or reuses its private-DM thread. When the owner writes in an unknown thread, the leader reclaims and routes the first one only if it lacks an active binding; later unknown threads remain intact and receive an in-thread chooser to reroute the captured prompt or restore a stale binding. Destructive cleanup requires explicit confirmation through `thread-reconciler`; another Pi instance still requires a manually started process followed by `/telegram-connect`. Without Threaded Mode, process the message through classic routing.
91
91
  - Telegram extension work should not hold Pi's core agent lifecycle hostage once Pi has produced the semantic result. For Telegram-originated turns, final Telegram delivery, attachment upload, and transport cleanup are extension-owned side effects; schedule them off the critical `agent_end` path when ordering can still be preserved, record failures as runtime events, and keep dispatch of the next Telegram turn behind the delivery task when needed to avoid interleaving previews/finals. Treat a low-level error `agent_end` as retryable while its Telegram turn remains active; only a later successful `agent_end` or Pi's `agent_settled` boundary may finalize and release that turn. Public activity handlers run through isolated asynchronous per-handler queues; lifecycle hooks enqueue normalized events and never await consumer work. Proactive push defaults enabled and requires explicit `assistant.proactivePush: false` to opt out. While enabled, core delivery projects each completed Pi-visible assistant text block from local/autonomous work once and in order; bind admitted blocks to the exact target, profile/token transport stamp, direct leader epoch or follower registration generation, and session generation. Token deltas, hidden reasoning, tool traffic, stale authority, and Telegram-owned turns never enter that projection.
92
- - Core thinking/tool visibility is controlled by file-backed `assistant.activity: "quiet" | "thinking" | "tools" | "verbose"`; every Pi instance reloads the shared config at `agent-start` before admitting activity so process-local cache cannot broaden the selected class. It defaults to `verbose` when absent and remains separate from proactive public prose and assistant Markdown: provider-exposed thinking uses durable ordinary HTML with a standard expandable blockquote, while completed executed tools use native Rich Messages with separate closed details and JSON pre blocks for arguments, retained updates, and result/error evidence. Thinking uses the sentence-case `🧠 Thinking: <level>` header with the current Pi thinking level instead of a generic lifecycle status and renders inline Markdown as Telegram HTML inside its expandable blockquote; tools use sentence-case `πŸ›  <Tool>: <status>` headers, `verbose` enables both, and every activity send/edit disables Telegram link previews while technical evidence also neutralizes HTTP(S) auto-link detection as a client-safe fallback. Keep it quiet by default, exact-target/transport-generation fenced, start-order preserving, boundary-aware, memory/transport bounded, and non-blocking; final delivery waits for admitted technical activity only inside the extension-owned background task, and session replacement abandons queued old-generation work without waiting on old transport. Read legacy `assistant.activityVerbosity` only when `assistant.activity` is absent and remove it on the next Settings write. Never invent unavailable provider-private thinking or replay a possibly committed technical message.
92
+ - Core thinking/tool visibility is controlled by file-backed `assistant.activity: "quiet" | "thinking" | "tools" | "verbose"`; every Pi instance reloads the shared config at `agent-start` before admitting activity so process-local cache cannot broaden the selected class. It defaults to `verbose` when absent and remains separate from proactive public prose and assistant Markdown: provider-exposed thinking uses only a durable ordinary HTML expandable blockquote, while completed executed tools use native Rich Messages whose closed `πŸ›  <Tool>: <status>` root details contain separate closed JSON pre-block details for arguments, retained updates, and result/error evidence. Thinking renders inline Markdown as Telegram HTML without spending a separate header row; `verbose` enables both classes, and every activity send/edit disables Telegram link previews while technical evidence also neutralizes HTTP(S) auto-link detection as a client-safe fallback. Keep it compact by default, exact-target/transport-generation fenced, start-order preserving, boundary-aware, memory/transport bounded, and non-blocking; final delivery waits for admitted technical activity only inside the extension-owned background task, and session replacement abandons queued old-generation work without waiting on old transport. Read legacy `assistant.activityVerbosity` only when `assistant.activity` is absent and remove it on the next Settings write. Never invent unavailable provider-private thinking or replay a possibly committed technical message.
93
93
  - Telegram runtime state should be treated as event-driven reconciliation of extension assumptions against observable Telegram signals, not as a full Telegram bot read-model and not as a reason to query Telegram on every action. Keep a local assumption model for bot identity/capabilities, pairing assumptions, thread support, known thread lifecycle, bound instance targets, reservations, and transport health. Invalidate and reconcile the relevant slice on meaningful events: startup/reload, lifecycle service messages, stale-send/API errors, setup/pairing changes, follower register/prune, explicit status/debug requests, and bounded low-frequency leader health ticks. `lib/sync.ts` owns sync slices, observation intake, invalidation triggers, status/debug freshness, and reconciliation scheduling; it must not promise complete bot-state mirroring because Bot API lacks a complete topic listing surface. `lib/thread-reconciler.ts` owns pure Threaded Mode lifecycle planning and should become the only policy authority for destructive thread cleanup decisions. `threads` owns current Telegram UI thread/tab binding primitives and thread-name helpers mapped to Bot API topic transport, `status` renders projections, and `index.ts` only wires ports. `tmp/telegram/logs.jsonl` is a session-local redacted runtime evidence stream for race debugging and resets on extension start / runtime scope changes; it is diagnostics only, not routing/provisioning authority. `state.json` should be an observable/debug snapshot aligned with `/telegram-status` (extension runtime, bot capabilities including `bot.lastSlot`, runtime role, live roster projection, reservations, diagnostics), not routing/provisioning authority. Because every process on one Telegram profile shares this file, only the active transport lock owner may persist it; followers read current state and gain write authority only after promotion. Status-only writes must refresh disk-backed bindings before serialization so a stale loaded snapshot cannot erase newer leader state. Live bus/runtime state is authoritative; file reservations and fresh capability observations may be startup hints/collision guards only; stale snapshots must re-probe before suppressing bus/topic behavior. Do not persist stale/offline/failed thread target history as source of truth.
94
94
  - Thread bindings are bidirectional. From the Pi-instance side, an instance knows and preserves its target, slot, thread name, lifecycle state, and direct-delivery defaults. From the Telegram-client/bot side, the bridge observes thread creation/close/reopen/stale-send signals and reconciles them into instance binding state. Lifecycle transitions that affect operator understanding should be Telegram-visible when a live transport remains: every successful follower registration/re-registration gets a compact connected notice in the assigned thread, while heartbeat pruning stays silent because it is only liveness suspicion. These notices should use the instance thread name when known and fall back to the slot letter only while the thread is still unnamed. New unknown or command-created temporary threads receive the complete forward plus replace/restore control set and remain preserved until the user chooses one. Successful forward deletes the chooser and temporary thread; successful replace/restore always deletes the chooser, rebinds the temporary thread, and deletes only the replaced old thread. Any destructive cleanup must go through `thread-reconciler`. Unknown `forum_topic_created` service events are observations, not destructive cleanup proof.
95
95
  - The multi-instance bus uses private bot DMs with Telegram private-chat Threaded Mode enabled for the bot. No supergroup, group, or forum configuration is needed; the thread chat is always the private chat with the paired owner. Threaded capability checks must use bot/private-chat evidence such as `getMe.has_topics_enabled`, incoming `message_thread_id`, and topic operation success/failure; do not use group chat metadata as the control-plane truth for Telegram private-chat Threaded Mode.
package/BACKLOG.md CHANGED
@@ -2,30 +2,30 @@
2
2
 
3
3
  _This backlog tracks only open release-relevant work: hotfixes, bounded maintenance, live runtime verification, evidence-gated Telegram client follow-ups, and upstream Pi API blockers. Completed outcomes and validation evidence belong in `CHANGELOG.md`, not in this queue._
4
4
 
5
- ## P0 β€” Rich Tool Activity And Discoverable Defaults Release
5
+ ## P0 β€” Compact Activity Tree Hotfix
6
6
 
7
- Context: Thinking keeps its current persistent HTML expandable quote. Tool activity moves to native Rich Messages so arguments, retained updates, and result/error evidence become separate collapsed details containing compact JSON code blocks. New installations should discover context and activity features through `assistant.timeInjection: "interval"` and `assistant.activity: "verbose"` defaults without migrating explicit existing settings.
7
+ Context: `0.26.0` proved the Rich tool-details direction but retained one standalone line for every thinking header and every tool header. The compact default removes the thinking header entirely and makes each tool call itself the closed root details node containing nested Arguments/Update/Result/Error details.
8
8
 
9
9
  Open work:
10
10
 
11
- - [x] Render completed tool batches through `sendRichMessage` / `editMessageText.rich_message` with one sentence-case tool header followed by separate closed `Arguments`, `Update N`, and `Result`/`Error` details containing `json` pre blocks.
12
- - [x] Preserve tool ordering, coalescing, redaction, truncation, dropped-update evidence, transport/authority fencing, message bounds, final-answer ordering, and commit-unknown no-replay behavior; use the existing HTML representation only for a known-safe Rich capability rejection.
13
- - [x] Change absent Activity mode to `verbose` and absent time injection mode to `interval` with the existing one-hour interval fallback; preserve every explicit stored value without migration.
14
- - [x] Update tests, README, technical docs, durable project rules, changelog, and package metadata for the final behavior.
15
- - [ ] Publish and verify GitHub/npm release evidence for `0.26.0`.
16
- - [ ] Live-smoke Rich tool details on Telegram mobile and Desktop in classic and Threaded Mode, including multi-tool batches, updates, truncation, failures, follower transport, and final-answer ordering.
11
+ - [x] Render thinking as only its existing HTML expandable quote, without a separate `Thinking: <level>` line.
12
+ - [x] Render each Rich tool as one closed root details summary `πŸ›  <Tool>: <status>` whose children are the existing closed JSON evidence details; preserve multi-tool coalescing, fallback, bounds, redaction, ordering, and fencing.
13
+ - [x] Update regressions, README/docs, durable UI rules, changelog, and package metadata.
14
+ - [ ] Publish and verify GitHub/npm release evidence for `0.26.1`.
15
+ - [ ] Live-smoke compact thinking and nested tool trees on Telegram mobile/Desktop in classic and Threaded Mode.
17
16
 
18
- Done when: native Rich tool activity is compact and logically segmented, explicit settings remain authoritative, new defaults are discoverable, automated validation passes, live clients confirm the rendering/routing contract, and the release is published through the repository release flow.
17
+ Done when: thinking costs no standalone header row, each collapsed tool costs one root row, expanding a tool reveals only logical evidence nodes, automated validation passes, live clients confirm nested disclosure behavior, and the hotfix is published.
19
18
 
20
19
  ## P1 β€” Configurable Activity Live Smoke
21
20
 
22
- Context: configurable activity offers quiet, thinking-only, tools-only, and verbose modes. Thinking uses persistent collapsed ordinary HTML instead of draft-only Rich Thinking blocks; completed tools use native Rich details with JSON code blocks.
21
+ Context: `0.26.0` shipped configurable quiet, thinking-only, tools-only, and verbose modes with absent-config `verbose` plus interval time-injection defaults. Thinking uses persistent collapsed ordinary HTML; completed tools use native Rich details with separate JSON code blocks.
23
22
 
24
23
  Open work:
25
24
 
26
- - [ ] Live-smoke all four modes in classic and Threaded Mode on Telegram mobile and Desktop, including progressive thinking edits, multiple sequential tools, oversized output rollover, cancellation during thinking, tool failure, session replacement, follower transport, and final-answer ordering.
25
+ - [ ] Live-smoke all four modes in classic and Threaded Mode on Telegram mobile and Desktop, including progressive headerless thinking edits, collapsed tool roots with nested Arguments/Update/Result/Error details, multiple sequential tools, oversized output rollover, cancellation during thinking, tool failure and known-safe HTML fallback, session replacement, follower transport, and final-answer ordering.
26
+ - [ ] From omitted settings, verify `verbose` Activity and one-hour interval time injection activate without persistence or migration; then verify explicit quiet/hidden and single-class settings remain authoritative across live instances.
27
27
 
28
- Done when: live mobile and Desktop clients confirm the intended mode isolation, persistent disclosures, routing, rollover, cancellation, failure, and final-answer ordering behavior in classic and Threaded Mode.
28
+ Done when: live mobile and Desktop clients confirm mode/default isolation, persistent thinking, structured Rich tool disclosures, routing, rollover, cancellation, failure/fallback, explicit overrides, and final-answer ordering in classic and Threaded Mode.
29
29
 
30
30
  ## P1 β€” Native Windows Runtime Smoke
31
31
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.26.1: Compact Activity Trees
4
+
5
+ - `Thinking Density`: Removed the standalone `🧠 Thinking: <level>` line from provider-exposed thinking so each message consists only of its recognizable expandable HTML quote. Impact: persistent reasoning saves one chat row without changing its bounded, redacted, progressively edited evidence.
6
+ - `Tool Density`: Moved each `πŸ›  <Tool>: <status>` label into a closed native Rich root details node whose children are the existing closed `Arguments`, retained `Update N`, and `Result`/`Error` JSON details. Impact: every collapsed tool costs one row, while opening it reveals the same logically separated evidence tree.
7
+ - `Safety`: Preserved multi-tool coalescing, message bounds, redaction, truncation, ordering, target/transport fencing, known-safe HTML fallback, and no replay after ambiguous sends. Impact: the denser presentation changes layout rather than delivery guarantees.
8
+
3
9
  ## 0.26.0: Rich Tool Activity
4
10
 
5
11
  - `Tool Presentation`: Moved completed tool batches from one ordinary HTML expandable quote to native Rich Messages. Each sentence-case tool header now precedes separate closed `Arguments`, retained `Update N`, and `Result`/`Error` details containing compact JSON code blocks; dropped-update counts remain visible in the first retained update summary. Thinking keeps its distinct persistent HTML expandable quote. Impact: technical history becomes easier to scan, tool phases no longer compete inside one large JSON disclosure, and thinking remains optimized for prose.
package/README.md CHANGED
@@ -101,9 +101,9 @@ The first Telegram user to message the bot becomes the allowed owner. Other user
101
101
  | Model and thinking | Switch model or thinking level from Telegram through safe continuation flows. | Mobile control can adjust execution strategy without tearing down the current session. |
102
102
  | Compaction | Confirm `/compact`, show native active status during compaction, and preserve Telegram-owned turn semantics. | Context maintenance is visible and safe from the phone. |
103
103
  | Draft previews | Show Telegram's native `…typing` indicator whenever the connected instance is doing agent work, or enable Rich Draft previews for streamed answer text. | Local prompts, Telegram turns, and autonomous continuations remain visibly active while draft visibility stays independent from final rendering. |
104
- | Activity | Keep the default `verbose` technical surface, show only `thinking`, show only `tools`, or select `quiet` for answer-only delivery. Every instance reloads this shared file-backed choice before a new agent run, and thinking headers report the current Pi thinking level instead of a generic completion state. | Persistent collapsed technical activity uses sentence-case headers and stays bounded, redacted, target-fenced, density-aware, free of URL previews, and visually separate from semantic assistant answers. |
104
+ | Activity | Keep the default `verbose` technical surface, show only `thinking`, show only `tools`, or select `quiet` for answer-only delivery. Every instance reloads this shared file-backed choice before a new agent run; thinking uses a headerless expandable quote, while each tool uses one closed root row containing nested evidence details. | Persistent collapsed technical activity minimizes chat height and stays bounded, redacted, target-fenced, free of URL previews, and visually separate from semantic assistant answers. |
105
105
  | Assistant rendering | Choose Native Rich Markdown or legacy Markdown-to-HTML for final assistant replies. | Renderer compatibility is explicit instead of being conflated with draft previews. |
106
- | Bridge UI rendering | Render thinking through ordinary expandable HTML with inline emphasis/code, render tools through native Rich details with JSON code blocks, and keep menus, queue controls, status, settings, diagnostics, and sections on Telegram HTML/plain UI. | Harness-owned surfaces remain operationally predictable and visually distinct from model-authored answers. |
106
+ | Bridge UI rendering | Render thinking through headerless expandable HTML with inline emphasis/code, render each tool as a native Rich root details tree containing JSON evidence details, and keep menus, queue controls, status, settings, diagnostics, and sections on Telegram HTML/plain UI. | Harness-owned surfaces remain operationally predictable and visually distinct from model-authored answers. |
107
107
  | Inbound files | Download inbound files to the Pi agent temp directory with size limits. | Screenshots, PDFs, datasets, and artifacts enter Pi as inspectable local files. |
108
108
  | Outbound artifacts | Return generated files through `telegram_attach` during active turns or explicit direct delivery. | Agents send real artifacts as files, not pasted blobs. |
109
109
  | Voice input | Route audio through configured command-template handlers, programmatic handlers, or STT providers. | Voice notes become usable prompt context. |
@@ -172,7 +172,7 @@ Messages sent while Pi is busy become queued turns. Priority lanes support contr
172
172
 
173
173
  ### Native Rich Markdown
174
174
 
175
- Rich Markdown is the default model-answer membrane. Complete assistant and guest model replies use Telegram's native Rich Message APIs. Activity thinking uses persistent ordinary HTML with a standard expandable blockquote, while completed tools use native Rich details with JSON code blocks; `thinking`, `tools`, and `verbose` select the visible classes, while menus, status rows, queue controls, settings, diagnostics, and other operational UI retain explicit Telegram HTML/plain rendering. Three Settings controls keep the layers separate: `Draft previews` toggles streamed answer drafts, `Activity` chooses `quiet` or `verbose` technical activity, and `Assistant rendering` chooses final-answer delivery (`rich` Native Rich Markdown or `html` legacy Markdown-to-HTML).
175
+ Rich Markdown is the default model-answer membrane. Complete assistant and guest model replies use Telegram's native Rich Message APIs. Activity thinking uses persistent headerless expandable HTML, while each completed tool uses one native Rich root details node containing nested JSON evidence details; `thinking`, `tools`, and `verbose` select the visible classes, while menus, status rows, queue controls, settings, diagnostics, and other operational UI retain explicit Telegram HTML/plain rendering. Three Settings controls keep the layers separate: `Draft previews` toggles streamed answer drafts, `Activity` chooses `quiet` or `verbose` technical activity, and `Assistant rendering` chooses final-answer delivery (`rich` Native Rich Markdown or `html` legacy Markdown-to-HTML).
176
176
 
177
177
  ### Files And Artifacts
178
178
 
@@ -315,7 +315,7 @@ Queue reactions are shortcut controls for waiting turns. Promotion reactions (`
315
315
 
316
316
  When `assistant.proactivePush` is enabled and this instance has exact direct or follower transport authority, completed public assistant text blocks from local/autonomous work are sent once and in source order to the instance's authorized target. Visible commentary/checkpoints and the final block use the configured Rich or HTML renderer. Proactive projection excludes reasoning, tool traffic, token deltas, local prompt text, Telegram-owned turns, and stale generations. Each admitted block remains fenced to its exact target, profile/token stamp, leader epoch or follower registration generation, and session generation; non-idempotent acknowledgement ambiguity never authorizes replay.
317
317
 
318
- `assistant.activity` is an independent bridge-owned projection over normalized Activity events. Each process reloads the shared file-backed setting at `agent-start` before activity admission, so multi-instance mode cannot continue projecting a stale broader process-local selection. Omitted values resolve to `verbose`, while invalid values fail closed to `quiet`; `thinking` and `tools` select one technical class, while `verbose` enables both. Provider-exposed thinking uses persistent ordinary HTML with a standard expandable blockquote. Completed executed tools use native Rich Messages: each sentence-case `πŸ› ` header precedes separate closed `Arguments`, retained `Update N`, and `Result`/`Error` details containing JSON pre blocks, with known-safe Rich rejections falling back to the previous HTML disclosure. Thinking uses a `🧠` header carrying the current Pi thinking level and a bounded redacted latest-text window with inline Markdown rendered as Telegram HTML inside the expandable quote. The projection captures the exact target and transport stamp at activity admission, serializes updates, preserves tool-start order, closes coalescing across assistant/thinking boundaries, bounds retained text/update memory plus edit frames and message/tool size, disables previews and HTTP(S) auto-link recognition inside technical evidence, and never replays a possibly committed send. Session generations own independent queues, so replacement drops queued old work without waiting on an old call. Both proactive prose and active-turn final delivery wait for the admitted activity queue inside their extension-owned delivery tasks, preserving technical-before-semantic ordering without delaying Pi lifecycle completion. Settlement, replacement, disconnect, failure, or stale authority clears only local ownership; already-sent activity messages remain in chat.
318
+ `assistant.activity` is an independent bridge-owned projection over normalized Activity events. Each process reloads the shared file-backed setting at `agent-start` before activity admission, so multi-instance mode cannot continue projecting a stale broader process-local selection. Omitted values resolve to `verbose`, while invalid values fail closed to `quiet`; `thinking` and `tools` select one technical class, while `verbose` enables both. Provider-exposed thinking uses persistent ordinary HTML containing only a standard expandable blockquote with a bounded redacted latest-text window and inline Markdown rendered as Telegram HTML. Completed executed tools use native Rich Messages: each closed sentence-case `πŸ›  <Tool>: <status>` root details node contains separate closed `Arguments`, retained `Update N`, and `Result`/`Error` child details with JSON pre blocks; known-safe Rich rejections fall back to the previous HTML disclosure. The projection captures the exact target and transport stamp at activity admission, serializes updates, preserves tool-start order, closes coalescing across assistant/thinking boundaries, bounds retained text/update memory plus edit frames and message/tool size, disables previews and HTTP(S) auto-link recognition inside technical evidence, and never replays a possibly committed send. Session generations own independent queues, so replacement drops queued old work without waiting on an old call. Both proactive prose and active-turn final delivery wait for the admitted activity queue inside their extension-owned delivery tasks, preserving technical-before-semantic ordering without delaying Pi lifecycle completion. Settlement, replacement, disconnect, failure, or stale authority clears only local ownership; already-sent activity messages remain in chat.
319
319
 
320
320
  Telegram prompt guidance is context- and authority-aware. Only an exact direct owner or live registered follower exposes the three pi-telegram model tools, their active-tool metadata, and the compact local bridge suffix. Disconnect or authority loss removes those surfaces for subsequent requests without touching foreign tools; reconnect/recovery restores only the pi-telegram subset that was active before suspension, including across same-process reload. Telegram-originated turns receive a compact pointer to `telegram_help` plus dynamic prompt blocks such as `[voice] delivery: automatic voice`; full voice/button/direct-delivery/Threaded Mode syntax stays in the help tool rather than every system prompt.
321
321
 
package/docs/outbound.md CHANGED
@@ -16,8 +16,8 @@ Proactive blocks use `assistant.rendering` independently of voice policy. Rich m
16
16
 
17
17
  `assistant.activity` defaults to `verbose` when absent and accepts four modes: `quiet`, `thinking`, `tools`, and `verbose`. Explicit stored values remain unchanged; invalid values fail closed to `quiet`. Every Pi instance reloads this shared file-backed mode at `agent-start`, so a setting changed from one thread applies to subsequent runs in the other live instances without requiring process reload. `thinking` shows only provider-exposed thinking, `tools` shows only completed executed tools, and `verbose` shows both without mixing technical UI into assistant Markdown.
18
18
 
19
- - Provider-exposed thinking updates one persistent ordinary HTML message. Its `🧠 Thinking: <level>` header reports the current Pi thinking level, such as `high`, above a standard Telegram `<blockquote expandable>` containing a bounded redacted latest-text window whose inline Markdown emphasis and code render as Telegram HTML instead of leaking raw markers. The bridge sends the message once, edits it only as thinking grows, and leaves the disclosure in chat; it never replaces useful density metadata with a generic completion state or uses Rich drafts/Rich Messages. Providers that expose no thinking produce none.
20
- - Completed executed tools use native `sendRichMessage` block objects. Each tool starts with the sentence-case `πŸ›  <Tool>: <status>` header, then separate closed `Arguments`, retained `Update N`, and `Result`/`Error` details; every detail contains one preformatted `json` block. Updates remain chronological, dropped-update counts appear in the first retained update summary, and arrays of object entries keep the denser `[{ ... }, { ... }]` layout. A known-safe Rich HTTP 400 rejection falls back once to the previous expandable HTML representation; ambiguous non-idempotent outcomes never replay.
19
+ - Provider-exposed thinking updates one persistent ordinary HTML message containing only a standard Telegram `<blockquote expandable>` with a bounded redacted latest-text window. Omitting a separate icon/level header saves one chat row while the disclosure's unique shape remains recognizable. Inline Markdown emphasis and code render as Telegram HTML instead of leaking raw markers. The bridge sends the message once, edits it only as thinking grows, and leaves the disclosure in chat; it never uses Rich drafts/Rich Messages. Providers that expose no thinking produce none.
20
+ - Completed executed tools use native `sendRichMessage` block objects. Each tool is one closed root details node summarized as `πŸ›  <Tool>: <status>`; opening it reveals separate closed `Arguments`, retained `Update N`, and `Result`/`Error` child details, each containing one preformatted `json` block. Updates remain chronological, dropped-update counts appear in the first retained update summary, and arrays of object entries keep the denser `[{ ... }, { ... }]` layout. A known-safe Rich HTTP 400 rejection falls back once to the previous expandable HTML representation; ambiguous non-idempotent outcomes never replay.
21
21
  - Thinking `sendMessage`/`editMessageText` disables link previews and breaks HTTP(S) auto-link recognition inside evidence. Rich tool messages set `skip_entity_detection: true`, so URL-like arguments, updates, and results remain literal code. Consecutive tools coalesce by editing one message only while target, activity, generation, ordering boundary, tool count, and serialized-size bounds still match. Assistant or thinking content closes the batch. A non-idempotent send with unknown commit state is never replayed; failed or ambiguous edits start no fallback send.
22
22
 
23
23
  Thinking retains only a bounded latest-text window, tool updates retain only a bounded latest-entry window, and a session reset abandons queued old-generation work without making the replacement session wait for an old transport call. Final-answer delivery waits for the admitted activity queue inside the extension-owned background delivery task, so completed technical evidence cannot be overtaken by the semantic answer and Pi lifecycle completion remains non-blocking.
package/docs/ui-style.md CHANGED
@@ -34,8 +34,8 @@ Use emoji as stable semantic markers, not decoration. Emoji carry transportable
34
34
  | `πŸ•’` | Time injection / wall-clock context | Time injection settings row and detail card | Clock-face marker with hands; not a generic duration/progress marker. |
35
35
  | `πŸ“Œ` | Proactive push / pinned behavior | Proactive push settings row and detail card | Not generic active/selected state. |
36
36
  | `πŸ”¬` | Activity / technical detail | Activity settings row and detail card | Chooses quiet, thinking, tools, or verbose bridge activity; not a generic diagnostics marker. |
37
- | `🧠` | Model thinking activity | Collapsed persistent thinking disclosure summaries | One marker per thinking message; header text reports the current thinking level such as `high`. |
38
- | `πŸ› ` | Executed tool activity | Native Rich tool headers above closed Arguments/Update/Result/Error details | One marker per tool header; tool result state remains text (`done`/`failed`). |
37
+ | `🧠` | Model thinking controls | Thinking menus and status rows | Thinking activity quotes omit this icon and their header entirely to minimize chat height. |
38
+ | `πŸ› ` | Executed tool activity | Native Rich root details containing closed Arguments/Update/Result/Error child details | One marker per collapsed tool root; tool result state remains text (`done`/`failed`). |
39
39
  | `πŸ“Ž` | Attachment | Attachment summaries, queue rows for attachment-only turns | Not for thread binding. |
40
40
 
41
41
  ### Command And Control Actions
package/index.ts CHANGED
@@ -508,7 +508,6 @@ export default function (pi: Pi.ExtensionAPI) {
508
508
  ActivityVerbosity.createTelegramActivityVerbosityRuntime({
509
509
  getActivityMode: configControls.getActivityVerbosity,
510
510
  refreshActivityMode: configControls.refreshActivityVerbosity,
511
- getThinkingLevel,
512
511
  resolveTarget(event) {
513
512
  return event.target ?? proactivePushTargetGetter();
514
513
  },
@@ -20,7 +20,6 @@ import type {
20
20
  import type { TelegramTarget } from "./target.ts";
21
21
 
22
22
  export const TELEGRAM_TOOL_ACTIVITY_ICON = "πŸ› ";
23
- export const TELEGRAM_THINKING_ACTIVITY_ICON = "🧠";
24
23
  export const TELEGRAM_ACTIVITY_DETAIL_MAX_CHARS = 1_200;
25
24
  export const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3_900;
26
25
  export const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
@@ -213,24 +212,15 @@ function createToolActivityDetail(
213
212
  };
214
213
  }
215
214
 
216
- function renderToolActivityRichBlocks(tool: ToolActivity): TelegramInputRichBlock[] {
215
+ function renderToolActivityRichBlocks(
216
+ tool: ToolActivity,
217
+ ): TelegramInputRichBlock[] {
217
218
  const status = tool.complete
218
219
  ? tool.isError
219
220
  ? "failed"
220
221
  : "done"
221
222
  : "running";
222
- const blocks: TelegramInputRichBlock[] = [
223
- {
224
- type: "paragraph",
225
- text: [
226
- {
227
- type: "bold",
228
- text: `${TELEGRAM_TOOL_ACTIVITY_ICON} ${capitalizeActivityLabel(tool.name)}:`,
229
- },
230
- " ",
231
- { type: "code", text: status },
232
- ],
233
- },
223
+ const evidenceBlocks: TelegramInputRichBlock[] = [
234
224
  createToolActivityDetail("Arguments", tool.args),
235
225
  ];
236
226
  tool.updates.forEach((update, index) => {
@@ -239,14 +229,29 @@ function renderToolActivityRichBlocks(tool: ToolActivity): TelegramInputRichBloc
239
229
  index === 0 && tool.droppedUpdates > 0
240
230
  ? ` Β· ${tool.droppedUpdates} earlier omitted`
241
231
  : "";
242
- blocks.push(createToolActivityDetail(`Update ${number}${omitted}`, update));
232
+ evidenceBlocks.push(
233
+ createToolActivityDetail(`Update ${number}${omitted}`, update),
234
+ );
243
235
  });
244
236
  if (tool.complete && tool.result !== undefined) {
245
- blocks.push(
237
+ evidenceBlocks.push(
246
238
  createToolActivityDetail(tool.isError ? "Error" : "Result", tool.result),
247
239
  );
248
240
  }
249
- return blocks;
241
+ return [
242
+ {
243
+ type: "details",
244
+ summary: [
245
+ {
246
+ type: "bold",
247
+ text: `${TELEGRAM_TOOL_ACTIVITY_ICON} ${capitalizeActivityLabel(tool.name)}:`,
248
+ },
249
+ " ",
250
+ { type: "code", text: status },
251
+ ],
252
+ blocks: evidenceBlocks,
253
+ },
254
+ ];
250
255
  }
251
256
 
252
257
  export function renderTelegramToolActivityRichMessage(
@@ -266,14 +271,8 @@ function isKnownSafeRichActivityRejection(error: unknown): boolean {
266
271
  return error instanceof Error && /HTTP 400: Bad Request:/i.test(error.message);
267
272
  }
268
273
 
269
- export function renderTelegramThinkingActivityHtml(
270
- text: string,
271
- thinkingLevel: string,
272
- ): string {
273
- return [
274
- `<b>${TELEGRAM_THINKING_ACTIVITY_ICON}&#160; Thinking:</b> <code>${escapeHtml(thinkingLevel)}</code>`,
275
- `<blockquote expandable>${renderThinkingActivityEvidenceHtml(text)}</blockquote>`,
276
- ].join("\n");
274
+ export function renderTelegramThinkingActivityHtml(text: string): string {
275
+ return `<blockquote expandable>${renderThinkingActivityEvidenceHtml(text)}</blockquote>`;
277
276
  }
278
277
 
279
278
  export interface TelegramActivityVerbosityRuntime {
@@ -286,7 +285,6 @@ export interface TelegramActivityVerbosityRuntime {
286
285
  export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
287
286
  getActivityMode: () => "quiet" | "thinking" | "tools" | "verbose";
288
287
  refreshActivityMode?: () => Promise<void>;
289
- getThinkingLevel: () => string;
290
288
  resolveTarget: (event: TelegramActivityEvent) => TelegramTarget | undefined;
291
289
  captureAuthority: () => TAuthority;
292
290
  isAuthorityActive: (authority: TAuthority) => boolean;
@@ -375,7 +373,7 @@ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
375
373
  ? `… [${omitted} earlier chars omitted]\n${retained}`
376
374
  : retained,
377
375
  );
378
- body = renderTelegramThinkingActivityHtml(text, deps.getThinkingLevel());
376
+ body = renderTelegramThinkingActivityHtml(text);
379
377
  if (body.length <= TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS) break;
380
378
  retained = retained.slice(-Math.max(1, Math.floor(retained.length * 0.75)));
381
379
  } while (retained.length > 1);
@@ -285,7 +285,6 @@ export type TelegramRichText =
285
285
  | { type: "bold" | "code"; text: TelegramRichText };
286
286
 
287
287
  export type TelegramInputRichBlock =
288
- | { type: "paragraph"; text: TelegramRichText }
289
288
  | { type: "pre"; text: TelegramRichText; language?: string }
290
289
  | {
291
290
  type: "details";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"