@llblab/pi-telegram 0.28.0 → 0.30.0

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
@@ -42,6 +42,8 @@ Keep each fact in one authoritative layer:
42
42
  - `/api/*.ts`: Stable public package membranes documented in `docs/public-api.md`.
43
43
  - `/lib/*.ts`: Flat, cohesive runtime domains; package-private unless re-exported through `/api`.
44
44
  - `/tests/*.test.ts`: Domain-mirrored suites; `tests/integration.test.ts` owns cross-domain runtime flows.
45
+ - `/skills/telegram-bridge`: Stable agent operating protocol for Telegram turns, delivery, actions, Threaded Mode, and diagnosis.
46
+ - `/skills/control-surface`: Optional agent-generated cybernetic control surface over truthful domain evidence, workflows, and choices; it remains independent from the bridge skill and owns no parallel state.
45
47
  - `/.agents/skills/telegram-bot`: Bot API lookup guidance and vendored `api.md`; keep the reference intact.
46
48
  - `/.agents/skills/domain-dag`: Repository architecture guidance and validator.
47
49
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  > Each release keeps at most 8 outcome records of at most 512 characters.
4
4
 
5
+ ## 0.30.0: Generated Control Surfaces
6
+
7
+ - `Generated Control Surface`: Replaced the CLI-bound Button Console with a transport-aware Control Surface Skill that proactively generates truthful contextual controls when they shorten feedback for workflows, stateful systems, navigation, Actor supervision, and decisions while preserving domain ownership, ordinary prompt authority, confirmation boundaries, and stateless regeneration.
8
+ - `Action Markup`: Standardized `telegram_button` and `telegram_voice` on one colon-free action marker for both JSON and attribute payloads. Colon-prefixed forms are rejected instead of remaining a redundant compatibility syntax.
9
+
10
+ ## 0.29.0: Skill-First Agent Operation
11
+
12
+ - `Bundled Skills`: Added discoverable `telegram-bridge` and `button-console` Skills through package metadata and source-checkout resource discovery. Stable turn, delivery, action, Threaded Mode, formatting, handler, and diagnosis guidance now lives in the bridge Skill, while Button Console provides optional evidence-backed CLI navigation.
13
+ - `Model Context Contraction`: Removed the redundant `telegram_help` model tool and its repeated guidance implementation. Compact authority-aware prompts route agents to the bundled Skill, while disconnect/recovery now suppresses and restores only the two delivery tools without affecting foreign tools.
14
+
5
15
  ## 0.28.0: Durable Inbound And Protocol Reconstruction
6
16
 
7
17
  - `Owned Polling Request`: Only `getUpdates` gets an automatic budget: Telegram long-poll timeout plus 10 seconds. Poller abort settles requests and API retry waits; broad ordinary, media, and follower budgets were removed.
package/README.md CHANGED
@@ -188,11 +188,11 @@ Inbound files land under `<agent-dir>/tmp/telegram` and default to a 50 MiB limi
188
188
 
189
189
  ### Voice And Media
190
190
 
191
- Voice notes, audio, images, PDFs, and other media can pass through configured inbound handlers, programmatic handlers, or registered STT providers. Outbound voice can use configured `outboundHandlers` or registered TTS providers; `pi-telegram` owns reply policy and Telegram transport, while providers own synthesis. Explicit `telegram_voice` actions accept either a JSON object or compact double-quoted attributes, with equivalent `text` and `value` payload keys and an optional format-neutral colon after the action name.
191
+ Voice notes, audio, images, PDFs, and other media can pass through configured inbound handlers, programmatic handlers, or registered STT providers. Outbound voice can use configured `outboundHandlers` or registered TTS providers; `pi-telegram` owns reply policy and Telegram transport, while providers own synthesis. Explicit `telegram_voice` actions accept either a JSON object or compact double-quoted attributes, with equivalent `text` and `value` payload keys and one colon-free action marker.
192
192
 
193
193
  ### Buttons And Callbacks
194
194
 
195
- Assistant replies can include top-level hidden `telegram_button` comments using either a JSON object or compact double-quoted attributes. Buttons use `label` plus `prompt`, or the compact `value` key when both are identical. The optional colon after the action name never changes format detection. The bridge strips the comments from visible text, renders inline buttons, and routes callbacks back into Pi as queued prompts or extension-owned callback actions. Button-only replies receive the standard `☑️ **Choose an option:**` heading as automatic visible fallback text. Once a generated prompt button is accepted, only that exact button switches to its optional `selected_style` (`primary` blue by default, `success` green, or `danger` red) without altering its agent-authored label or emoji; every style still queues the selected prompt.
195
+ Assistant replies can include top-level hidden `telegram_button` comments using either a JSON object or compact double-quoted attributes. Buttons use `label` plus `prompt`, or the compact `value` key when both are identical. The action marker is colon-free for both payload forms. The bridge strips the comments from visible text, renders inline buttons, and routes callbacks back into Pi as queued prompts or extension-owned callback actions. Button-only replies receive the standard `☑️ **Choose an option:**` heading as automatic visible fallback text. Once a generated prompt button is accepted, only that exact button switches to its optional `selected_style` (`primary` blue by default, `success` green, or `danger` red) without altering its agent-authored label or emoji; every style still queues the selected prompt.
196
196
 
197
197
  ### Threaded Mode And Multi-Instance Bus
198
198
 
@@ -257,7 +257,7 @@ Durable inbound admission is a **process-crash recovery** guarantee. Atomic priv
257
257
 
258
258
  Telegram is a companion surface around a live Pi runtime, not a second runtime. It can compact the current session, but it cannot create, resume, fork, browse, or switch sessions until Pi exposes safe public extension APIs for those operations.
259
259
 
