@llblab/pi-telegram 0.27.12 → 0.29.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 +152 -258
- package/BACKLOG.md +1 -169
- package/CHANGELOG.md +398 -443
- package/README.md +10 -7
- package/api/updates.ts +5 -0
- package/docs/architecture.md +73 -28
- package/docs/multi-instance-bus.md +23 -5
- package/docs/outbound.md +2 -2
- package/docs/public-api.md +8 -7
- package/docs/ui-style.md +6 -6
- package/docs/updates.md +29 -11
- package/index.ts +358 -246
- package/lib/activity-verbosity.ts +26 -0
- package/lib/bindings.ts +240 -5
- package/lib/bus-follower.ts +436 -238
- package/lib/bus-leader.ts +395 -42
- package/lib/bus.ts +994 -153
- package/lib/commands.ts +184 -30
- package/lib/config.ts +23 -2
- package/lib/journal.ts +3140 -0
- package/lib/lifecycle.ts +4 -0
- package/lib/locks.ts +4 -2
- package/lib/media.ts +71 -32
- package/lib/menu-queue.ts +31 -17
- package/lib/menu.ts +5 -3
- package/lib/model.ts +51 -24
- package/lib/ownership.ts +42 -7
- package/lib/paths.ts +35 -0
- package/lib/polling.ts +591 -106
- package/lib/prompts.ts +20 -89
- package/lib/queue.ts +732 -143
- package/lib/routing.ts +291 -64
- package/lib/runtime.ts +26 -10
- package/lib/skills.ts +21 -0
- package/lib/status.ts +257 -18
- package/lib/sync.ts +131 -5
- package/lib/telegram-api.ts +41 -11
- package/lib/text-groups.ts +75 -35
- package/lib/threads.ts +112 -4
- package/lib/turns.ts +79 -14
- package/lib/updates.ts +3771 -223
- package/package.json +7 -3
- package/scripts/check-downgrade.mjs +435 -0
- package/skills/button-console/SKILL.md +139 -0
- package/skills/telegram-bridge/SKILL.md +138 -0
package/docs/ui-style.md
CHANGED
|
@@ -54,7 +54,7 @@ Use emoji as stable semantic markers, not decoration. Emoji carry transportable
|
|
|
54
54
|
| `➡️` | Choose replacement target | Thread replace/restore target buttons that select which Pi instance should move to the current thread | Use inside the second replace/restore chooser, not for ordinary reroutes. |
|
|
55
55
|
| `☑️` | Activate / choose this item | Model detail activation action, generated button-only choice heading | Positive selection cue; use `🟢 Active` for already-current state. |
|
|
56
56
|
| `❌` | No / cancel | Confirmation cancel buttons | Use for safe cancellation, not destructive removal. |
|
|
57
|
-
| `🗑` | Delete /
|
|
57
|
+
| `🗑` | Delete / suppress | Queue delete actions, destructive confirmations, suppression reaction | The explicit queue button deletes; the reaction reversibly suppresses a waiting turn. |
|
|
58
58
|
|
|
59
59
|
### State Indicators And Button Grammars
|
|
60
60
|
|
|
@@ -77,11 +77,11 @@ Queue reactions are shortcut controls for waiting turns. Preserve their semantic
|
|
|
77
77
|
| `❤` / `❤️` | Promote to priority | Queue reaction shortcut | Normalize display consistently where code normalizes reactions. |
|
|
78
78
|
| `🕊` / `🕊️` | Promote to priority | Queue reaction shortcut | Soft/peaceful promotion gesture. |
|
|
79
79
|
| `🔥` | Promote to priority | Queue reaction shortcut | Urgent/hot promotion gesture. |
|
|
80
|
-
| `👎` |
|
|
81
|
-
| `👻` |
|
|
82
|
-
| `💔` |
|
|
83
|
-
| `💩` |
|
|
84
|
-
| `🗑` |
|
|
80
|
+
| `👎` | Suppress waiting turn | Queue reaction shortcut and suppressed queue badge | Suppression is reversible and is not negative feedback to the agent. |
|
|
81
|
+
| `👻` | Suppress waiting turn | Queue reaction shortcut and suppressed queue badge | Disappear/suppress metaphor. |
|
|
82
|
+
| `💔` | Suppress waiting turn | Queue reaction shortcut and suppressed queue badge | Reversible cancel metaphor. |
|
|
83
|
+
| `💩` | Suppress waiting turn | Queue reaction shortcut and suppressed queue badge | Reversible reject metaphor. |
|
|
84
|
+
| `🗑` | Suppress or explicitly delete | Queue reaction shortcut, suppressed queue badge, and queue delete UI | The reaction is reversible suppression; only the explicit queue button is destructive. |
|
|
85
85
|
|
|
86
86
|
### Decorative Or Local-Example Emoji
|
|
87
87
|
|
package/docs/updates.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
This document describes the registry that lets layered pi extensions running in the same pi process hook into `pi-telegram`'s polling loop and react to inbound Telegram updates **before** `pi-telegram`'s default routing fires.
|
|
8
8
|
|
|
9
|
+
The `0.28.0` transport/execution split is specified in [Durable Admission And Recovery](./architecture.md#durable-admission-and-recovery). Polling durably admits and advances batches before the independent worker invokes this registry. The worker binds source ids only after public interception passes, supplies public and built-in handlers with the same optional execution fence, settles late grouping under its exact generation, and publishes durable queue readiness. This document describes the executable public registry contract.
|
|
10
|
+
|
|
9
11
|
It is the runtime counterpart to [Callback Namespaces](./callback-namespaces.md): callback namespaces define how to share `callback_data` cleanly; update handlers define how to observe and optionally short-circuit the dispatch of those updates.
|
|
10
12
|
|
|
11
13
|
## When to use it
|
|
@@ -22,9 +24,10 @@ If the extension needs a durable top-level Telegram menu section with managed re
|
|
|
22
24
|
|
|
23
25
|
## Constraints
|
|
24
26
|
|
|
25
|
-
- One bot
|
|
26
|
-
- Handlers run in the
|
|
27
|
+
- One bot profile has one leader-owned `getUpdates` loop. This process-local registry neither creates another transport nor replaces the Threaded Mode leader/follower bus.
|
|
28
|
+
- Handlers run before built-in routing in the independent semantic worker. They must return promptly because a long await delays later handler/routing execution for that source order, but it does not delay `getUpdates` admission or offset persistence. Replacement generations wait for the unsettled handler rather than executing the same update concurrently.
|
|
27
29
|
- Handler errors are caught and logged silently so polling never breaks. If you need durable error reporting, do it inside your handler.
|
|
30
|
+
- Typed handlers receive an optional second `execution` argument with `signal`, `updateId`, `generation`, `isCurrent()`, and `assertCurrent()`. Pass `signal` into cancellable work and call `assertCurrent()` immediately before irreversible effects. The runtime also rejects a verdict returned after cancellation. Legacy one-argument and zero-coupling handlers remain compatible, but any effect they commit while still awaiting is their responsibility; replacement replay still waits for that handler's actual settlement.
|
|
28
31
|
- The registry lives on `globalThis`. Module instance identity is not required, so layered extensions can reach it without importing `@llblab/pi-telegram`.
|
|
29
32
|
|
|
30
33
|
## Verdicts
|
|
@@ -45,11 +48,12 @@ Two equivalent paths.
|
|
|
45
48
|
```ts
|
|
46
49
|
import { registerTelegramUpdateHandler } from "@llblab/pi-telegram/updates";
|
|
47
50
|
|
|
48
|
-
const off = registerTelegramUpdateHandler(async (update) => {
|
|
51
|
+
const off = registerTelegramUpdateHandler(async (update, execution) => {
|
|
49
52
|
const cb = (update as { callback_query?: { id?: string; data?: string } })
|
|
50
53
|
.callback_query;
|
|
51
54
|
if (!cb?.data?.startsWith("myext:")) return "pass";
|
|
52
|
-
await resolveMyApproval(cb);
|
|
55
|
+
await resolveMyApproval(cb, execution?.signal);
|
|
56
|
+
execution?.assertCurrent();
|
|
53
57
|
return "consume";
|
|
54
58
|
});
|
|
55
59
|
|
|
@@ -69,13 +73,26 @@ type PiTelegramVerdict =
|
|
|
69
73
|
| "pass"
|
|
70
74
|
| void
|
|
71
75
|
| Promise<"consume" | "pass" | void>;
|
|
72
|
-
type
|
|
76
|
+
type PiTelegramUpdateExecutionFence = {
|
|
77
|
+
readonly generation: number;
|
|
78
|
+
readonly updateId: number;
|
|
79
|
+
readonly signal: AbortSignal;
|
|
80
|
+
isCurrent: () => boolean;
|
|
81
|
+
assertCurrent: () => void;
|
|
82
|
+
};
|
|
83
|
+
type PiTelegramUpdateHandler = (
|
|
84
|
+
update: unknown,
|
|
85
|
+
execution?: PiTelegramUpdateExecutionFence,
|
|
86
|
+
) => PiTelegramVerdict;
|
|
73
87
|
|
|
74
88
|
interface PiTelegramUpdateHandlerRegistry {
|
|
75
89
|
readonly version: 1;
|
|
76
90
|
add: (handler: PiTelegramUpdateHandler) => () => void;
|
|
77
91
|
// Required: pi-telegram's polling loop calls this on every update.
|
|
78
|
-
dispatch: (
|
|
92
|
+
dispatch: (
|
|
93
|
+
update: unknown,
|
|
94
|
+
execution?: PiTelegramUpdateExecutionFence,
|
|
95
|
+
) => Promise<"consume" | "pass">;
|
|
79
96
|
}
|
|
80
97
|
|
|
81
98
|
const REGISTRY_KEY = "__piTelegramUpdateHandlerRegistry__";
|
|
@@ -100,10 +117,11 @@ function getOrCreateRegistry(): PiTelegramUpdateHandlerRegistry {
|
|
|
100
117
|
handlers.add(handler);
|
|
101
118
|
return () => handlers.delete(handler);
|
|
102
119
|
},
|
|
103
|
-
async dispatch(update) {
|
|
120
|
+
async dispatch(update, execution) {
|
|
104
121
|
for (const handler of handlers) {
|
|
122
|
+
execution?.assertCurrent();
|
|
105
123
|
try {
|
|
106
|
-
const result = await handler(update);
|
|
124
|
+
const result = await handler(update, execution);
|
|
107
125
|
if (result === "consume") return "consume";
|
|
108
126
|
} catch {
|
|
109
127
|
// Never break polling because of a handler error.
|
|
@@ -122,7 +140,7 @@ const off = getOrCreateRegistry().add((update) => {
|
|
|
122
140
|
});
|
|
123
141
|
```
|
|
124
142
|
|
|
125
|
-
The registry object on `globalThis.__piTelegramUpdateHandlerRegistry__` is versioned (`version: 1`) and stable across pi-telegram releases;
|
|
143
|
+
The registry object on `globalThis.__piTelegramUpdateHandlerRegistry__` is versioned (`version: 1`) and stable across pi-telegram releases; the optional second arguments preserve existing v1 callers and implementations. Internal adapters can capture one stable check with `createTelegramUpdateExecutionFenceGuard(update)` or transfer the hidden binding across an unavoidable clone with `carryTelegramUpdateExecutionFence(source, target)`, while `getTelegramUpdateExecutionFence` and `assertTelegramUpdateExecutionCurrent` serve carriers that retain it. Future breaking changes will use a new schema version and a new key.
|
|
126
144
|
|
|
127
145
|
## Interaction with built-in routing
|
|
128
146
|
|
|
@@ -140,9 +158,9 @@ The handler registry is ownership-agnostic and does not interact with the extens
|
|
|
140
158
|
|
|
141
159
|
If a layered extension needs to react to ownership changes, it should observe `pi-telegram` lifecycle events through the standard pi extension hooks rather than through the handler registry.
|
|
142
160
|
|
|
143
|
-
## Not a multiplexer
|
|
161
|
+
## Not a transport multiplexer
|
|
144
162
|
|
|
145
|
-
This registry does not
|
|
163
|
+
This registry does not bypass Telegram's single-polling-connection-per-bot constraint and does not route updates between processes. Threaded Mode provides the separate profile-scoped leader/follower bus: the leader owns `getUpdates`, applies its process-local update registry, and then routes retained built-in traffic to the owning instance. The registry remains an in-process extension interception surface only.
|
|
146
164
|
|
|
147
165
|
## Relationship to extension sections
|
|
148
166
|
|