@llblab/pi-telegram 0.36.6 → 0.36.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  > Each release keeps at most 8 outcome records of at most 512 characters.
4
4
 
5
+ ## 0.36.7: Context-Aware Proactive Controls
6
+
7
+ - `Proactive Prompt Buttons`: Plans valid top-level `telegram_button` comments before local/autonomous assistant segments enter Rich or HTML proactive delivery, preserving the ordinary callback store and renderer path instead of silently hiding correct controls.
8
+ - `Connection-Aware Agent Context`: Successful setup/connect and disconnect commands queue a hidden connection-state note for delivery with the next agent turn without triggering one. Connected system guidance and Telegram Skills now distinguish transport capability from user intent, reserving proactive Telegram controls for Telegram-originated turns or explicit requests.
9
+
5
10
  ## 0.36.6: Orphan Journal Guard
6
11
 
7
12
  - `Orphan Journal Guard`: Rejects a missing journal snapshot when immutable revision segments still exist, preventing later admission from replacing uncertain durable authority with a new revisionless snapshot while preserving snapshot-first compaction and fail-closed recovery.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  It is a **runtime adapter**, not a remote terminal. Start or supervise work in the Pi TUI, then continue from Telegram while away from the keyboard. Each Telegram destination follows a running Pi instance and sends prompts into that instance's currently active session; it is not permanently bound to one session file or session identity. The bridge preserves Pi session semantics instead of pretending Telegram is a PTY, shell, process launcher, or session browser. That boundary is the product: Telegram gets safe runtime handles, not raw terminal power.
10
10
 