260
- A Telegram prompt is a normal model turn in the active Pi session and therefore inherits that session's active post-compaction context; the bridge does not make token cost proportional only to the new mobile message. Current releases keep per-turn guidance small and transient, with detailed bridge instructions available on demand through `telegram_help` instead of persisted in every user turn. Disconnecting removes pi-telegram's model tools and prompt guidance from later requests until direct ownership or follower registration returns, without changing other active Pi tools. Pi session JSONL contains model history; profile-scoped pi-telegram `logs*.jsonl` contains redacted operational events and is never model context.
260
+ A Telegram prompt is a normal model turn in the active Pi session and therefore inherits that session's active post-compaction context; the bridge does not make token cost proportional only to the new mobile message. The bundled `telegram-bridge` Skill owns agent operation and `control-surface` provides optional evidence-backed controls for workflows, stateful systems, navigation, supervision, and decisions—not only terminal operations. Disconnecting removes pi-telegram's delivery tools and transient routing guidance from later requests until direct ownership or follower registration returns, without changing other active Pi tools. Pi session JSONL contains model history; profile-scoped pi-telegram `logs*.jsonl` contains redacted operational events and is never model context.
261
261
 
262
262
  ## Documentation Map
263
263
 
@@ -47,7 +47,7 @@ A Telegram destination follows a Pi instance, not an immutable Pi session file.
47
47
 
48
48
  Pi session JSONL and pi-telegram runtime JSONL serve different purposes. Pi session files contain model conversation, tool, usage, branch, and compaction entries. Profile-scoped `logs.jsonl` / `logs.<profile>.jsonl` contain redacted bridge operations from one or more instances and never become model context. Sharing a Telegram profile or working directory does not by itself merge Pi session identities or model histories.
49
49
 
50
- A Telegram prompt is a normal Pi model turn. It inherits the active post-compaction context just like a TUI prompt in the same session; pi-telegram does not promise context isolation or token cost proportional only to the new message. Current prompt guidance uses a small transient system note and the on-demand `telegram_help` tool instead of persisting the former large guidance suffix in every user turn. Existing session files created by older versions may still contain those historical repeated suffixes until session replacement or compaction removes them from active context.
50
+ A Telegram prompt is a normal Pi model turn. It inherits the active post-compaction context just like a TUI prompt in the same session; pi-telegram does not promise context isolation or token cost proportional only to the new message. The bundled `telegram-bridge` Skill owns agent operation, while a small authority-aware system note routes applicable turns to it. Existing session files created by older versions may still contain historical repeated guidance until session replacement or compaction removes it from active context.
51
51
 
52
52
  The repository uses a **Flat Domain DAG**:
53
53
 
@@ -324,7 +324,7 @@ Final delivery attaches reply metadata only where requested. Reply parameters ap
324
324
 
325
325
  Outbound files staged during an active Telegram turn are delivered after that turn completes. They use `telegram_attach`, are checked atomically per tool call, and use configurable size limits before photo/document upload. When no Telegram turn is active, `telegram_attach` sends files immediately to the paired/default chat, an assigned follower thread, or an explicit `chat_id` plus optional `thread_id`; `telegram_message` provides direct local/TUI Markdown text delivery for explicit user requests and runs the same `telegram_button` markup planner so buttons attach to that text message. Direct local/TUI delivery is singleton-controlled: classic mode requires this Pi instance to own `/telegram-connect`, while Threaded Mode followers must be registered and route through the leader-owned transport. Already accepted active-turn reply/attachment delivery remains session-local.
326
326
 
327
- Assistant-authored final-message actions use hidden top-level comments. Both actions accept a JSON object or double-quoted HTML-like attributes; an optional colon after the action name is format-neutral and stripped before payload detection:
327
+ Assistant-authored final-message actions use hidden top-level comments. Both actions accept a JSON object or double-quoted HTML-like attributes after one colon-free action marker; colon-prefixed payloads are rejected:
328
328
 
329
329
  - `telegram_voice` creates voice reply artifacts through configured outbound handlers, programmatic voice handlers, or registered synthesis providers.
330
330
  - `telegram_button` creates inline buttons whose callbacks enqueue the configured prompt text as a normal Telegram prompt turn.
@@ -362,7 +362,7 @@ Complete intermediate assistant text blocks from Telegram-originated activity ar
362
362
 
363
363
  `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 `<Tool>: <status>` root details node renders snake-case names as title words while preserving an uppercase two- or three-letter repeated prefix per word, then the native disclosure chevron reveals an open-by-default `arguments` child plus closed retained `update N` and `result`/`error` child details with lowercase monospaced, marker-free summaries and 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.
364
364
 
365
- 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.
365
+ Telegram prompt guidance is context- and authority-aware. The package and source-checkout extension both contribute `telegram-bridge` plus the optional `control-surface` Skill through Pi resource discovery. The latter generates transient evidence-backed controls over domain-owned workflows, systems, navigation, supervision, and decisions without creating parallel application state. Only an exact direct owner or live registered follower exposes the two pi-telegram delivery tools, their active-tool metadata, and the compact routing suffix. Disconnect or authority loss removes those tool 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 route to the stable Skill contract and retain dynamic blocks such as `[voice] delivery: automatic voice`; the Skill and public documentation own syntax, target routing, Threaded Mode behavior, and diagnostics.
366
366
 
367
367
  ## In-Flight Model Switching
368
368
 
package/docs/outbound.md CHANGED
@@ -135,12 +135,12 @@ Assistant replies can include hidden voice actions in either supported payload f
135
135
  ```md
136
136
  Full text answer stays here.
137
137
 
138
- <!-- telegram_voice: {"value":"Short spoken companion summary.","lang":"ru","rate":"+30%"} -->
138
+ <!-- telegram_voice {"value":"Short spoken companion summary.","lang":"ru","rate":"+30%"} -->
139
139
 
140
140
  <!-- telegram_voice text="Short spoken companion summary." lang="ru" rate="+30%" -->
141
141
  ```
142
142
 
143
- The bridge strips the comment from Telegram text. On `agent_end`, it maps each `telegram_voice` action to a provider call, generates one file per action, and sends each file as an independent Telegram-native voice message. Equivalent `text` or `value` supplies the spoken payload, with explicit `text` taking precedence when both appear; `lang` and `rate` are optional. The colon after `telegram_voice` is optional for both JSON and attributes and never changes format detection. Use JSON for long or escaped text and encode line breaks inside JSON strings as `\n`. The opening marker must start at column zero on a top-level line outside fenced code, quotes, lists, and indented examples; otherwise it remains literal Markdown.
143
+ The bridge strips the comment from Telegram text. On `agent_end`, it maps each `telegram_voice` action to a provider call, generates one file per action, and sends each file as an independent Telegram-native voice message. Equivalent `text` or `value` supplies the spoken payload, with explicit `text` taking precedence when both appear; `lang` and `rate` are optional. A colon after `telegram_voice` is rejected so both payload forms share one unambiguous action marker. Use JSON for long or escaped text and encode line breaks inside JSON strings as `\n`. The opening marker must start at column zero on a top-level line outside fenced code, quotes, lists, and indented examples; otherwise it remains literal Markdown.
144
144
 
145
145
  ## Buttons Markup
146
146
 
@@ -149,17 +149,17 @@ Assistant replies can include independent button actions in the same two payload
149
149
  ```md
