@llblab/pi-telegram 0.20.6 โ 0.21.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 +7 -5
- package/BACKLOG.md +3 -45
- package/CHANGELOG.md +24 -0
- package/README.md +16 -8
- package/api/activity.ts +15 -0
- package/api/commands.ts +1 -1
- package/api/delivery.ts +21 -0
- package/api/inbound.ts +1 -1
- package/api/keyboard.ts +1 -1
- package/api/outbound.ts +1 -1
- package/api/sections.ts +1 -1
- package/api/status.ts +2 -2
- package/api/updates.ts +1 -1
- package/api/voice.ts +1 -1
- package/docs/README.md +2 -0
- package/docs/activity.md +294 -0
- package/docs/architecture.md +21 -5
- package/docs/delivery.md +224 -0
- package/docs/public-api.md +93 -2
- package/docs/sections.md +6 -5
- package/index.ts +53 -0
- package/lib/activity.ts +683 -0
- package/lib/bindings.ts +60 -8
- package/lib/bus-api.ts +2 -1
- package/lib/bus-leader.ts +4 -1
- package/lib/bus.ts +38 -0
- package/lib/delivery.ts +800 -0
- package/lib/lifecycle.ts +23 -4
- package/lib/model.ts +3 -1
- package/lib/pi.ts +21 -5
- package/lib/routing.ts +18 -3
- package/lib/sections.ts +1 -1
- package/lib/status.ts +2 -2
- package/lib/updates.ts +4 -4
- package/package.json +6 -4
package/docs/public-api.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
- **Compatibility:** older import/config paths that remain supported but should not be used for new code.
|
|
10
10
|
- **Internal:** exported from source for tests or domain reuse, but not a compatibility promise.
|
|
11
11
|
|
|
12
|
+
The 0.21 Activity surface requires Pi `0.80.6` or newer. This minimum belongs to the package peer contract because `agent_settled` provides the only safe terminal boundary after retries, compaction, and queued continuations.
|
|
13
|
+
|
|
12
14
|
## Package Entrypoints
|
|
13
15
|
|
|
14
16
|
Preferred public imports:
|
|
@@ -21,6 +23,8 @@ import { registerTelegramUpdateHandler } from "@llblab/pi-telegram/updates";
|
|
|
21
23
|
import { registerTelegramCommand } from "@llblab/pi-telegram/commands";
|
|
22
24
|
import { registerTelegramInboundHandler } from "@llblab/pi-telegram/inbound";
|
|
23
25
|
import { registerTelegramOutboundHandler } from "@llblab/pi-telegram/outbound";
|
|
26
|
+
import { sendTelegramView } from "@llblab/pi-telegram/delivery";
|
|
27
|
+
import { registerTelegramActivityHandler } from "@llblab/pi-telegram/activity";
|
|
24
28
|
import {
|
|
25
29
|
registerTelegramVoiceSynthesisProvider,
|
|
26
30
|
registerTelegramVoiceTranscriptionProvider,
|
|
@@ -53,7 +57,7 @@ Stable commands inside the paired Telegram DM:
|
|
|
53
57
|
|
|
54
58
|
Hidden compatibility shortcuts may open sections directly: `/help`, `/status`, `/model`, `/thinking`, `/queue`, and `/settings`.
|
|
55
59
|
|
|
56
|
-
This command surface is a mobile companion subset, not a raw terminal-command bridge.
|
|
60
|
+
This command surface is a mobile companion subset, not a raw terminal-command bridge or session browser. A Telegram destination follows its assigned Pi instance and sends prompts into that instance's currently active session; it is not permanently bound to one session identity. Compaction operates on the current session, while new-session, resume, fork, tree navigation, session switching, TUI transcript clearing, and arbitrary slash-command dispatch stay out of the stable Telegram API unless Pi exposes safe public extension hooks for them.
|
|
57
61
|
|
|
58
62
|
### Tools and assistant-authored actions
|
|
59
63
|
|
|
@@ -118,6 +122,12 @@ High-level stable APIs:
|
|
|
118
122
|
- `registerTelegramStatusLineProvider()`
|
|
119
123
|
- Identity: required `id`.
|
|
120
124
|
- Purpose: compact companion status rows in the `/start` menu status text.
|
|
125
|
+
- `sendTelegramView()` / `editTelegramView()` / `deleteTelegramView()` / `sendTelegramChatAction()`
|
|
126
|
+
- Identity: current process-local delivery generation and returned logical message handles.
|
|
127
|
+
- Purpose: ownership-gated operational delivery to active-turn, current-instance, aggregate, or explicitly authorized targets.
|
|
128
|
+
- `registerTelegramActivityHandler()`
|
|
129
|
+
- Identity: required stable `id`.
|
|
130
|
+
- Purpose: normalized non-blocking Pi lifecycle activity with source identity and fresh delivery contexts.
|
|
121
131
|
- `registerTelegramVoiceTranscriptionProvider()`
|
|
122
132
|
- Identity: required stable `id` for new code.
|
|
123
133
|
- Purpose: STT fallback for voice/audio input.
|
|
@@ -145,6 +155,45 @@ Advanced stable diagnostics:
|
|
|
145
155
|
|
|
146
156
|
All registration APIs return a disposer. Companion extensions should call disposers on shutdown and re-register on session start when they recreate runtime state. Low-level bus APIs intentionally avoid ids and run in registration order. High-level provider/UI APIs require stable identity in their public contract so diagnostics, replacement, and cleanup are understandable. Generated voice-provider ids remain a temporary compatibility path where documented.
|
|
147
157
|
|
|
158
|
+
## Capability Inventory And Gap Classification
|
|
159
|
+
|
|
160
|
+
This inventory maps the complete bridge capability plane to its supported extension boundary. A capability may stay private deliberately; completeness means every meaningful capability has an explicit classification, not that every internal helper becomes public.
|
|
161
|
+
|
|
162
|
+
### Public now
|
|
163
|
+
|
|
164
|
+
- **Extension loading:** The root export loads the bridge as a Pi extension; companion code uses the domain subpaths below rather than importing root runtime state.
|
|
165
|
+
- **Telegram commands:** `/commands` registers explicit Telegram-native slash commands with scoped reply and prompt-enqueue ports.
|
|
166
|
+
- **Managed menu and Settings UI:** `/sections` registers main-menu views, Settings rows, namespaced callbacks, standalone callback-scoped messages, and diagnostics.
|
|
167
|
+
- **Programmatic target-aware delivery:** `/delivery` sends, edits, deletes, and signals operational views against active-turn, current-instance, aggregate, or explicitly authorized targets through generation-bound logical handles.
|
|
168
|
+
- **Normalized lifecycle activity:** `/activity` registers non-blocking extension handlers for evidence-based run/source identity, assistant prose/reasoning segments, executed tools, compaction, and settlement with fresh delivery contexts.
|
|
169
|
+
- **Compact status projection:** `/status` contributes synchronous status rows to the `/start` menu.
|
|
170
|
+
- **Raw inbound update interception:** `/updates` observes or consumes Telegram updates before default routing and remains the low-level callback escape hatch.
|
|
171
|
+
- **Inbound content transforms:** `/inbound` adds Telegram-to-Pi text/media preprocessing after operator-configured handlers.
|
|
172
|
+
- **Final outbound transforms:** `/outbound` adds final text/voice transformation fallbacks and exposes redacted runtime-event recording.
|
|
173
|
+
- **Voice providers and policy helpers:** `/voice` registers STT/TTS providers and exposes stable voice-mode projections.
|
|
174
|
+
- **Keyboard structures:** `/keyboard` exposes inline-keyboard structural types without transport operations.
|
|
175
|
+
- **Agent-callable delivery:** `telegram_message` and `telegram_attach` provide ownership-gated text/file delivery to the agent, not a JavaScript companion-extension transport API.
|
|
176
|
+
|
|
177
|
+
### Intentionally private
|
|
178
|
+
|
|
179
|
+
- **Credentials and raw transport:** Bot tokens, Telegram clients, unrestricted Bot API calls, polling, retry loops, offsets, and multipart/download internals stay private so companions cannot bypass pairing or open a second transport owner.
|
|
180
|
+
- **Ownership and multi-instance routing:** Locks, named-profile isolation, leader/follower IPC, authorization capabilities, thread provisioning, reconciliation, and sync assumptions stay bridge-owned.
|
|
181
|
+
- **Session and queue coordination:** Active turns, queue lanes, dispatch gates, abort/compaction state, previews, final-reply ordering, and session-bound context stores stay internal invariants rather than shared mutable extension state. Telegram targets identify Pi instances and resolve their current session at dispatch time; they are not public handles to immutable session files.
|
|
182
|
+
- **Core operator UI:** Built-in menus, model/thinking controls, rendering internals, prompt-template expansion, status diagnostics assembly, and thread naming remain core policy; companions extend them through commands, sections, and status providers.
|
|
183
|
+
- **Raw Pi runtime objects:** Companion APIs never return captured `ExtensionContext`, `ExtensionCommandContext`, session managers, or private session-replacement/runtime handles.
|
|
184
|
+
|
|
185
|
+
### Assessed and not required for 0.21
|
|
186
|
+
|
|
187
|
+
- **General managed callbacks outside Sections:** The documented issue #126 consumer shape needs interactive Settings toggles, not interactive activity rows. `/sections` already owns stable callback namespacing, callback answers, edits, navigation, and cleanup for those toggles; `/delivery` can render the resulting non-interactive activity views. A second callback registry would duplicate ownership without a proven use case, while `/updates` remains the deliberate low-level escape hatch for consumers that truly need raw callback interception. Revisit only when a public-import-only consumer must generate managed callbacks independently of a registered Section context for arbitrary delivered messages.
|
|
188
|
+
|
|
189
|
+
### Explicitly deferred
|
|
190
|
+
|
|
191
|
+
- **Programmatic artifact/media delivery:** `telegram_attach` covers agent-authored artifacts, while companion JavaScript has no general file/media send contract. The first 0.21 delivery slice targets operational text/activity views; media should earn a typed extension only from a concrete companion use case.
|
|
192
|
+
- **General configuration mutation:** Companions own their configuration and Settings state. pi-telegram does not expose unrestricted mutation of `telegram.json`, profile identity, pairing, rendering, queue, or transport settings.
|
|
193
|
+
- **Process and session control:** Reload, new-session, fork, resume, process launch, and arbitrary Pi slash-command dispatch remain outside the Telegram companion API until Pi exposes safe async extension hooks.
|
|
194
|
+
|
|
195
|
+
The 0.21 platform boundary lets a public-import-only consumer own reasoning, intermediate-prose, and tool-row policy while pi-telegram retains target selection, transport, authorization, lifecycle safety, and delivery ordering. Activity-specific examples live in this documentation; the separate [`pi-telegram-extension-demo`](https://github.com/llblab/pi-telegram-extension-demo) project remains the maintained companion-extension reference.
|
|
196
|
+
|
|
148
197
|
## Commands
|
|
149
198
|
|
|
150
199
|
Import from `@llblab/pi-telegram/commands`. This registers Telegram slash commands only; it does not expose Pi slash commands and is unrelated to command-template handlers.
|
|
@@ -209,6 +258,48 @@ Contract:
|
|
|
209
258
|
|
|
210
259
|
Full behavior: [Extension Sections](./sections.md).
|
|
211
260
|
|
|
261
|
+
## Telegram Delivery API
|
|
262
|
+
|
|
263
|
+
Import from `@llblab/pi-telegram/delivery`.
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
const sent = await sendTelegramView(
|
|
267
|
+
{
|
|
268
|
+
text: "<b>Indexingโฆ</b>",
|
|
269
|
+
parseMode: "html",
|
|
270
|
+
},
|
|
271
|
+
{ scope: { kind: "instance" } },
|
|
272
|
+
);
|
|
273
|
+
if (!sent.ok) {
|
|
274
|
+
recordLocalDiagnostic(sent.reason, sent.message);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The delivery runtime resolves its live binding on every call and returns structured failures for unavailable runtimes, missing or unauthorized targets, stale handles, invalid views, and transport failures. A logical handle may represent several chunked Telegram messages; edit and delete reconcile the whole logical view. If send or edit growth fails after materializing messages, the failure carries a valid partial handle for deterministic retry or cleanup. Followers route through the existing leader transport, and reload/session replacement invalidates old handles rather than retaining Pi contexts.
|
|
279
|
+
|
|
280
|
+
Full behavior: [Telegram Delivery API](./delivery.md).
|
|
281
|
+
|
|
282
|
+
## Telegram Activity API
|
|
283
|
+
|
|
284
|
+
Import from `@llblab/pi-telegram/activity`.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const off = registerTelegramActivityHandler({
|
|
288
|
+
id: "@scope/activity-view",
|
|
289
|
+
async handle(event, ctx) {
|
|
290
|
+
if (event.type !== "tool-start") return;
|
|
291
|
+
await ctx.send({
|
|
292
|
+
text: `Tool: ${event.toolName}`,
|
|
293
|
+
parseMode: "plain",
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Handlers receive ordered normalized events but run outside Pi's critical lifecycle path. Each handler has an isolated asynchronous queue; adjacent high-frequency deltas may coalesce while semantic boundaries remain ordered. Activity contexts choose active-turn delivery for Telegram-owned work and instance delivery for local/autonomous/unknown work, delegating every operation through the current `/delivery` generation.
|
|
300
|
+
|
|
301
|
+
Full behavior and consumer policy examples: [Telegram Activity API](./activity.md).
|
|
302
|
+
|
|
212
303
|
## Status Lines
|
|
213
304
|
|
|
214
305
|
Import from `@llblab/pi-telegram/status`.
|
|
@@ -458,7 +549,7 @@ async function synthesizeDemoOgg(_text: string): Promise<string> {
|
|
|
458
549
|
|
|
459
550
|
### Smoke Checklist
|
|
460
551
|
|
|
461
|
-
- The extension imports only public package membranes: `@llblab/pi-telegram`, `/commands`, `/sections`, `/status`, `/updates`, `/inbound`, `/outbound`, `/voice`, or `/keyboard`.
|
|
552
|
+
- The extension imports only public package membranes: `@llblab/pi-telegram`, `/commands`, `/sections`, `/status`, `/delivery`, `/activity`, `/updates`, `/inbound`, `/outbound`, `/voice`, or `/keyboard`.
|
|
462
553
|
- It does not import `@llblab/pi-telegram/lib/*`.
|
|
463
554
|
- It registers on `session_start` and disposes on `session_shutdown`.
|
|
464
555
|
- Stable high-level registrations use durable ids.
|
package/docs/sections.md
CHANGED
|
@@ -441,10 +441,11 @@ Available programmatically via `getTelegramSectionDiagnostics()`. Main-menu/sett
|
|
|
441
441
|
|
|
442
442
|
## 15. Demo Extension
|
|
443
443
|
|
|
444
|
-
`@llblab/pi-telegram-extension-demo`
|
|
444
|
+
[`@llblab/pi-telegram-extension-demo`](https://github.com/llblab/pi-telegram-extension-demo) is the maintained companion-extension reference:
|
|
445
445
|
|
|
446
|
-
- Main
|
|
447
|
-
-
|
|
448
|
-
-
|
|
446
|
+
- Main-menu and Settings surfaces with dynamic labels.
|
|
447
|
+
- Managed section callbacks, buttons, edits, navigation, and cleanup.
|
|
448
|
+
- Public `@llblab/pi-telegram/*` imports rather than package-private `/lib` paths.
|
|
449
|
+
- Independent package and lifecycle ownership outside pi-telegram core.
|
|
449
450
|
|
|
450
|
-
Use it as a template for
|
|
451
|
+
Use it as a template for section-based extensions. Activity-specific registration and delivery patterns remain in [Telegram Activity API](./activity.md); pi-telegram does not duplicate the demo as an in-package `examples/` directory.
|
package/index.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Keeps the runtime wiring in one place while delegating reusable domain logic to /lib modules
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import * as Activity from "./lib/activity.ts";
|
|
7
8
|
import * as Bindings from "./lib/bindings.ts";
|
|
8
9
|
import * as BusApi from "./lib/bus-api.ts";
|
|
9
10
|
import * as Bus from "./lib/bus.ts";
|
|
@@ -13,6 +14,7 @@ import * as BusTransport from "./lib/bus-transport.ts";
|
|
|
13
14
|
import * as CommandTemplates from "./lib/command-templates.ts";
|
|
14
15
|
import * as Commands from "./lib/commands.ts";
|
|
15
16
|
import * as Config from "./lib/config.ts";
|
|
17
|
+
import * as Delivery from "./lib/delivery.ts";
|
|
16
18
|
import * as Threads from "./lib/threads.ts";
|
|
17
19
|
import * as Inbound from "./lib/inbound.ts";
|
|
18
20
|
import * as Lifecycle from "./lib/lifecycle.ts";
|
|
@@ -531,6 +533,54 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
531
533
|
});
|
|
532
534
|
const { replyTransport, editInteractiveMessage, sendInteractiveMessage } =
|
|
533
535
|
replyRuntime;
|
|
536
|
+
const getDeliveryTargetPolicyView = function (): Delivery.TelegramDeliveryTargetPolicyView {
|
|
537
|
+
const ownsDirect = lockRuntime.owns();
|
|
538
|
+
const followerTarget = telegramBusFollowerRegistrationState.getTarget();
|
|
539
|
+
return {
|
|
540
|
+
canDeliver:
|
|
541
|
+
ownsDirect || telegramBusFollowerRegistrationState.isRegistered(),
|
|
542
|
+
ownsDirect,
|
|
543
|
+
allowedChatId: configStore.getAllowedUserId(),
|
|
544
|
+
followerTarget,
|
|
545
|
+
leaderTarget: telegramBusLeaderTarget,
|
|
546
|
+
liveTargets: threadStore.list().map(function (record) {
|
|
547
|
+
return record.target;
|
|
548
|
+
}),
|
|
549
|
+
};
|
|
550
|
+
};
|
|
551
|
+
const deliveryGenerationSeed = `${telegramInstanceId}:${Date.now()}`;
|
|
552
|
+
const deliveryLifecycleRuntime =
|
|
553
|
+
Delivery.createTelegramBridgeDeliveryLifecycleHooks({
|
|
554
|
+
generationSeed: deliveryGenerationSeed,
|
|
555
|
+
getTargetPolicyView: getDeliveryTargetPolicyView,
|
|
556
|
+
getActiveTurnTarget() {
|
|
557
|
+
if (activeTurnRuntime.getGuestQueryId()) return undefined;
|
|
558
|
+
return activeTurnRuntime.getTarget();
|
|
559
|
+
},
|
|
560
|
+
api: telegramApiRuntime,
|
|
561
|
+
recordOwnership(input) {
|
|
562
|
+
messageOwnershipStore.record({
|
|
563
|
+
...input,
|
|
564
|
+
instanceId: telegramInstanceId,
|
|
565
|
+
});
|
|
566
|
+
},
|
|
567
|
+
recordFailure(operation, error, target) {
|
|
568
|
+
recordRuntimeEvent("delivery", error, {
|
|
569
|
+
operation,
|
|
570
|
+
scope: target?.threadId === undefined ? "aggregate" : "thread",
|
|
571
|
+
});
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
const activityRuntime = Activity.createTelegramActivityBridgeRuntime({
|
|
575
|
+
generation: deliveryGenerationSeed,
|
|
576
|
+
recordFailure(handlerId, event, error) {
|
|
577
|
+
recordRuntimeEvent("activity", error, {
|
|
578
|
+
handlerId,
|
|
579
|
+
eventType: event.type,
|
|
580
|
+
activityId: event.activityId,
|
|
581
|
+
});
|
|
582
|
+
},
|
|
583
|
+
});
|
|
534
584
|
const { sendTextReply, sendMarkdownReply } =
|
|
535
585
|
Outbound.createTelegramOutboundTextReplyRuntime({
|
|
536
586
|
sendTextReply: replyRuntime.sendTextReply,
|
|
@@ -1238,11 +1288,13 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1238
1288
|
queueSessionLifecycle,
|
|
1239
1289
|
{
|
|
1240
1290
|
async onSessionStart(event, ctx) {
|
|
1291
|
+
await deliveryLifecycleRuntime.onSessionStart();
|
|
1241
1292
|
await lockedPollingRuntime.onSessionStart(event, ctx);
|
|
1242
1293
|
telegramThreadCapabilityMonitor.start(ctx);
|
|
1243
1294
|
queueDispatchWatchdogRuntime.start(ctx);
|
|
1244
1295
|
},
|
|
1245
1296
|
async onSessionShutdown() {
|
|
1297
|
+
await deliveryLifecycleRuntime.onSessionShutdown();
|
|
1246
1298
|
queueDispatchWatchdogRuntime.stop();
|
|
1247
1299
|
telegramThreadCapabilityMonitor.stop();
|
|
1248
1300
|
},
|
|
@@ -1301,6 +1353,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1301
1353
|
...sessionLifecycleRuntime,
|
|
1302
1354
|
onModelSelect: currentModelRuntime.onModelSelect,
|
|
1303
1355
|
},
|
|
1356
|
+
activityRuntime,
|
|
1304
1357
|
configStore,
|
|
1305
1358
|
abort,
|
|
1306
1359
|
typing,
|