11
- Every completed intermediate commentary block from a Telegram-originated turn is delivered once as its own message before the existing final reply. Proactive push is enabled by default for local, autonomous, and unclassified extension follow-up work: `assistant.proactivePush` projects visible checkpoints and the final answer to the authorized Telegram target once and in order; set it explicitly to `false` to disable only that local/autonomous projection. Neither path mirrors local prompts, thinking, tool traffic, token deltas, or stale-generation work. The separate `Activity` setting defaults to `verbose` so new installations discover collapsed provider-exposed thinking and tool evidence immediately; operators can narrow it to one class or choose `quiet`. See [Outbound](docs/outbound.md#public-assistant-output) and the [configuration reference](docs/public-api.md#configuration-api).
11
+ Every completed intermediate commentary block from a Telegram-originated turn is delivered once as its own message before the existing final reply. Proactive push is enabled by default for local, autonomous, and unclassified extension follow-up work: `assistant.proactivePush` projects visible checkpoints and the final answer to the authorized Telegram target once and in order, preserving assistant-authored `telegram_button` comments as interactive prompt buttons; set it explicitly to `false` to disable only that local/autonomous projection. Neither path mirrors local prompts, thinking, tool traffic, token deltas, or stale-generation work. The separate `Activity` setting defaults to `verbose` so new installations discover collapsed provider-exposed thinking and tool evidence immediately; operators can narrow it to one class or choose `quiet`. See [Outbound](docs/outbound.md#public-assistant-output) and the [configuration reference](docs/public-api.md#configuration-api).
12
12
 
13
13
  This repository is an actively maintained fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
14
14
 
package/docs/outbound.md CHANGED
@@ -12,7 +12,7 @@ Every completed `assistant-segment` with `placement: "intermediate"` from a Tele
12
12
 
13
13
  Proactive projection defaults on for local, autonomous, and unclassified extension follow-up Pi work. With `assistant.proactivePush` omitted or set to `true`, every completed public block—including visible commentary/checkpoints and the final answer—is projected to the instance's authorized target in source order; set it explicitly to `false` to opt out of only this local/autonomous projection. Both paths consume normalized complete Activity segments rather than raw token deltas, reasoning, or tool traffic.
14
14
 
15
- Projected blocks use `assistant.rendering` independently of voice policy. Rich mode sends native Rich Markdown and HTML mode keeps the established HTML renderer; projection does not synthesize voice or attach queued files merely because Rich rendering is active. Ordered admission revalidates the exact target, profile/token transport generation, leader epoch or follower registration generation, and session generation before each send. Active-turn final delivery waits for admitted commentary inside its existing background delivery task, preserving commentary-before-final order without blocking Pi lifecycle completion. A `commit-unknown` outcome never permits replay.
15
+ Projected blocks use `assistant.rendering` independently of voice policy. Rich mode sends native Rich Markdown and HTML mode keeps the established HTML renderer; assistant-authored `telegram_button` comments are planned into prompt buttons before either renderer runs, while projection does not synthesize voice or attach queued files merely because Rich rendering is active. Ordered admission revalidates the exact target, profile/token transport generation, leader epoch or follower registration generation, and session generation before each send. Active-turn final delivery waits for admitted commentary inside its existing background delivery task, preserving commentary-before-final order without blocking Pi lifecycle completion. A `commit-unknown` outcome never permits replay.
16
16
 
17
17
  ## Technical Activity
18
18
 
@@ -40,8 +40,8 @@ import {
40
40
  Stable commands inside Pi:
41
41
 
42
42
  - `/telegram-setup` — configure/update the bot token.
43
- - `/telegram-connect` — start polling here and acquire external Telegram control ownership. Accepted queue/reply state stays local if ownership later moves elsewhere.
44
- - `/telegram-disconnect` — after destructive confirmation, stop polling and release ownership without deleting or silencing accepted local queue state. In Threaded Mode it deletes this instance's current Telegram thread; a follower waits for its active leader to confirm generation-fenced cleanup before stopping. Graceful Pi `quit` performs the same teardown without prompting, while `reload`, `new`, `resume`, and `fork` preserve same-process handoff.
43
+ - `/telegram-connect` — start polling here and acquire external Telegram control ownership. Accepted queue/reply state stays local if ownership later moves elsewhere. A successful command queues a hidden connection-state note for delivery with the agent's next turn without triggering one; it says Telegram is connected and that connectivity alone is not user intent.
44
+ - `/telegram-disconnect` — after destructive confirmation, stop polling and release ownership without deleting or silencing accepted local queue state. A successful command queues the corresponding hidden, non-triggering disconnected context note; cancelled or failed disconnects do not publish a false state transition. In Threaded Mode it deletes this instance's current Telegram thread; a follower waits for its active leader to confirm generation-fenced cleanup before stopping. Graceful Pi `quit` performs the same teardown without prompting, while `reload`, `new`, `resume`, and `fork` preserve same-process handoff.
45
45
  - `/telegram-status` — show connection, polling, execution, queue, and recent event diagnostics; debug output separates poller and worker progress, durable automatic-retry state, exact foreign queued-owner identity, and negotiated protocol/build/capabilities.
46
46
 
47
47
  ### Telegram commands
package/index.ts CHANGED
@@ -539,6 +539,8 @@ export default function (pi: Pi.ExtensionAPI) {
539
539
  sendRichMessage,
540
540
  editMessage: editTelegramMessageText,
541
541
  getAssistantRenderingMode: configControls.getAssistantRenderingMode,
542
+ planButtonReply:
543
+ Outbound.createTelegramButtonReplyPlanner(buttonActionStore),
542
544
  execCommand: CommandTemplates.execCommandTemplate,
543
545
  getHandlers: configStore.getOutboundHandlers,
544
546
  recordRuntimeEvent,
package/lib/bindings.ts CHANGED
@@ -514,6 +514,18 @@ export function registerTelegramCommandsAndTools({
514
514
  sendMarkdownReply(chatId, undefined, markdown, options),
515
515
  recordRuntimeEvent,
516
516
  });
517
+ const queueAgentConnectionContext = (connected: boolean): void => {
518
+ pi.sendMessage(
519
+ {
520
+ customType: "telegram-connection-state",
521
+ content: connected
522
+ ? Prompts.TELEGRAM_CONNECTED_CONTEXT_MESSAGE
523
+ : Prompts.TELEGRAM_DISCONNECTED_CONTEXT_MESSAGE,
524
+ display: false,
525
+ },
526
+ { deliverAs: "nextTurn" },
527
+ );
528
+ };
517
529
  Commands.registerTelegramBridgeCommands(pi, {
518
530
  promptForConfig: async (ctx, profileName) => {
519
531
  const nextProfileName = profileName ?? undefined;
@@ -581,8 +593,11 @@ export function registerTelegramCommandsAndTools({
581
593
  recordRuntimeEvent,
582
594
  });
583
595
  const completion = await runSetup(ctx);
584
- if (profileName && completion.status === "success") {
585
- ctx.ui.notify(`Profile "${profileName}" saved and connected.`, "info");
596
+ if (completion.status === "success") {
597
+ queueAgentConnectionContext(true);
598
+ if (profileName) {
599
+ ctx.ui.notify(`Profile "${profileName}" saved and connected.`, "info");
600
+ }
586
601
  }
587
602
  },
588
603
  getStatusLines,
@@ -599,6 +614,7 @@ export function registerTelegramCommandsAndTools({
599
614
  stopPolling: stopPolling ?? lockedPollingRuntime.stop,
600
615
  recoverPollingStart,
601
616
  getDisconnectThreadName,
617
+ queueAgentConnectionContext,
602
618
  updateStatus,
603
619
  getProfileNames: () =>
604
620
  Config.getTelegramProfileNames(configStore.getStoredConfig()),
package/lib/commands.ts CHANGED
@@ -334,6 +334,7 @@ export interface TelegramBridgeCommandRegistrationDeps {
334
334
  error: unknown,
335
335
  ) => Promise<TelegramPollingStartRecoveryResult>;
336
336
  getDisconnectThreadName?: () => string | undefined;
337
+ queueAgentConnectionContext?: (connected: boolean) => void;
337
338
  updateStatus: (ctx: ExtensionCommandContext) => void;
338
339
  getProfileNames?: () => string[];
339
340
  activateDefaultProfileConfig?: (ctx: ExtensionCommandContext) => Promise<void>;
@@ -468,6 +469,9 @@ export function registerTelegramBridgeCommands(
468
469
  if (result?.message) {
469
470
  ctx.ui.notify(result.message, result.ok ? "info" : "warning");
470
471
  }
472
+ if (!result || result.ok) {
473
+ deps.queueAgentConnectionContext?.(true);
474
+ }
471
475
  deps.updateStatus(ctx);
472
476
  },
473
477
  });
@@ -490,6 +494,7 @@ export function registerTelegramBridgeCommands(
490
494
  try {
491
495
  const message = await deps.stopPolling();
492
496
  if (message) ctx.ui.notify(message, "info");
497
+ deps.queueAgentConnectionContext?.(false);
493
498
  } catch (error) {
494
499
  const detail = error instanceof Error ? error.message : String(error);
495
500
  ctx.ui.notify(
package/lib/outbound.ts CHANGED
@@ -958,6 +958,10 @@ export function createTelegramAssistantOutputSender<
958
958
  ) => Promise<TelegramSentMessage>;
959
959
  editMessage: (body: TelegramEditMessageTextBody) => Promise<unknown>;
960
960
  getAssistantRenderingMode: () => "rich" | "html";
961
+ planButtonReply?: (markdown: string) => {
962
+ markdown: string;
963
+ replyMarkup?: TReplyMarkup;
964
+ };
961
965
  execCommand: TelegramOutboundTextReplyRuntimeDeps<TReplyMarkup>["execCommand"];
962
966
  getHandlers?: TelegramOutboundTextReplyRuntimeDeps<TReplyMarkup>["getHandlers"];
963
967
  recordRuntimeEvent?: TelegramOutboundTextReplyRuntimeDeps<TReplyMarkup>["recordRuntimeEvent"];
@@ -997,11 +1001,19 @@ export function createTelegramAssistantOutputSender<
997
1001
  getHandlers: deps.getHandlers,
998
1002
  recordRuntimeEvent: deps.recordRuntimeEvent,
999
1003
  });
1004
+ const buttonReply = deps.planButtonReply?.(event.text) ?? {
1005
+ markdown: event.text,
1006
+ };
1000
1007
  await outboundRuntime.sendMarkdownReply(
1001
1008
  target.chatId,
1002
1009
  undefined,
1003
- event.text,
1004
- { target },
1010
+ buttonReply.markdown,
1011
+ {
1012
+ target,
1013
+ ...(buttonReply.replyMarkup
1014
+ ? { replyMarkup: buttonReply.replyMarkup }
1015
+ : {}),
1016
+ },
1005
1017
  );
1006
1018
  };
1007
1019
  }
package/lib/prompts.ts CHANGED
@@ -7,9 +7,14 @@
7
7
  import type { BeforeAgentStartEvent } from "./pi.ts";
8
8
  import { TELEGRAM_PREFIX } from "./turns.ts";
9
9
 
10
+ export const TELEGRAM_CONNECTED_CONTEXT_MESSAGE =
11
+ "Telegram session connected. Use Telegram features for Telegram-originated turns or explicit Telegram requests; connectivity alone is not user intent.";
12
+ export const TELEGRAM_DISCONNECTED_CONTEXT_MESSAGE =
13
+ "Telegram session disconnected. Do not use Telegram delivery, actions, or Telegram-specific reply features unless the user reconnects it.";
14
+
10
15
  const LOCAL_SYSTEM_PROMPT_SUFFIX = `
11
16
 
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.`;
17
+ ${TELEGRAM_CONNECTED_CONTEXT_MESSAGE} Load the \`telegram-bridge\` Skill for Telegram-originated turns or explicit requests involving Telegram delivery, actions, Threaded Mode, or diagnosis. Do not use Telegram-specific features from unrelated local/TUI prompts.`;
13
18
 
14
19
  const TELEGRAM_TURN_SYSTEM_PROMPT_SUFFIX = `
15
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.36.6",
3
+ "version": "0.36.7",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: generated-control-surface
3
- description: Proactively compiles current state, available capabilities, and user intent into contextual, evidence-backed, ephemeral prompt-button interfaces. Use on Telegram turns and other prompt-button transports whenever controls materially shorten likely feedback, without requiring an explicit user request; route reusable deterministic loops toward Generative Apps, omit decorative UI, and preserve domain ownership while fixed transport menus and callbacks remain with their runtime owners.
3
+ description: Proactively compiles current state, available capabilities, and user intent into contextual, evidence-backed, ephemeral prompt-button interfaces. Use without an explicit button request on Telegram-originated turns when controls materially shorten likely feedback, or when a user explicitly requests controls on another prompt-button transport; connection or proactive projection alone is not activation intent. Route reusable deterministic loops toward Generative Apps, omit decorative UI, and preserve domain ownership while fixed transport menus and callbacks remain with their runtime owners.
4
4
  ---
5
5
 
6
6
  # Generated Control Surface
@@ -29,7 +29,7 @@ This Skill and `generative-apps` share one logical button-matrix and `label + pr
29
29
 
30
30
  When a generated surface reveals repeated stable interaction with bounded state and deterministic transitions, load the complementary `generative-apps` Skill and consider compiling that loop. A Generative App may retain ordinary model-mediated prompt buttons beside deterministic bound methods, so only the stable actions need bypass inference. Conversely, keep one-off, interpretive, changing, and context-heavy interaction here when a reusable script would add no concrete latency, token, cost, reliability, or UX value.
31
31
 
32
- On Telegram turns, evaluate this Skill proactively rather than waiting for the user to ask for buttons. Load and apply it when a likely next decision, approval, navigation step, inspection, or bounded action can be made materially easier through controls; its correct output may still contain zero buttons when the admission test fails.
32
+ On Telegram-originated turns, evaluate this Skill proactively rather than waiting for the user to ask for buttons. A connected Telegram session, available button syntax, or proactive projection of local output is capability evidence, not activation intent: do not load this Skill for an unrelated local/TUI prompt merely because its answer may also appear in Telegram. Load and apply it when the current turn is Telegram-originated or the user explicitly requests a prompt-button surface and a likely next decision, approval, navigation step, inspection, or bounded action can be made materially easier through controls; its correct output may still contain zero buttons when the admission test fails.
33
33
 
34
34
  A control surface may expose:
35
35
 
@@ -7,6 +7,12 @@ description: Operates pi-telegram turns, replies, attachments, direct delivery,
7
7
 
8
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
9
 
10
+ ## Connection Awareness
11
+
12
+ `Telegram session connected.` means this Pi instance currently has authorized Telegram transport, so Telegram tools and proactive projection may be available. It does not mean the current prompt came from Telegram and does not grant intent to add Telegram actions to unrelated local/TUI work. Use Telegram-specific reply features proactively only on a turn carrying structured Telegram origin; from local/TUI prompts, use them only when the user explicitly requests Telegram delivery or the established workflow is explicitly Telegram-mediated.
13
+
14
+ `Telegram session disconnected.` revokes that availability. Do not attempt Telegram delivery, actions, buttons, or voice until a later connected context is present. Treat the newest connection-state context as authoritative.
15
+
10
16
  ## Turn Recognition
11
17
 
12
18
  Telegram-originated prompts carry structured context:
@@ -48,7 +54,7 @@ Use `telegram_attach` outside Telegram turns only when the user explicitly reque
48
54
 
49
55
  Before compiling assistant-authored controls, inspect already-loaded capability guidance for an advertised maintained Generative App or view/controller adapter. When that owner-provided view exists and the current intent concerns repeated interaction, load and follow the bundled `generative-apps` Skill and prefer binding or invoking the existing app over synthesizing one-shot prompt buttons. This routing guidance is not permission for the bridge to discover capability-specific apps, own their state, or hard-code their identities.
50
56
 
51
- Otherwise, on Telegram turns, proactively load `generated-control-surface` when a likely next decision or action may benefit from prompt buttons; do not wait for an explicit button request, and accept zero controls when its admission rules reject decorative or low-value UI.
57
+ Otherwise, only on Telegram-originated turns, proactively load `generated-control-surface` when a likely next decision or action may benefit from prompt buttons; do not activate it merely because Telegram is connected or local output may be projected by proactive push. Do not wait for an explicit button request on a Telegram-originated turn, and accept zero controls when its admission rules reject decorative or low-value UI.
52
58
 
53
59
  `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.
54
60