150
150
  I can continue.
151
151
 
152
- <!-- telegram_button: {"label":"Continue","prompt":"Continue with the current plan.","selected_style":"primary"} -->
152
+ <!-- telegram_button {"label":"Continue","prompt":"Continue with the current plan.","selected_style":"primary"} -->
153
153
 
154
154
  <!-- telegram_button label="Show risks" prompt="List the main risks first." selected_style="danger" -->
155
155
 
156
- <!-- telegram_button: {"value":"Done"} -->
156
+ <!-- telegram_button {"value":"Done"} -->
157
157
  ```
158
158
 
159
159
  Rules:
160
160
 
161
161
  - JSON objects and double-quoted HTML-like attributes are the only accepted payload forms; shorthand, body, paired-comment, unquoted-attribute, and single-quoted-attribute forms are rejected.
162
- - The colon after `telegram_button` is optional for both forms and never changes format detection.
162
+ - A colon after `telegram_button` is rejected so both payload forms share one unambiguous action marker.
163
163
  - Use `label` plus `prompt`, or the compact `value` key when both strings are identical. Explicit `label` or `prompt` takes precedence over its `value` fallback. Use JSON with `\n` escapes for multiline prompts.
164
164
  - The opening marker must start at column zero on a top-level line outside fenced code, quotes, lists, and indented examples; otherwise it remains literal Markdown.
165
165
  - Use one comment per button; this mirrors HTML's singular element model and avoids a nested button DSL.
@@ -176,8 +176,8 @@ Buttons are built in and do not need a command template because they are pure Te
176
176
  The extension injects prompt guidance by context:
177
177
 
178
178
  - If no bot token is configured, no Telegram bridge suffix is injected.
179
- - For ordinary local/TUI prompts, the agent only sees compact direct-delivery guidance: use `telegram_attach` or `telegram_message` when the user asks to send something to Telegram, and otherwise answer locally as normal.
180
- - For Telegram-originated turns, the prompt carries only minimal mobile/reply/file guidance; agents can call `telegram_help()` for full voice/button/direct-delivery/Threaded Mode/formatting/debug details.
179
+ - For ordinary local/TUI prompts, the compact routing note points to the bundled `telegram-bridge` Skill and forbids Telegram use unless explicitly requested.
180
+ - For Telegram-originated turns, the compact note routes the agent to `telegram-bridge`, which owns voice/button/direct-delivery/Threaded Mode/formatting/debug guidance.
181
181
  - For Telegram-originated turns, write the full technical answer as normal Markdown.
182
182
  - Add `telegram_voice` with either a JSON object or double-quoted attributes when a Telegram-native voice message is useful. A companion summary is optional, no specific summary format is required.
183
183
  - Add `telegram_button` with either a JSON object or double-quoted attributes. Use `label` plus `prompt`, or `value` when they are identical; `selected_style` is optional. A button-only reply may omit parent text because the bridge supplies `☑️ **Choose an option:**` automatically.
@@ -63,9 +63,9 @@ This command surface is a mobile companion subset, not a raw terminal-command br
63
63
 
64
64
  - `telegram_attach(paths, chat_id?, thread_id?, caption?)` is the stable artifact delivery tool for generated files. During Telegram turns it queues files for the active reply; with `assistant.rendering: "rich"`, exactly one PNG/JPEG, MP4, or MP3 artifact plus non-empty final Markdown can become one reply-anchored Rich Message. HTML mode, multiple/unsupported files, Guest Mode, and voice outputs retain their established paths. Outside Telegram turns the tool sends files directly to the paired/default chat, the registered follower's assigned thread, or an explicit `chat_id` plus optional `thread_id` when this Pi instance owns `/telegram-connect` or is registered with the multi-instance bus.
65
65
  - `telegram_message(text, chat_id?, thread_id?)` sends a direct Telegram Markdown message when this Pi instance owns `/telegram-connect` or is registered with the multi-instance bus. During an active Telegram turn, omitted targeting and an explicit target equal to that turn are rejected so the ordinary final-reply path remains the sole current-target response; an explicit different chat/thread target remains allowed for requested cross-target delivery. Outside active turns, paired/default local/TUI delivery remains unchanged. Top-level `telegram_button` comments inside `text` are parsed with the same planner used for normal replies and attached to that message; buttons are never standalone Telegram messages.
66
- - `telegram_help()` returns detailed agent-facing guidance for pi-telegram delivery actions, Threaded Mode, formatting, and debugging. The regular prompt only points agents at this tool instead of repeating the full guidance on every turn. `telegram_attach`, `telegram_message`, and `telegram_help` remain registered but are model-active only while this instance owns direct transport or holds a live follower registration; disconnect/loss suppresses their schemas and prompt metadata, and recovery restores only the operator's previously active pi-telegram subset.
66
+ - The bundled `telegram-bridge` Skill owns action syntax, target routing, Threaded Mode, formatting, and profile-specific debugging guidance. The regular prompt routes applicable turns to that Skill. `telegram_attach` and `telegram_message` remain registered but are model-active only while this instance owns direct transport or holds a live follower registration; disconnect/loss suppresses their schemas and prompt metadata, and recovery restores only the operator's previously active pi-telegram subset.
67
67
  - `telegram_voice` hidden comments request Telegram-native voice delivery through either a JSON object or double-quoted attributes. Equivalent `text` or `value` supplies the spoken payload; explicit `text` takes precedence.
68
- - `telegram_button` hidden comments create inline buttons whose taps enqueue prompts through the same two payload forms. Use `label` plus `prompt`, or `value` when both strings are identical. The colon after either action name is optional and does not select the format. Use top-level column-zero comments outside code, quotes, lists, and indented examples; do not emit standalone button actions.
68
+ - `telegram_button` hidden comments create inline buttons whose taps enqueue prompts through the same two payload forms. Use `label` plus `prompt`, or `value` when both strings are identical. Both action names use one colon-free marker; colon-prefixed payloads are rejected. Use top-level column-zero comments outside code, quotes, lists, and indented examples; do not emit standalone button actions.
69
69
 
70
70
  Prompt guidance is context-aware: local/TUI prompts see only explicit direct-delivery guidance, while Telegram-originated turns receive the full action-comment syntax and phone-width output contract.
71
71
 
package/index.ts CHANGED
@@ -39,6 +39,7 @@ import * as Replies from "./lib/replies.ts";
39
39
  import * as Routing from "./lib/routing.ts";
40
40
  import * as Runtime from "./lib/runtime.ts";
41
41
  import * as Sections from "./lib/sections.ts";
42
+ import * as Skills from "./lib/skills.ts";
42
43
  import * as Status from "./lib/status.ts";
43
44
  import * as Sync from "./lib/sync.ts";
44
45
  import * as TelegramApi from "./lib/telegram-api.ts";
@@ -60,6 +61,7 @@ const telegramBusProtocolIdentity =
60
61
  // --- Extension Runtime ---
61
62
 
62
63
  export default function (pi: Pi.ExtensionAPI) {
64
+ Skills.registerTelegramSkillDiscovery(pi);
63
65
  const piRuntime = Pi.createExtensionApiRuntimePorts(pi);
64
66
  const {
65
67
  getActiveTools,
package/lib/bindings.ts CHANGED
@@ -404,9 +404,6 @@ export function registerTelegramCommandsAndTools({
404
404
  sendMarkdownReply(chatId, undefined, markdown, options),
405
405
  recordRuntimeEvent,
406
406
  });
407
- Prompts.registerTelegramHelpTool(pi, {
408
- getActiveProfileName: configStore.getActiveProfileName,
409
- });
410
407
  Commands.registerTelegramBridgeCommands(pi, {
411
408
  promptForConfig: async (ctx, profileName) => {
412
409
  const nextProfileName = profileName ?? undefined;
@@ -176,11 +176,11 @@ export function parseTelegramActionPayload(
176
176
  ): Record<string, unknown> | undefined {
177
177
  const parsed = parseTopLevelTelegramComment(comment, command);
178
178
  if (!parsed) return undefined;
179
+ if (parsed.head.trimStart().startsWith(":")) return undefined;
179
180
  const source = [parsed.head, parsed.body]
180
181
  .filter((part): part is string => part !== undefined)
181
182
  .join("\n")
182
- .trim()
183
- .replace(/^:\s*/, "");
183
+ .trim();
184
184
  if (!source) return undefined;
185
185
  if (source.startsWith("{")) {
186
186
  try {
package/lib/prompts.ts CHANGED
@@ -4,19 +4,16 @@
4
4
  * Owns Telegram-specific system prompt suffixes injected into pi agent turns
5
5
  */
6
6
 
7
- import { Type } from "@sinclair/typebox";
8
-
9
- import { getTelegramDiagnosticsDisplayPaths } from "./paths.ts";
10
- import type { BeforeAgentStartEvent, ExtensionAPI } from "./pi.ts";
7
+ import type { BeforeAgentStartEvent } from "./pi.ts";
11
8
  import { TELEGRAM_PREFIX } from "./turns.ts";
12
9
 
13
10
  const LOCAL_SYSTEM_PROMPT_SUFFIX = `
14
11
 
15
- Telegram bridge available. Do not use it from local/TUI prompts unless explicitly asked.`;
12
+ Telegram bridge available. Load the \`telegram-bridge\` Skill for Telegram delivery, actions, Threaded Mode, or diagnosis. Do not use Telegram from local/TUI prompts unless explicitly asked.`;
16
13
 
17
14
  const TELEGRAM_TURN_SYSTEM_PROMPT_SUFFIX = `
18
15
 
19
- Telegram turn note: Call \`telegram_help\` if you need the pi-telegram bridge action contract.`;
16
+ Telegram turn note: Load and follow the \`telegram-bridge\` Skill.`;
20
17
 
21
18
  export const TELEGRAM_ATTACH_PROMPT_SNIPPET =
22
19
  "Queue files for the active Telegram reply; outside Telegram turns, send files directly to Telegram.";
@@ -37,7 +34,6 @@ export const TELEGRAM_MESSAGE_PROMPT_GUIDELINES = [
37
34
  const TELEGRAM_MODEL_CONTEXT_TOOL_NAMES = new Set([
38
35
  "telegram_attach",
39
36
  "telegram_message",
40
- "telegram_help",
41
37
  ]);
42
38
  const TELEGRAM_MODEL_CONTEXT_MEMORY_KEY = Symbol.for(
43
39
  "@llblab/pi-telegram:model-context-suspended-tools",
@@ -135,88 +131,6 @@ export function createTelegramModelContextAvailabilityRuntime(deps: {
135
131
  };
136
132
  }
137
133
 
138
- function buildTelegramHelpText(profileName?: string): string {
139
- const diagnosticsPaths = getTelegramDiagnosticsDisplayPaths(profileName);
140
- return `--- TELEGRAM BRIDGE HELP ---
141
-
142
- How to understand Telegram turns:
143
- - \`[telegram|thread:name|from:user|guest:group]\` marks Telegram origin and attributes.
144
- - \`thread\` is the visible Thread identity in Threaded Mode; it is not a bus role.
145
- - \`[reply]\` is quoted context only; act on the user's current instruction.
146
- - \`[attachments]\` are local files; \`[outputs]\` are handler results/transcripts; \`[time]\` is wall-clock context.
147
- - \`[voice] delivery: automatic voice\` means pi-telegram will synthesize ordinary assistant text for this turn; no \`[voice]\` line means no automatic voice policy.
148
-
149
- How to answer Telegram turns:
150
- - Reply in concise, scannable mobile Telegram Rich Markdown.
151
- - Use \`$...$\` for inline math and \`$$...$$\` for block math.
152
- - Real code blocks must stay literal.
153
- - For generated/requested files, call \`telegram_attach(local_path)\`; do not only mention the path.
154
-
155
- Assistant-authored Telegram actions:
156
- - \`telegram_voice\` and \`telegram_button\` are hidden top-level HTML comments, not Pi tools.
157
- - Put action comments at column zero, outside code, quotes, lists, and indented examples.
158
- - Action payloads use either JSON or double-quoted attributes; the colon after the action name is optional and does not affect parsing. Voice accepts equivalent \`text\` or \`value\`: \`<!-- telegram_voice: {"value":"Short summary","lang":"en"} -->\` or \`<!-- telegram_voice text="Short summary" lang="en" -->\`.
159
- - Keep the complete action in one top-level comment and include non-empty \`text\` or \`value\`; encode line breaks inside JSON strings as \`\\n\`.
160
- - Keep voice text TTS-friendly; avoid raw Markdown, code, and tables in voice text.
161
- - Voice delivery generates and attaches OGG automatically; do not also call \`telegram_attach\` for the same audio.
162
- - Voice reply modes are compact: \`hidden\` emits no automatic context, \`mirror\` emits it for voice/audio input, and \`always\` emits it for every Telegram turn. Explicit \`telegram_voice\` remains available for an intentionally distinct spoken payload.
163
- - Button payloads use \`label\` plus \`prompt\`, or compact \`value\` when both are identical: \`<!-- telegram_button: {"label":"Continue","prompt":"Continue with the current plan."} -->\` or \`<!-- telegram_button value="Continue" -->\`.
164
- - Optional \`selected_style\` in either payload form controls the button after queue admission: \`primary\` (default, blue), \`success\` (green), or \`danger\` (red). It never suppresses the prompt.
165
- - If hidden button comments form the whole reply, the bridge supplies the standard \`☑️ **Choose an option:**\` heading automatically.
166
-
167
- Local/TUI direct delivery:
168
- - Do not send Telegram actions from local/TUI prompts unless explicitly asked.
169
- - Use \`telegram_attach\` for files and \`telegram_message\` for direct Markdown text.
170
- - Direct delivery requires this Pi instance to own \`/telegram-connect\` or be registered with the Threaded Mode bus.
171
- - For explicit targets, pass \`chat_id\` plus optional \`thread_id\`; registered followers default to their assigned Thread target.
172
- - Do not use \`telegram_message\` for ordinary Telegram-originated replies; answer normally and let the bridge deliver the active turn reply.
173
-
174
- Threaded Mode:
175
- - pi-telegram supports private-chat Threaded Mode when Telegram exposes thread support for the bot.
176
- - Product/user language is Thread; Bot API primitive names may still say topic.
177
- - Threaded Mode has one leader transport and visible follower Pi processes joined manually through \`/telegram-connect\`.
178
- - Thread names are bridge-assigned or preserved identities; do not invent rename prompts or use a rename tool.
179
- - The \`All\` surface is for routing/control, not hidden Pi process creation.
180
-
181
- Configurable handlers:
182
- - \`telegram.json\` can add no-code \`inboundHandlers\`/\`outboundHandlers\` using command templates before writing an extension.
183
- - For speech-to-text, configure an \`inboundHandlers\` entry matching \`type: "voice"\` or \`mime: "audio/*"\`; stdout becomes \`[outputs]\` prompt context.
184
- - If command-template config is not enough, build a companion extension through the public pi-telegram APIs; do not import package-private \`lib/*\` paths.
185
-
186
- Debugging pi-telegram:
187
- - Inspect \`${diagnosticsPaths.state}\` for runtime state, roster, bindings, slots, reservations, and diagnostics.
188
- - Inspect \`${diagnosticsPaths.logs}\` for redacted runtime event evidence.
189
- - Use terminal \`telegram-status\` for compact human health; use \`telegram-status --debug\` for the full human-readable diagnostic dump.`;
190
- }
191
-
192
- export function getTelegramHelpText(profileName?: string): string {
193
- return buildTelegramHelpText(profileName);
194
- }
195
-
196
- export function registerTelegramHelpTool(
197
- pi: ExtensionAPI,
198
- options: { getActiveProfileName?: () => string | undefined } = {},
199
- ): void {
200
- pi.registerTool({
201
- name: "telegram_help",
202
- label: "Telegram Help",
203
- description:
204
- "Read pi-telegram usage guidance for delivery actions, Threaded Mode, handlers, formatting, and debugging.",
205
- parameters: Type.Object({}),
206
- async execute() {
207
- return {
208
- content: [
209
- {
210
- type: "text",
211
- text: getTelegramHelpText(options.getActiveProfileName?.()),
212
- },
213
- ],
214
- details: {},
215
- };
216
- },
217
- });
218
- }
219
-
220
134
  export function buildTelegramBridgeSystemPrompt(options: {
221
135
  prompt: string;
222
136
  systemPrompt: string;
package/lib/skills.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Bundled Telegram skill discovery
3
+ * Zones: pi agent, telegram guidance
4
+ * Owns source-checkout and installed-package skill path contribution
5
+ */
6
+
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ import type { ExtensionAPI } from "./pi.ts";
10
+
11
+ export const TELEGRAM_SKILLS_PATH = fileURLToPath(
12
+ new URL("../skills", import.meta.url),
13
+ );
14
+
15
+ export function registerTelegramSkillDiscovery(
16
+ pi: Pick<ExtensionAPI, "on">,
17
+ ): void {
18
+ pi.on("resources_discover", () => ({
19
+ skillPaths: [TELEGRAM_SKILLS_PATH],
20
+ }));
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -45,6 +45,7 @@
45
45
  "CHANGELOG.md",
46
46
  "docs/",
47
47
  "scripts/",
48
+ "skills/",
48
49
  "screenshot.png"
49
50
  ],
50
51
  "exports": {
@@ -64,6 +65,9 @@
64
65
  "extensions": [
65
66
  "./index.ts"
66
67
  ],
68
+ "skills": [
69
+ "./skills"
70
+ ],
67
71
  "image": "https://raw.githubusercontent.com/llblab/pi-telegram/main/screenshot.png"
68
72
  },
69
73
  "peerDependencies": {
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: control-surface
3
+ description: Generates contextual, evidence-backed prompt-button interfaces as transient control surfaces for workflows, tools, stateful systems, choices, navigation, and operator actions—not only terminals. Use when a user asks the agent to generate buttons or a contextual operating surface through Telegram or another prompt-button transport. Existing transport menus and callback interfaces remain with their runtime owners.
4
+ ---
5
+
6
+ # Control Surface
7
+
8
+ Generate a temporary, truthful interface that turns the agent's current understanding and available capabilities into useful controls. The agent remains the interpreter, authority boundary, and interface generator; buttons are contextual prompts, not a second application, hidden daemon, or direct grant of capability.
9
+
10
+ ## Concept
11
+
12
+ ```text
13
+ User intent + current evidence → compact state projection → contextual controls
14
+ → next prompt → authorized act → fresh projection
15
+ ```
16
+
17
+ Each response is one generated control surface. Reinspect mutable reality after actions and regenerate the surface instead of maintaining a parallel UI model.
18
+
19
+ ## Scope
20
+
21
+ Use this Skill only to synthesize an agent-generated prompt-button surface. Operating or modifying an existing Telegram bridge menu, callback interface, or runtime-owned control stays with that subsystem and does not route here merely because it contains buttons.
22
+
23
+ A control surface may expose:
24
+
25
+ - Observation: status, evidence, progress, diagnostics, or summaries.
26
+ - Navigation: files, concepts, projects, artifacts, media, threads, or Actor Runs.
27
+ - Action: safe next operations over tools, workflows, services, or project state.
28
+ - Choice: alternatives, filters, priorities, approvals, or design decisions.
29
+ - Supervision: pause, continue, inspect, redirect, retry, or stop bounded work.
30
+
31
+ Console programs are one capability source, not the defining boundary. Use the real owner of each capability: a tool, API, repository, Actor runtime, filesystem, media system, documented workflow, or the explicit state of the conversation.
32
+
33
+ ## Core Contract
34
+
35
+ - Generate controls from current evidence, an explicit contract, or clearly labeled conversational state.
36
+ - Keep domain state with its real owner; never invent a shadow navigation tree, task database, or application session.
37
+ - Make every button prompt self-contained: identify the target, intended result, relevant constraints, and freshness requirement.
38
+ - Treat a click as an ordinary user request subject to the same authority, validation, and safety rules as typed text.
39
+ - Never infer permission for destructive, privileged, credential-bearing, external, or irreversible work merely because a button exists.
40
+ - Do not encode secrets, hidden reasoning, credentials, private keys, tokens, cookies, wallet material, or sensitive content in labels or prompts.
41
+ - Show uncertainty, unavailable state, truncation, filtering, and stale evidence honestly.
42
+ - Prefer a few high-value controls over exhaustive action enumeration.
43
+
44
+ ## Control Admission
45
+
46
+ Buttons are optional, but bias toward offering them when they materially shorten a likely feedback loop. If the user can approve, reject, refine, prioritize, redirect, inspect, or choose a concrete next step faster by tapping than by composing a reply, proactively expose the smallest useful control set without waiting to be asked for buttons.
47
+
48
+ Zero buttons remains preferable when controls would only decorate the answer, restate visible prose, solicit generic “What next?” input, expose an unclear consequence, or save negligible effort. A button earns its place by reducing response effort, ambiguity, turnaround time, or supervision cost while preserving an ordinary typed reply as a first-class option.
49
+
50
+ For status requests, show a compact `Refresh` control and bounded inspect/drill-down controls only when work is active, blocked, stale-sensitive, or otherwise actionable. A completed static status needs no buttons. Do not add destructive shortcuts or actions whose target and consequence are not yet clear.
51
+
52
+ ## Feedback Leverage
53
+
54
+ Treat feedback compression as the primary reason to make controls more visible and proactive:
55
+
56
+ - Offer 2–6 high-confidence choices when they cover likely responses without pretending to be exhaustive.
57
+ - Prefer controls for approval gates, bounded alternatives, priority changes, review verdicts, correction direction, and active-work supervision.
58
+ - Put the most likely or highest-leverage response first, while keeping labels neutral enough to avoid steering the decision dishonestly.
59
+ - Include an explicit free-form path in visible text when the listed choices cannot represent the full answer.
60
+ - Regenerate controls after feedback so the next surface reflects the new decision rather than repeating stale options.
61
+ - Omit controls when the user is already expressing a clear command and no immediate follow-up decision is needed.
62
+
63
+ ## Surface Model
64
+
65
+ A surface normally contains:
66
+
67
+ 1. A short title naming the controlled object or decision.
68
+ 2. A compact projection of relevant state, evidence, choices, or output.
69
+ 3. Provenance when it matters: target, source, timestamp, run identity, status, or truncation note.
70
+ 4. Buttons for likely next intents.
71
+ 5. `Back` or `Up` for hierarchy navigation when meaningful.
72
+ 6. `Refresh` when the projected state can change.
73
+
74
+ Prefer 2–6 controls for feedback and decisions; navigation collections may use up to 12 when the additional entries remain scannable. Split larger sets by category or page instead of building a button wall. Do not add navigation controls when the surface is a one-step decision.
75
+
76
+ ## Truth Modes
77
+
78
+ Name the basis of the surface when ambiguity matters:
79
+
80
+ - Live: freshly inspected mutable state.
81
+ - Contract: stable documented capabilities or choices.
82
+ - Conversation: alternatives or intents established in the current dialogue.
83
+ - Adapted: bounded or transformed output whose omissions are stated.
84
+
85
+ Do not present remembered or inferred state as live. After a mutation, refresh affected state before claiming success or generating dependent controls.
86
+
87
+ ## Evidence Fidelity
88
+
89
+ Preserve material identities, values, ordering, warnings, errors, and status. Adaptation may group, translate, label, paginate, rank, or collapse repeated successful detail, but it must not:
90
+
91
+ - Convert failure into success.
92
+ - Hide material warnings or unavailable evidence.
93
+ - Present a subset as complete.
94
+ - Change identifiers, values, causal order, or authority.
95
+ - Turn a proposal into completed state.
96
+
97
+ State adaptation explicitly, for example: `Показаны 20 из 184 записей, по размеру`.
98
+
99
+ ## Safety And Confirmation
100
+
101
+ Classify each action as read-only, ordinary mutation, privileged, destructive, secret-bearing, external, or irreversible.
102
+
103
+ Use a two-stage flow for high-impact actions:
104
+
105
+ 1. An action button opens a confirmation surface naming the exact target, effect, and recovery boundary.
106
+ 2. A distinct confirmation button requests the exact operation.
107
+
108
+ Re-check mutable targets immediately before execution. Access denial never authorizes automatic privilege escalation. If evidence may expose secrets, stop before display and offer metadata-only or redacted alternatives.
109
+
110
+ ## Prompt Buttons
111
+
112
+ Use the transport's canonical prompt-button syntax. For pi-telegram, emit a top-level hidden comment:
113
+
114
+ ```html
115
+ <!-- telegram_button {"label":"🔍 Inspect run","prompt":"Inspect Run run:example read-only, summarize its current status and latest material evidence, then regenerate relevant supervision controls."} -->
116
+ ```
117
+
118
+ Button prompts must:
119
+
120
+ - Preserve the user's language.
121
+ - Name exact targets where possible.
122
+ - Express one coherent next intent.
123
+ - Carry material safety and scope restrictions.
124
+ - Request fresh inspection when state may have changed.
125
+ - Avoid embedding volatile output that should be rediscovered.
126
+
127
+ Labels stay short, distinct, and scannable. Emoji are optional semantic markers; do not rely on color alone. If buttons are unavailable, render the same control surface as a numbered choice list.
128
+
129
+ ## Capability Adapters
130
+
131
+ ### Console And System
132
+
133
+ Use normal console programs as the capability owner. Check exit status and stderr before rendering success. Preserve complete output when reasonably sized; otherwise label pagination, filtering, head/tail, or ranked subsets. High-impact process, service, package, permission, shutdown, disk, and deletion actions require confirmation.
134
+
135
+ ### Filesystem
136
+
137
+ Resolve paths before listing. Directory navigation may show safe names and metadata without reading contents. Never preview credential stores, private keys, browser profiles, cookies, tokens, wallets, or other secret-bearing files. Use unambiguous paths in prompts and offer safe operations before mutations.
138
+
139
+ ### Workflows And Actor Runs
140
+
141
+ Keep exact workflow, Recipe, Run, artifact, or task identity visible. Controls may inspect, pause, continue, redirect, retry, or stop only through the owning runtime contract. Never simulate lifecycle state, bypass Control semantics, or treat a generated button as direct execution authority.
142
+
143
+ ### Decisions And Design
144
+
145
+ Buttons may represent explicit alternatives without live system inspection. State the decision being made, preserve meaningful trade-offs in visible text, and ensure each prompt records the selected intent rather than silently executing downstream consequences.
146
+
147
+ ## Action Procedure
148
+
149
+ 1. Identify the controlled object, user goal, and capability owner.
150
+ 2. Decide whether the surface needs live, contract, conversational, or adapted evidence.
151
+ 3. Inspect only the state required for a truthful projection.
152
+ 4. Classify candidate controls by authority and impact.
153
+ 5. Render compact state plus context-relevant controls.
154
+ 6. On the next turn, interpret the click as a new request and execute only what it authorizes.
155
+ 7. Validate the result and regenerate from retained reality.
156
+
157
+ ## Failure And Empty States
158
+
159
+ - Show concise failure evidence and offer diagnosis, retry, refresh, back, or a narrower action.
160
+ - If a target disappears, return to the nearest valid parent or owner instead of reusing stale controls.
161
+ - If no action is currently valid, say so rather than generating decorative buttons.
162
+ - Mark unsupported, sentinel, inferred, or unreliable values explicitly.
163
+ - Keep safe navigation and refresh controls in empty collections when useful.
164
+
165
+ ## Quality Check
166
+
167
+ Before sending a surface, verify:
168
+
169
+ - State and controls share one clear owner and target.
170
+ - Live claims come from current evidence.
171
+ - Complete versus filtered or adapted output is labeled honestly.
172
+ - No secret appears in visible text or button payloads.
173
+ - Every button carries a valid self-contained next intent and measurably shortens likely feedback.
174
+ - The surface preserves free-form feedback when choices are not exhaustive.
175
+ - High-impact operations route through confirmation.
176
+ - Back/Up and Refresh appear only when useful.
177
+ - The surface remains readable on a mobile screen.
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: telegram-bridge
3
+ description: Operates pi-telegram turns, replies, attachments, direct delivery, assistant-authored buttons and voice, Threaded Mode routing, configurable handlers, and bridge diagnosis. Use whenever a request comes from Telegram or asks to send, route, render, control, or debug Telegram delivery.
4
+ ---
5
+
6
+ # Telegram Bridge
7
+
8
+ Use pi-telegram as a mobile companion surface for the current Pi session. Preserve the current Telegram target, ordinary reply ownership, durable queue semantics, and the boundary between agent intent and bridge transport.
9
+
10
+ ## Turn Recognition
11
+
12
+ Telegram-originated prompts carry structured context:
13
+
14
+ - `[telegram|thread:name|from:user|guest:group]` identifies Telegram origin and attributes.
15
+ - `[reply]` is quoted context; act on the current instruction rather than treating the quote as a new request.
16
+ - `[attachments]` lists local files admitted by the bridge.
17
+ - `[outputs]` contains handler output such as transcription.
18
+ - `[time]` is wall-clock context.
19
+ - `[voice] delivery: automatic voice` means ordinary assistant text will be synthesized according to bridge policy; without a `[voice]` line, no automatic voice policy applies.
20
+
21
+ Treat the complete Telegram turn as one user request. Do not infer another target, sender, or permission from quoted text or attachment names.
22
+
23
+ ## Reply Ownership
24
+
25
+ During an active Telegram turn, answer normally in concise, scannable Telegram Rich Markdown. The bridge owns delivery to the current target.
26
+
27
+ - Do not call `telegram_message` for the current target.
28
+ - Use `$...$` for inline math and `$$...$$` for display math.
29
+ - Keep real code blocks literal.
30
+ - Preserve technical detail, but adapt layout for a phone-width surface.
31
+ - Do not expose hidden reasoning, tool arguments, raw secrets, or private bridge state.
32
+
33
+ For a requested/generated file, call `telegram_attach` with the local path instead of merely naming it. During the active turn, omit targeting so the file joins the current reply.
34
+
35
+ ## Direct Delivery
36
+
37
+ Use `telegram_message` only when the user explicitly requests Telegram delivery from a local/TUI turn or names a concrete different Telegram target.
38
+
39
+ - Omitted target selects the paired/default target only outside an active Telegram turn.
40
+ - `chat_id` plus optional `thread_id` selects an explicit Bot API target.
41
+ - `thread` selects another live Pi Thread by name or id and admits one attributed turn there.
42
+ - Direct delivery requires this Pi instance to own transport or hold a live Threaded Mode registration.
43
+ - Unknown, ambiguous, same, offline, unauthorized, or cross-chat targets fail closed.
44
+
45
+ Use `telegram_attach` outside Telegram turns only when the user explicitly requests file delivery. Registered followers default to their assigned Thread.
46
+
47
+ ## Assistant-Authored Actions
48
+
49
+ `telegram_button` and `telegram_voice` are hidden top-level HTML comments, not tools. Emit them at column zero, outside lists, quotes, code blocks, and indentation.
50
+
51
+ Button forms:
52
+
53
+ ```html
54
+ <!-- telegram_button {"label":"Continue","prompt":"Continue with the current plan."} -->
55
+ <!-- telegram_button value="Continue" -->
56
+ ```
57
+
58
+ - Payloads accept JSON after an optional colon or double-quoted attributes; keep the complete action in one top-level comment and encode JSON line breaks as `\n`.
59
+ - Use `label` plus a self-contained `prompt`, or non-empty `value` when both are identical.
60
+ - Optional `selected_style` is `primary` (default), `success`, or `danger`; style never suppresses prompt admission.
61
+ - If button comments form the whole reply, the bridge supplies the standard choice heading.
62
+ - A button click creates a new user request; it does not bypass authority or confirmation.
63
+ - Labels stay short and distinct. Prompts name the exact target, intended operation, and safety exclusions.
64
+
65
+ Voice forms:
66
+
67
+ ```html
68
+ <!-- telegram_voice {"text":"Short spoken message","lang":"en"} -->
69
+ <!-- telegram_voice text="Short spoken message" lang="en" -->
70
+ ```
71
+
72
+ - `text` and `value` are equivalent payload forms; explicit `text` wins.
73
+ - Keep speech TTS-friendly and omit Markdown syntax, tables, and raw code.
74
+ - Voice delivery creates OGG itself; do not attach a duplicate audio file.
75
+ - Automatic voice modes are `hidden` (no automatic context), `mirror` (voice/audio input), and `always` (every Telegram turn).
76
+ - Explicit voice remains available in every automatic voice mode for an intentionally distinct spoken payload.
77
+
78
+ This Skill is the canonical operating contract. For implementation-level uncertainty, inspect the extension's public documentation and current code rather than relying on a model tool or guessed syntax.
79
+
80
+ ## Attachments And Secrets
81
+
82
+ - Inspect only what the request requires.
83
+ - Treat local attachment paths as admitted inputs, not proof that their contents are safe to expose.
84
+ - Never place tokens, private keys, cookies, credentials, wallet material, or sensitive file contents in text or button payloads.
85
+ - Sending a sensitive file requires an explicit user request naming that delivery intent.
86
+ - For generated artifacts, queue the file with `telegram_attach`; do not base64 or paste binary content into chat.
87
+
88
+ ## Threaded Mode
89
+
90
+ Threaded Mode operates in private chats when Telegram exposes thread support for the bot. It has one leader transport and visible operator-started follower Pi processes.
91
+
92
+ - `Thread` is the product term; reserve `topic` for Bot API primitives.
93
+ - A Thread follows its assigned live Pi instance and current session.
94
+ - Do not invent hidden followers, launch shadow Pi processes, or expose internal bus roles as user identity.
95
+ - Do not rename Threads through guessed prompts or unsupported tools.
96
+ - The `All` surface is routing/control, not process creation.
97
+
98
+ Cross-Thread delivery must preserve the concrete target and current registration authority. Use ordinary reply delivery for the source turn and `telegram_message(thread=...)` only for an explicitly requested different live Thread.
99
+
100
+ ## Configurable Handlers And Extensions
101
+
102
+ Prefer no-code command-template configuration in `telegram.json` before adding a companion extension:
103
+
104
+ - `inboundHandlers` transforms text/media before queueing.
105
+ - `outboundHandlers` transforms final replies.
106
+ - Voice transcription handlers can match `type: "voice"` or `mime: "audio/*"`; stdout becomes `[outputs]`.
107
+
108
+ When configuration is insufficient, use documented `@llblab/pi-telegram/*` public API subpaths. Never import package-private `lib/*`, start another polling loop, or bypass bridge ownership with raw Bot API access.
109
+
110
+ ## Safety
111
+
112
+ - Read-only inspection may proceed when requested.
113
+ - Destructive, privileged, external, credential-bearing, or irreversible operations require explicit authorization under the active engineering contract.
114
+ - A button offering a dangerous action should open a consequence/confirmation screen before execution.
115
+ - Re-check volatile targets immediately before mutation.
116
+ - Report Telegram delivery failures honestly; do not claim a send from a queued comment or failed tool call.
117
+
118
+ ## Diagnosis
119
+
120
+ Prefer:
121
+
122
+ 1. `telegram-status` for compact health.
123
+ 2. `telegram-status --debug` for bounded human-readable diagnostics.
124
+ 3. `~/.pi/agent/tmp/telegram/state.json` and `logs.jsonl` for default-profile redacted evidence.
125
+ 4. `state.<profile>.json` and `logs.<profile>.jsonl` in the same directory for a named profile.
126
+
127
+ When `PI_CODING_AGENT_DIR` selects another compatible runtime, resolve the equivalent `tmp/telegram` directory under that agent root. Do not mutate bridge state, ownership files, journals, bindings, or locks to force recovery. Use supported commands and exact current authority.
128
+
129
+ ## Completion Check
130
+
131
+ Before sending a Telegram response, verify:
132
+
133
+ - The reply goes through the correct current or explicit target path.
134
+ - Requested files are attached rather than only mentioned.
135
+ - Action comments are top-level and syntactically complete.
136
+ - Buttons carry self-contained prompts and dangerous actions retain confirmation.
137
+ - No secret or hidden reasoning appears in text, actions, or attachments without explicit authorization.
138
+ - Direct delivery is not duplicating the ordinary current-turn reply.