@llblab/pi-kit 0.5.1 → 0.6.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/CHANGELOG.md +8 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-grow-loop/AGENTS.md +2 -2
- package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -0
- package/node_modules/@llblab/pi-grow-loop/README.md +6 -6
- package/node_modules/@llblab/pi-grow-loop/index.ts +6 -3
- package/node_modules/@llblab/pi-grow-loop/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +20 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +4 -0
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +27 -5
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +1 -0
- package/node_modules/@llblab/pi-telegram/index.ts +24 -19
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +43 -25
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +79 -6
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +110 -91
- package/node_modules/@llblab/pi-telegram/lib/config.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +14 -3
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +41 -37
- package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +39 -42
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +28 -17
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +134 -73
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +113 -72
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +46 -38
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +192 -58
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +36 -3
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +27 -35
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/skills/abcd-context/AGENTS.md +1 -0
- package/node_modules/@llblab/skills/abcd-context/CHANGELOG.md +6 -2
- package/node_modules/@llblab/skills/abcd-context/SKILL.md +1 -1
- package/node_modules/@llblab/skills/abcd-context/docs/validation-design.md +11 -5
- package/node_modules/@llblab/skills/abcd-context/scripts/_self-test.mjs +61 -0
- package/node_modules/@llblab/skills/abcd-context/scripts/validate-context.mjs +67 -0
- package/node_modules/@llblab/skills/package.json +1 -1
- package/node_modules/@llblab/skills/release-flow/SKILL.md +2 -4
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@llblab/pi-kit` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.6.0 - 2026-09-06
|
|
6
|
+
|
|
7
|
+
- `Current Distribution`: Advances Telegram to `0.44.0`, Grow Loop to `0.7.5`, and Skills to `1.14.1`. The kit now includes reliable default-on Telegram draft previews and prompt anchors, unambiguous `grow-loop` runtime status, and ABCd contiguous-list validation while preserving the existing package set, resource inventory, and load order.
|
|
8
|
+
|
|
9
|
+
## 0.5.2 - 2026-09-06
|
|
10
|
+
|
|
11
|
+
- `Telegram Ordering Hotfix`: Advances the exact Telegram pin to `0.43.2`, bringing bounded threadless command lifetime, causal final/artifact/compaction publication order, stale-delivery fencing, and suppression of voice replay after uncertain acknowledgement. Other pins and the resource inventory remain unchanged; operator-coordinated live smoke follows release.
|
|
12
|
+
|
|
5
13
|
## 0.5.1 - 2026-09-06
|
|
6
14
|
|
|
7
15
|
- `Telegram Continuity Hotfix`: Advances the exact Telegram pin to `0.43.1`, bringing bounded persistent polling-conflict stand-down with accepted queue preservation, missing/null system-prompt normalization, and authority-fenced preview cleanup without changing the resource inventory.
|
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ Package links lead to the owning repositories for usage, documentation, issues,
|
|
|
13
13
|
| [`@llblab/pi-actors`](https://github.com/llblab/pi-actors) | `0.52.1` | Inspectable local Runs, reusable Recipes, persistent tools, and delegation Skills |
|
|
14
14
|
| [`@llblab/pi-clean-room`](https://github.com/llblab/pi-clean-room) | `0.1.1` | Isolated nested Pi TUI with explicitly selected extensions |
|
|
15
15
|
| [`@llblab/pi-codex-usage`](https://github.com/llblab/pi-codex-usage) | `0.9.4` | Compact Codex/Spark subscription-limit status |
|
|
16
|
-
| [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.7.
|
|
16
|
+
| [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.7.5` | Visible continuation scheduling and bounded worker Skills |
|
|
17
17
|
| [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.3.0` | Opt-in validated state handoffs between agent runs |
|
|
18
|
-
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.
|
|
19
|
-
| [`@llblab/skills`](https://github.com/llblab/skills) | `1.14.
|
|
18
|
+
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.44.0` | Telegram companion, queues, files, voice, controls, and Generative Apps guidance |
|
|
19
|
+
| [`@llblab/skills`](https://github.com/llblab/skills) | `1.14.1` | Portable workflows for engineering, review, design, context maintenance, and other focused tasks |
|
|
20
20
|
|
|
21
21
|
Versions are exact by design. An upstream release does not change an installed kit until this repository explicitly advances the dependency and publishes a new kit version. Runtime defects and package-specific feature requests belong in the linked repository; package selection and kit installation issues belong here.
|
|
22
22
|
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
- `grow-loop` skill owns continuation semantics: continue or stop.
|
|
24
24
|
- Routing selects the protocol before worker execution: explicit names override inference; otherwise concrete iterative tasks may select Grow Loop when multiple validated slices and visible checkpoints are useful.
|
|
25
25
|
- `while-true` skill owns only one portable worker pass: discover one canonical work surface, including neutral `Canonical open work: <path>` declarations from relevant project-local delivery instructions, assess reality, reconcile the plan, assemble one bounded validation cohort, execute independent low-coupling tasks with per-task falsification, run shared validation, and hand off. It batches by default when diagnosis remains clear and falls back to a single task for coupled, ordered, large, high-risk, or ambiguous work. A standalone invocation must not activate or call a continuation scheduler. It must not own continuation, user-stop semantics, command phrases, runtime tools, status text, or extension-specific controls; `grow-loop` plus `grow_loop` own sequential continuation only after Grow Loop was selected.
|
|
26
|
-
- `grow_loop` tool only schedules the next visible iteration; its optional `after_seconds` argument accepts `3` through `3600` and defaults to `3`; the minimum preserves an operator-interrupt window before every continuation. Scheduling is idle-deferred: the tool first waits until Pi is idle and no user messages are pending, then starts the configured countdown and sends the compact trigger `while true | grow loop` only if Pi is still idle and no user messages are pending. A longer delay may serve as a continuation timer while asynchronous work finishes. The agent chooses it from evidence about expected remaining duration and reassesses after every wake; the one-hour maximum is exceptional rather than a polling default. If the runtime becomes busy during the countdown, the tool returns to deferred waiting instead of queueing a hidden follow-up. Status shows deferred scheduling as `loop ∞N` with the iteration number in warning color, countdown as `loop Ns`, and active iterations as `loop ∞N` with the iteration number dimmed; `N` is monotonic within the extension instance and advances once per turn that arms a deferred iteration; repeated tool calls in that turn only replace the delay while retaining the same iteration number.
|
|
26
|
+
- `grow_loop` tool only schedules the next visible iteration; its optional `after_seconds` argument accepts `3` through `3600` and defaults to `3`; the minimum preserves an operator-interrupt window before every continuation. Scheduling is idle-deferred: the tool first waits until Pi is idle and no user messages are pending, then starts the configured countdown and sends the compact trigger `while true | grow loop` only if Pi is still idle and no user messages are pending. A longer delay may serve as a continuation timer while asynchronous work finishes. The agent chooses it from evidence about expected remaining duration and reassesses after every wake; the one-hour maximum is exceptional rather than a polling default. If the runtime becomes busy during the countdown, the tool returns to deferred waiting instead of queueing a hidden follow-up. Status shows deferred scheduling as `grow-loop ∞N` with the iteration number in warning color, countdown as `grow-loop Ns`, and active iterations as `grow-loop ∞N` with the iteration number dimmed; `N` is monotonic within the extension instance and advances once per turn that arms a deferred iteration; repeated tool calls in that turn only replace the delay while retaining the same iteration number.
|
|
27
27
|
- There is no slash-command control surface. Any ordinary user prompt exits the active runtime rhythm by clearing pending scheduling and hiding loop status; restart/continuation intent belongs to the agent and Grow Loop skill, not to a runtime latch or regex.
|
|
28
28
|
- No start slash commands, budgets, cycle counts, hidden processes, or background agents.
|
|
29
|
-
- Loop status is dynamic: show warning-colored `loop ∞N` only while the next iteration is deferred until idle, `loop Ns` only during the configured countdown, and dim `loop ∞N` only while a loop-scheduled turn is active. Clear active status only after Pi fully settles without an armed successor; low-level run endings may still lead to retry or compaction recovery. Any user input except the runtime's exact expected continuation prompt clears pending loop scheduling and hides loop status, including operator input injected through another extension.
|
|
29
|
+
- Loop status is dynamic: show warning-colored `grow-loop ∞N` only while the next iteration is deferred until idle, `grow-loop Ns` only during the configured countdown, and dim `grow-loop ∞N` only while a loop-scheduled turn is active. Clear active status only after Pi fully settles without an armed successor; low-level run endings may still lead to retry or compaction recovery. Any user input except the runtime's exact expected continuation prompt clears pending loop scheduling and hides loop status, including operator input injected through another extension.
|
|
30
30
|
- Grow Loop does not own Escape/abort semantics; Escape remains baseline Pi behavior for active agent turns. The runtime does not block future `grow_loop` tool calls; the skill contract owns whether recent user context means continue, stop, restart, or change direction.
|
|
31
31
|
|
|
32
32
|
## Topology
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.5: Status Identity Hotfix
|
|
4
|
+
|
|
5
|
+
- `Status Identity`: Labels deferred, countdown, and active runtime status as `grow-loop` instead of the ambiguous `loop`, matching the extension's public identity and the neighboring State Flow status convention.
|
|
6
|
+
|
|
3
7
|
## 0.7.4: Worker Handoff Ownership Hotfix
|
|
4
8
|
|
|
5
9
|
- `Worker Handoff`: Keeps canonical work-surface resolution and handoff fields with While True; Grow Loop locks user scope and decides whether to schedule another iteration from the returned evidence without duplicating discovery or worker analysis.
|
|
@@ -144,18 +144,18 @@ The tool never blocks future calls. Whether to continue belongs to the agent and
|
|
|
144
144
|
## Runtime Statuses
|
|
145
145
|
|
|
146
146
|
- No status — no active loop rhythm, or the operator took the turn.
|
|
147
|
-
- `loop ∞N` warning — the next loop prompt is armed and waiting for idle/no pending messages.
|
|
148
|
-
- `loop Ns` countdown — Pi is idle and the configured delay is running.
|
|
149
|
-
- `loop ∞N` dim — the compact loop prompt was sent for this iteration.
|
|
147
|
+
- `grow-loop ∞N` warning — the next loop prompt is armed and waiting for idle/no pending messages.
|
|
148
|
+
- `grow-loop Ns` countdown — Pi is idle and the configured delay is running.
|
|
149
|
+
- `grow-loop ∞N` dim — the compact loop prompt was sent for this iteration.
|
|
150
150
|
|
|
151
|
-
`N` is monotonic within the current extension instance and advances only once when a turn arms its deferred successor. Repeated `grow_loop` calls before that turn ends retain `N`, replace the pending delay, and report that the iteration was already scheduled. Active status clears when the scheduled agent run fully settles without arming a successor, so automatic retry or compaction recovery does not produce a false idle state. There is no `loop stopped` or `loop paused` status; absence of
|
|
151
|
+
`N` is monotonic within the current extension instance and advances only once when a turn arms its deferred successor. Repeated `grow_loop` calls before that turn ends retain `N`, replace the pending delay, and report that the iteration was already scheduled. Active status clears when the scheduled agent run fully settles without arming a successor, so automatic retry or compaction recovery does not produce a false idle state. There is no `grow-loop stopped` or `grow-loop paused` status; absence of Grow Loop status means the runtime rhythm is no longer active.
|
|
152
152
|
|
|
153
153
|
## Interruption Model
|
|
154
154
|
|
|
155
155
|
Any user prompt except the scheduler's exact expected continuation prompt exits the active runtime rhythm. This includes operator input delivered through Telegram, RPC bridges, or other extensions:
|
|
156
156
|
|
|
157
157
|
```text
|
|
158
|
-
Runtime: loop 3.0s or loop ∞2
|
|
158
|
+
Runtime: grow-loop 3.0s or grow-loop ∞2
|
|
159
159
|
User: What changed?
|
|
160
160
|
Runtime: hides loop status and cancels pending scheduling
|
|
161
161
|
Agent: answers, stops, changes direction, or later continues based on intent and context
|
|
@@ -193,7 +193,7 @@ Normal continuation:
|
|
|
193
193
|
```text
|
|
194
194
|
User: grow loop
|
|
195
195
|
Agent: closes one backlog slice, validates, reports evidence, calls grow_loop
|
|
196
|
-
Runtime: loop ∞1 → loop 3.0s → while true | grow loop
|
|
196
|
+
Runtime: grow-loop ∞1 → grow-loop 3.0s → while true | grow loop
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
Terminal stop proof:
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
import { Type } from "typebox";
|
|
10
10
|
|
|
11
11
|
const STATUS_KEY = "pi-grow-loop";
|
|
12
|
+
const STATUS_LABEL = "grow-loop";
|
|
12
13
|
const DEFAULT_FOLLOW_UP_DELAY_MS = 3000;
|
|
13
14
|
const DEFAULT_COUNTDOWN_TICK_MS = 100;
|
|
14
15
|
const MIN_AFTER_SECONDS = 3;
|
|
@@ -42,7 +43,8 @@ function statusCountdown(ctx: ExtensionContext, seconds: number) {
|
|
|
42
43
|
const theme = ctx.ui.theme;
|
|
43
44
|
ctx.ui.setStatus(
|
|
44
45
|
STATUS_KEY,
|
|
45
|
-
theme.fg("accent",
|
|
46
|
+
theme.fg("accent", STATUS_LABEL) +
|
|
47
|
+
theme.fg("dim", ` ${seconds.toFixed(1)}s`),
|
|
46
48
|
);
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -50,7 +52,7 @@ function statusRunning(ctx: ExtensionContext, iteration: number) {
|
|
|
50
52
|
const theme = ctx.ui.theme;
|
|
51
53
|
ctx.ui.setStatus(
|
|
52
54
|
STATUS_KEY,
|
|
53
|
-
theme.fg("accent",
|
|
55
|
+
theme.fg("accent", STATUS_LABEL) + theme.fg("dim", ` ∞${iteration}`),
|
|
54
56
|
);
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -58,7 +60,8 @@ function statusDeferred(ctx: ExtensionContext, iteration: number) {
|
|
|
58
60
|
const theme = ctx.ui.theme;
|
|
59
61
|
ctx.ui.setStatus(
|
|
60
62
|
STATUS_KEY,
|
|
61
|
-
theme.fg("accent",
|
|
63
|
+
theme.fg("accent", STATUS_LABEL) +
|
|
64
|
+
theme.fg("warning", ` ∞${iteration}`),
|
|
62
65
|
);
|
|
63
66
|
}
|
|
64
67
|
|
|
@@ -126,7 +126,7 @@ The detailed map is canonical in [`docs/architecture.md`](./docs/architecture.md
|
|
|
126
126
|
- Remove dead code immediately. Reachability from composition roots, public exports, tests, registered surfaces, and documented APIs—not recent usefulness—determines whether code is live.
|
|
127
127
|
- Treat every meaningful `index.ts` edit as a composition-pressure check, but keep one-off live adapter wiring there when extraction would only hide cross-domain state.
|
|
128
128
|
- Follow [`docs/ui-style.md`](./docs/ui-style.md) for interface copy, emoji, buttons, menus, and dialogs. Update the registry before assigning a new UI emoji meaning. Standalone notices use one fully bold emoji-led sentence with a terminal period; menu or chooser headings use the same hierarchy with a terminal colon. Material names may add nested italic emphasis without breaking the outer bold span. Callback alerts preserve equivalent emoji-led plain text because Telegram does not support rich formatting there.
|
|
129
|
-
- Markdown tables use compact source formatting with `---` separator cells and one surrounding space per cell. Preserve vendored references unchanged.
|
|
129
|
+
- Markdown lists never contain blank lines between adjacent items; list items are not paragraphs. Use blank lines only between paragraphs or independently separated blocks. Markdown tables use compact source formatting with `---` separator cells and one surrounding space per cell. Preserve vendored references unchanged.
|
|
130
130
|
- Treat Windows filesystem, named-pipe, lock, heartbeat, and atomic-rename reports as high-signal evidence; reduce them to regressions or explicit platform caveats.
|
|
131
131
|
- Route significant runtime failures through the redacted recent-event recorder. Keep the compact TUI status at generic `error`; details belong in diagnostics.
|
|
132
132
|
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.44.0: Reliable Draft Previews
|
|
8
|
+
|
|
9
|
+
- `Draft Experience`: Enables previews when no current or legacy preference is configured, preserving explicit opt-outs without migration and identifying on as the default in Settings. A two-second leading/trailing throttle sends the first eligible frame immediately and then only the latest accumulated text, producing an operator-accepted rhythm on short and long answers while reducing Telegram rate-limit pressure.
|
|
10
|
+
- `Preview Completion`: Seals draft updates when an assistant message ends, cancels pending throttle timers, and waits only for an already-issued request. Retryable failures defer fresh frames without replaying stale bodies or delaying the final; queued, late, failed, cancelled, and replacement drafts cannot append an obsolete tail.
|
|
11
|
+
- `Publication Ownership`: Gives intermediate and final text one permanent publication owner, eliminating rollover duplication. Background finals capture their originating preview before admission, so delayed work cannot seal, overwrite, await, or deadlock a replacement turn. Rollover barriers remain outside Pi lifecycle hooks and release on failure or cancellation.
|
|
12
|
+
- `Prompt Anchors`: Preserves once-per-turn quoting across delayed text, voice, ordinary uploads, Rich media, and operational delivery. The first successfully delivered permanent response carries the originating prompt anchor; known rejection preserves it for fallback, unknown ACK avoids unsafe replay, and later responses omit the repeated quote.
|
|
13
|
+
|
|
14
|
+
## 0.43.2: Command Lifetime And Publication Order
|
|
15
|
+
|
|
16
|
+
- `All-Tab Command Expiry`: Uses the original Telegram timestamp to expire unselected Threaded Mode command choosers after 60 minutes, settle their deferred source, and reject stale buttons; replay of an expired command creates no new chooser. Active dispatch pauses expiry; failed attempts retain the original deadline, and accepted queue receipts remain protected. Excludes `/thread`, bound threads, classic mode, and invalid timestamps; storage failures retain journal authority.
|
|
17
|
+
- `Selected All-Tab Commands`: Settles still-deferred sources after successful local command dispatch or confirmed follower acceptance, preventing replay after chooser cleanup. Failed follower transfers retain their sources. Background menu rendering remains non-blocking, and commands admitted to the Pi queue retain receipt-governed settlement.
|
|
18
|
+
- `Repeated All-Tab Starts`: A newly delivered chooser for an identical unselected `/start` supersedes older sources from the same user/chat and active admission worker. Different arguments, other commands, and previously selected intents remain separate; superseded callbacks become inert. Failed chooser sends discard their in-memory attempt without settling the journal source, so retries do not exhaust chooser capacity.
|
|
19
|
+
- `Causal Publication Order`: Serializes bridge-owned assistant blocks, activity disclosures, active-turn finals/artifacts, and automatic compaction notices through their existing activity domain before transport routing. Compaction notices cannot overtake delayed local finals; Pi hooks do not wait for their network delivery. Captures notice/activity target and authority at admission and fences queued publications across session replacement; independent handler queues remain separate.
|
|
20
|
+
- `Artifact Delivery Authority`: Rechecks active-turn/session authority after voice/file preparation and recording actions. Cancellation suppresses later uploads, provider/text fallbacks, and stale Rich-message ownership writes without discarding the attachment list. Already-issued requests and in-flight synthesis are not undone; ambiguous uploads never authorize replay.
|
|
21
|
+
- `Final Admission`: Reserves final/error publication at the terminal message boundary and transfers it only to the originating turn before asynchronous config loading. Compaction uses the same queue instead of a separate buffer. Empty outcomes reserve no slot; replacement, preparation failure, settlement, and session reset release unused reservations. Terminal preview cleanup is background and bound to the captured draft.
|
|
22
|
+
- `Compaction Observation`: Fences superseded timeout callbacks and stale-context terminal hooks so they cannot clear a newer observation. The five-minute fallback remains an observer timeout, not proof of Pi completion or cancellation.
|
|
23
|
+
- `Activity Replacement`: Fences late config refresh, thinking acknowledgements, tool edits, failures, and settlement cleanup before state mutation or HTML fallback. Old work can no longer overwrite a replacement target/message, erase new tool arguments, or block new thinking output; loss of transport authority also suppresses fallback without requiring a session reset.
|
|
24
|
+
|
|
5
25
|
## 0.43.1: Transport And Preview Continuity
|
|
6
26
|
|
|
7
27
|
- `Conflict Stand-Down`: Stops transport after ten consecutive competing getUpdates conflicts, including ownership checks, heartbeat, monitoring, and bus teardown. Releases only the exact local lock and revokes direct authority even if release fails, while preserving accepted local queue work. A persistent terminal status and one diagnostic distinguish lost ownership from a competing external client; cancelled reconnects and stale admission cannot supersede current lifecycle work.
|
|
@@ -256,7 +256,7 @@ Most controls live in Pi commands or the Telegram menu. Environment variables re
|
|
|
256
256
|
| Inbound file limit | `PI_TELEGRAM_INBOUND_FILE_MAX_BYTES`, `TELEGRAM_MAX_FILE_SIZE_BYTES` |
|
|
257
257
|
| Outbound attachment limit | `PI_TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES`, `TELEGRAM_MAX_ATTACHMENT_SIZE_BYTES` |
|
|
258
258
|
|
|
259
|
-
Defaults are chosen for ordinary private-bot use: saved config in `~/.pi/agent`, inbound temp files in `~/.pi/agent/tmp/telegram`, `assistant: { rendering: "rich", draftPreviews:
|
|
259
|
+
Defaults are chosen for ordinary private-bot use: saved config in `~/.pi/agent`, inbound temp files in `~/.pi/agent/tmp/telegram`, `assistant: { rendering: "rich", draftPreviews: true, activity: "verbose", timeInjection: "interval" }` for assistant output and activity, and native Telegram active status for long-running turns.
|
|
260
260
|
|
|
261
261
|
## Extension Platform
|
|
262
262
|
|
|
@@ -394,7 +394,7 @@ The bridge does not mirror arbitrary `ctx.ui.confirm/input/select/custom` prompt
|
|
|
394
394
|
|
|
395
395
|
## Diagnostics And Operational Behavior
|
|
396
396
|
|
|
397
|
-
Status rendering distinguishes connected, active, dispatching, queued, tool-running, model-switching, and compacting states; the Telegram status menu gives compaction precedence over generic active or pending work. Its compact Tokens row shows only input and output totals, while the adjacent Cache row groups `R` cache-read tokens, `W` cache-write tokens, and `CH` for the latest assistant request's cache-read share of prompt tokens rather than a misleading cumulative-session ratio; the labels remain distinct from companion-provided usage limits. Observed automatic compaction sends the same start and completion notices as the manual command without duplicating notices for command-owned compaction. If a queue mutation removes the last waiting item while Telegram-owned work still has running tools, status remains active instead of degrading to connected.
|
|
397
|
+
Status rendering distinguishes connected, active, dispatching, queued, tool-running, model-switching, and compacting states; the Telegram status menu gives compaction precedence over generic active or pending work. Its compact Tokens row shows only input and output totals, while the adjacent Cache row groups `R` cache-read tokens, `W` cache-write tokens, and `CH` for the latest assistant request's cache-read share of prompt tokens rather than a misleading cumulative-session ratio; the labels remain distinct from companion-provided usage limits. Observed automatic compaction sends the same start and completion notices as the manual command without duplicating notices for command-owned compaction. If no terminal hook arrives, the current observation times out after five minutes: it releases only observer-owned status/typing, records a diagnostic, and requests a guarded queue-dispatch recheck. Timeout is not proof that Pi compaction completed or was cancelled and emits no invented terminal notice. Superseded timeout callbacks and stale-context terminal hooks cannot abandon or cancel a newer observation. If a queue mutation removes the last waiting item while Telegram-owned work still has running tools, status remains active instead of degrading to connected.
|
|
398
398
|
|
|
399
399
|
Queue reaction behavior, lane-tail transitions, Keep/Skip independence, multi-reaction precedence, and the Bot API reaction-removal limitation are defined in [Priority, Reactions, Keep, and Skip](#priority-reactions-keep-and-skip). Reaction changes first flush a matching delayed text or media group so the governed turn exists before mutation, and dropping marked heads cannot leave status permanently queued.
|
|
400
400
|
|
|
@@ -402,7 +402,7 @@ Queue reaction behavior, lane-tail transitions, Keep/Skip independence, multi-re
|
|
|
402
402
|
|
|
403
403
|
Complete intermediate assistant text blocks from Telegram-originated activity are sent once to the immutable originating target before active-turn final delivery; final and terminal-partial segments stay with settlement so replies are not duplicated. While this instance has exact direct or follower transport authority, completed public blocks from local/autonomous work are always sent once and in source order to the instance's authorized target. Connected companion projection is not configurable; disconnect or authority loss is its boundary. Both paths use the configured Rich or HTML renderer and exclude reasoning, tool traffic, token deltas, local prompt text, unknown sources, and stale generations. Each admitted block remains fenced to its exact target, profile/token stamp, leader epoch or follower registration generation, and session generation; non-idempotent acknowledgement ambiguity never authorizes replay.
|
|
404
404
|
|
|
405
|
-
`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.
|
|
405
|
+
`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. Bridge-owned assistant output and activity projection share one activity-publication sequence admitted synchronously from the activity bus, so later tool disclosures cannot overtake earlier assistant blocks. Activity targets and transport authority are captured before queued work starts. Active Telegram-turn final replies/artifacts and automatic compaction notices use that same publication owner before the existing direct/follower transport split, without mutually waiting on separate output tails. Final settlement captures the exact active turn and assistant result and reserves its publication position before config loading. Empty outcomes without a publication candidate reserve no position. Replacement, preparation failure, or an unused reservation releases the position without resetting or replying for a replacement turn. Final errors also publish through this owner. Reservations accept one task; cancellation cannot undo a published task, and session reset releases unresolved reservations while fencing old queued tasks. Terminal assistant messages with a publication candidate reserve the final position synchronously; settlement consumes that reservation only for the exact originating turn. Compaction notices enter the same queue immediately, behind the reserved final rather than through a separate notice buffer. Settlement, a new agent run, or session replacement cancels an unconsumed reservation. Notice target and authority are captured when the event is observed. Pi lifecycle completion does not wait for network publication. This order is process-local and does not serialize unrelated instances or independent registered activity handlers. Settlement, replacement, disconnect, failure, or stale authority clears only local ownership; already-sent activity messages remain in chat.
|
|
406
406
|
|
|
407
407
|
Telegram prompt guidance is context- and authority-aware. The package and source-checkout extension contribute `telegram-bridge`, optional `generated-control-surface`, and `generative-apps` Skills through Pi resource discovery. Generated Control Surface treats `interface = f(state, capabilities, intent)` as a renderer-neutral primitive, compiling transient evidence-backed controls over domain-owned workflows, systems, navigation, supervision, and decisions without creating parallel application state. It composes an ordered ragged sequence of independently sized semantic rows rather than filling a rectangular grid: compact rows contain genuine peers, singleton rows isolate structurally independent actions, and rectangular layouts remain reserved for genuinely spatial state. Text-bearing controls use at most two columns and flow into additional rows, while denser rows are reserved for short position-bearing glyphs or codes and never exceed the eight-column phone-width UX maximum. Vertical extent is independent: a true spatial surface may retain substantially more rows, while non-spatial button walls route to grouping, disclosure, or pagination. Symmetry is treated as an evidence claim about equal relationships or real spatial topology; an abstract layout catalog supplies adaptable singleton, peer, staged, navigational, repeated-pair, and rectangular shapes without forcing tasks into preset grids. Repeated controls carry the smallest sufficient action delta when visible conversation is unambiguous; larger or error-prone state moves to a deterministic task-owned Markdown artifact, correctness-sensitive transitions move to a small domain-owned transition implementation, and repeated clicks are adjudicated against current state rather than stale button appearance. Its filesystem adapter reserves the first full-width row for parent traversal outside root, places available Previous/Next controls together in one compact row immediately afterward, orders visible directories, hidden directories, visible files, and hidden files alphabetically within each category before fixed ten-entry pagination, renders path/range metadata as stacked status-style key-value rows instead of middle-dot prose, emits the complete Telegram control set through one JSON-matrix action, suppresses duplicate plain/monospaced listings and default Refresh unless user preference overrides presentation, and retains an ordinary numbered fallback when buttons are unavailable. 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. Repeated stable interactions may graduate from that model-mediated surface into a reviewed Generative App whose deterministic bound methods bypass Pi queue admission; the `generative-apps` Skill owns this compilation and operating workflow while the underlying capability retains domain authority. Telegram-originated turns route to the stable Skill contracts and retain dynamic blocks such as `[voice] delivery: automatic voice`; the Skills and public documentation own syntax, target routing, Threaded Mode behavior, Generative App operation, and diagnostics.
|
|
408
408
|
|
|
@@ -424,6 +424,10 @@ Local regression evidence covers continuity and cleanup authority; operator-coor
|
|
|
424
424
|
|
|
425
425
|
Operator-confirmed client flow: send ordinary text from the **All** tab; Telegram creates a new thread containing that text. Choose **Replace/restore thread…** and the existing Pi instance. Successful leader Restore binds and renames the new thread, dispatches the original message to the same Pi, and removes the old thread. The resulting prompt's thread-name label reflects the restored destination, not proof that the user typed in the old thread. A threadless `/start` is not equivalent to this ordinary-text flow.
|
|
426
426
|
|
|
427
|
+
Unselected All-tab command choosers expire 60 minutes after the original Telegram message timestamp. Expiry reports terminal settlement only for the exact still-deferred update and makes its callback inert; it does not execute the command or discard an accepted queue receipt. A valid destination selection pauses expiry during dispatch and suppresses concurrent repeat clicks. Failed attempts return to the original deadline without extending it, matching the replay expiry policy after restart. Successful local command dispatch settles a still-deferred source without waiting for background menu delivery; an accepted Pi queue receipt retains its own settlement contract. Follower command forwarding retires a still-deferred source only after confirmed acceptance; thrown or retryable transfer outcomes retain it. Expired replay completes without recreating the chooser. Worker stop invalidates pending chooser authority even without a usable timestamp. The expiry timer rechecks and rearms against its original deadline after backward wall-clock changes. Missing/invalid timestamps are not guessed, and `/thread`, bound-thread messages, ordinary prompts, and classic mode do not use this policy. Journal-write failure preserves the source for recovery; an expired visible chooser may remain, but its button cannot route the command. At pending capacity, new chooser admission fails retryably rather than silently evicting unresolved All commands.
|
|
428
|
+
|
|
429
|
+
For repeated unselected All-tab `/start`, a newer durable source supersedes older exact-text equivalents only after its chooser message ID is returned. Coalescing requires the same chat, user, and active admission worker; different arguments, other commands, and previously selected intents are not combined. Superseded chooser messages may remain visible, but their callbacks are inert. Storage failure retains the source according to the worker's fail-closed settlement contract. Restart may recreate a chooser for a still-unexpired source using currently routable targets; it does not automatically forward that command into a restored thread. This does not purge historical Telegram messages or promise that no fresh chooser appears after restart.
|
|
430
|
+
|
|
427
431
|
The bounded pending reroute owns its original source target and the returned chooser message ID independently from remaining messages. Authenticated callbacks must match the stored chat/chooser and any supplied thread field. `InaccessibleMessage` may omit the thread field; a missing callback message, unknown chooser, conflicting identity, threadless source, or already-owned Restore source fails closed rather than forwarding to the old target. Restart or expiry without that pending identity does not authorize reconstruction from callback data alone. When the original input itself has no thread ID (including an All-tab `/start`), the chooser offers routing only, not Restore; old Restore callbacks explain that a plain message must first be sent in the destination thread. Client tab selection is not inferred from recent topic creation or from the chosen Pi instance.
|
|
428
432
|
|
|
429
433
|
After dispatch, cleanup retries retain source identity but never redispatch accepted messages. Confirmed typed HTTP 400 `message to delete not found` completes message deletion idempotently; permission and transient failures remain errors. Reroute cleanup rechecks current bindings, live targets, reservations, and pending provisions before close, before delete, and on retry. Other destructive cleanup origins use the shared synchronous target-protection policy: explicit retirement permits only its unchanged departing binding, persisted shutdown intent permits only its original pre-intent binding, and reservation/provision cleanup permits only the corresponding unchanged claim. Protection checks also guard post-API local invalidation, reservation, and disconnect completion. A newly protected target cancels remaining cleanup. Already-issued remote operations cannot be undone by a later local ownership change; checks prevent subsequent effects, not retroactive cancellation.
|
|
@@ -8,21 +8,41 @@ Text handlers use the portable [Command Template Standard](./command-templates.m
|
|
|
8
8
|
|
|
9
9
|
## Public Assistant Output
|
|
10
10
|
|
|
11
|
-
Every completed `assistant-segment` with `placement: "intermediate"` from a Telegram-originated turn is delivered as its own message to the immutable originating target before the ordinary active-turn final reply. Final and terminal-partial Telegram segments remain owned by active-turn settlement so the final answer, voice, buttons, previews, and artifacts are not duplicated.
|
|
11
|
+
Every completed `assistant-segment` with `placement: "intermediate"` from a Telegram-originated turn is delivered as its own message to the immutable originating target before the ordinary active-turn final reply. Final and terminal-partial Telegram segments remain owned by active-turn settlement so the final answer, voice, buttons, previews, and artifacts are not duplicated. Activity captures the originating turn's optional `replyToMessageId` at agent start; delayed projection uses that captured prompt rather than the current active turn. Once-per-turn quoting remains: the first successfully delivered permanent reply carries the prompt anchor, and subsequent messages/chunks do not repeat it. Native/HTML text, voice, ordinary uploads, Rich media, and operational delivery share the reply reservation owner. Known send rejection releases an unused anchor for fallback; an unknown ACK retains it because Telegram may already have delivered the message. Turn-start reset joins the publication FIFO behind the preceding turn's final instead of clearing its quote state early. Local/autonomous output without a Telegram prompt invents no anchor.
|
|
12
12
|
|
|
13
13
|
While Telegram is connected, local, autonomous, and unclassified extension follow-up Pi work always projects every completed public block—including visible commentary/checkpoints and the final answer—to the instance's authorized target in source order. There is no projection setting or opt-out: disconnecting Telegram is the boundary that stops this companion surface. 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; 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
|
|
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 joins the shared [activity-publication order](./architecture.md), preserving commentary-before-final order without blocking Pi lifecycle completion. A `commit-unknown` outcome never permits replay.
|
|
16
|
+
|
|
17
|
+
Assistant-message completion seals its preview state: queued follow-up drafts and late updates are suppressed. Native final delivery still waits for the already-issued draft request before sending the permanent answer, so an older draft is not deliberately allowed to overtake the final. Intermediate publication seals and drains its captured preview before sending permanent text. Preview rollover itself sends no permanent message: it carries the preceding delivery boundary and draft identity into the next state without holding the Pi message-start hook. The next draft waits for that publication to settle, including failure or cancellation. Active-turn final delivery captures its preview operations before entering the background queue: it drains only the originating draft, leaves a successor's preview untouched, and cannot wait for a successor publication queued behind itself. Delivery authority is rechecked after the captured flush; if the original preview has been replaced, ordinary final sending remains the queue's responsibility. This does not promise instant delivery or eliminate Telegram/client latency.
|
|
18
|
+
|
|
19
|
+
Assistant previews use a two-second leading/trailing throttle per preview controller, with at most one request in flight. The first eligible snapshot sends immediately; changes inside the window replace pending text, and one trailing timer sends the latest safe snapshot without moving its deadline on every delta. Message/turn rollover preserves the remaining interval. Sealing, clearing, or replacing preview state cancels its timer; final publication drains only an already-issued request and never waits for the throttle deadline.
|
|
20
|
+
|
|
21
|
+
The Bot API client does not replay draft snapshots through API retry backoff. After a retryable draft HTTP failure (`429` or `5xx`), it defers new draft requests for that bot/chat/thread using the existing `Retry-After` or default backoff delay; it stores a deadline, not a body or a background retry. A deferred update does not advance the preview's last-delivered text. Fresh updates can send after the deadline, while a sealed preview cannot resume. Different bots and thread targets have independent cooldowns; credential rotation for the same bot does not bypass its deadline. Permanent replies retain their existing retry policy. Already-issued requests and Telegram/client rendering can still delay visible completion.
|
|
22
|
+
|
|
23
|
+
### Publication Limits
|
|
24
|
+
|
|
25
|
+
The ordered publication queue is process-local, in-memory, and not a durable outbox. Slow preparation or delivery holds later publications from the same instance; Pi lifecycle hooks do not wait for network publication. Terminal preview cleanup also runs in the background, captures the original draft before scheduling, and rechecks draft identity and delivery authority after a pending flush; it cannot clear a replacement draft. A new session drops queued old-generation work and starts its own sequence without waiting for an already-running call. Separate instances have separate publication queues, but still share Telegram/leader transport limits.
|
|
26
|
+
|
|
27
|
+
There is no independent hard cap on pending publications and no publication-wide timeout. Transport and command-template deadlines apply to their own operations; a programmatic handler/provider that never settles can hold this instance's output indefinitely and allow pending work to accumulate. Session reset invalidates queued work but cannot forcibly stop arbitrary provider code or undo requests already issued. Bounded rendered thinking/tool payloads are not a bound on this pending queue.
|
|
28
|
+
|
|
29
|
+
A failed task does not poison later tasks. Pending publications are not replayed after restart, and uncertain non-idempotent acknowledgements never authorize automatic replay. These are ordering and authority guarantees, not exactly-once delivery or a guarantee that every remote send succeeds.
|
|
30
|
+
|
|
31
|
+
## Attachment Delivery
|
|
32
|
+
|
|
33
|
+
An uncertain `sendVoice` acknowledgement stops the logical voice batch: it cannot advance to another synthesis provider or voice item, and active-turn delivery records the uncertainty without issuing text fallback. Ordinary synthesis failures retain provider fallback; later independent publications can proceed.
|
|
34
|
+
|
|
35
|
+
Active Telegram-turn queued attachments recheck turn/session authority after file checks, before each upload, and before failure notices. Cancellation leaves the attachment list intact and suppresses later files and fallback text. Rich upload acknowledgements do not publish ownership into a replacement context. Already-issued uploads cannot be undone, and ambiguous results never authorize replay.
|
|
16
36
|
|
|
17
37
|
## Technical Activity
|
|
18
38
|
|
|
19
39
|
`assistant.activity` defaults to `verbose` when absent and accepts four modes: `quiet`, `thinking`, `tools`, and `verbose`. Explicit stored values remain unchanged; invalid values fail closed to `quiet`. Every Pi instance reloads this shared file-backed mode at `agent-start`, so a setting changed from one thread applies to subsequent runs in the other live instances without requiring process reload. `thinking` shows only provider-exposed thinking, `tools` shows only completed executed tools, and `verbose` shows both without mixing technical UI into assistant Markdown.
|
|
20
40
|
|
|
21
41
|
- Provider-exposed thinking updates one persistent ordinary HTML message containing only a standard Telegram `<blockquote expandable>` with a bounded redacted latest-text window. Omitting a separate icon/level header saves one chat row while the disclosure's unique shape remains recognizable. Inline Markdown emphasis and code render as Telegram HTML instead of leaking raw markers. The bridge sends the message once, edits it only as thinking grows, and leaves the disclosure in chat; it never uses Rich drafts/Rich Messages. Providers that expose no thinking produce none.
|
|
22
|
-
- Completed executed tools use native `sendRichMessage` block objects. Each tool is one closed root details node summarized as bold `<Tool>:` plus monospaced `<status>`; snake-case root labels render as title words (`telegram_attach` → `Telegram Attach`), while each word preserves a leading two- or three-character repeated-letter prefix in uppercase (`ff_find` → `FF Find`). The native disclosure chevron identifies the row, and opening it immediately reveals the open `arguments` child plus separate closed retained `update N`, `result`, or `error` child details. Each child summary is one lowercase monospaced label—visually a quote-free outer JSON key with no icon, list marker, or heading emphasis—and contains one preformatted `json` block. Updates remain chronological, dropped-update counts appear in the first retained update summary, and arrays of object entries keep the denser `[{ ... }, { ... }]` layout. A known-safe Rich HTTP 400 rejection falls back once to the previous expandable HTML representation; ambiguous non-idempotent outcomes never replay.
|
|
42
|
+
- Completed executed tools use native `sendRichMessage` block objects. Each tool is one closed root details node summarized as bold `<Tool>:` plus monospaced `<status>`; snake-case root labels render as title words (`telegram_attach` → `Telegram Attach`), while each word preserves a leading two- or three-character repeated-letter prefix in uppercase (`ff_find` → `FF Find`). The native disclosure chevron identifies the row, and opening it immediately reveals the open `arguments` child plus separate closed retained `update N`, `result`, or `error` child details. Each child summary is one lowercase monospaced label—visually a quote-free outer JSON key with no icon, list marker, or heading emphasis—and contains one preformatted `json` block. Updates remain chronological, dropped-update counts appear in the first retained update summary, and arrays of object entries keep the denser `[{ ... }, { ... }]` layout. A known-safe Rich HTTP 400 rejection falls back once to the previous expandable HTML representation only while the admitted generation and authority remain current; ambiguous non-idempotent outcomes never replay.
|
|
23
43
|
- Thinking `sendMessage`/`editMessageText` disables link previews and breaks HTTP(S) auto-link recognition inside evidence. Rich tool messages set `skip_entity_detection: true`, so URL-like arguments, updates, and results remain literal code. Consecutive tools coalesce by editing one message only while target, activity, generation, ordering boundary, tool count, and serialized-size bounds still match. Assistant or thinking content closes the batch. A non-idempotent send with unknown commit state is never replayed; failed or ambiguous edits start no fallback send.
|
|
24
44
|
|
|
25
|
-
Thinking retains only a bounded latest-text window, tool updates retain only a bounded latest-entry window, and a session reset abandons queued old-generation work without making the replacement session wait for an old transport call.
|
|
45
|
+
Thinking retains only a bounded latest-text window, tool updates retain only a bounded latest-entry window, and a session reset abandons queued old-generation work without making the replacement session wait for an old transport call. Late refresh results, acknowledgements, failures, and settlement continuations recheck their captured generation and authority before changing state; old work cannot replace new message handles, clear new tool arguments, or block the replacement's thinking output.
|
|
26
46
|
|
|
27
47
|
Technical activity is operational evidence, not part of the semantic answer stream. Final-answer rendering, voice policy, artifacts, and quiet behavior remain unchanged.
|
|
28
48
|
|
|
@@ -50,7 +70,7 @@ The voice pipeline is detailed below: configured `type: "voice"` handlers first,
|
|
|
50
70
|
|
|
51
71
|
### Single Rich attachment result
|
|
52
72
|
|
|
53
|
-
When `assistant.rendering` is `"rich"`, a Telegram-originated turn that queues exactly one probe-confirmed PNG/JPEG photo, MP4 video, or MP3 audio file through `telegram_attach` can combine that artifact with the final assistant Markdown in one multipart `sendRichMessage` result. The bridge normalizes the Markdown, adds one `tg://photo`, `tg://video`, or `tg://audio` reference, preserves the
|
|
73
|
+
When `assistant.rendering` is `"rich"`, a Telegram-originated turn that queues exactly one probe-confirmed PNG/JPEG photo, MP4 video, or MP3 audio file through `telegram_attach` can combine that artifact with the final assistant Markdown in one multipart `sendRichMessage` result. The bridge normalizes the Markdown, adds one `tg://photo`, `tg://video`, or `tg://audio` reference, preserves the assigned thread and once-per-turn reply policy, carries assistant-authored inline buttons, and records the returned message id under the exact local/follower ownership scope.
|
|
54
74
|
|
|
55
75
|
The optimization is deliberately narrow. HTML rendering, empty final text, multiple files, documents and other unsupported formats, Guest Mode, explicit `telegram_voice`, voice-preferred turns, and OGG/Opus artifacts retain their established text/attachment/voice paths. A known-safe Rich upload rejection falls back to those paths. A `commit-unknown` transport outcome or a nominally successful upload without a verifiable message id never falls back or replays because the first non-idempotent send may already have committed.
|
|
56
76
|
|
|
@@ -127,6 +147,8 @@ const dispose = registerTelegramVoiceSynthesisProvider(
|
|
|
127
147
|
|
|
128
148
|
Synthesis providers receive the extracted `telegram_voice` text plus optional `lang`/`rate` hints. Stable registrations pass a durable `id`; omitted ids remain a compatibility path for older providers. Providers own translation, TTS, speech rewriting, and OGG/Opus conversion. The bridge validates that the returned file ends in `.ogg` or `.opus`, sends it through Telegram `sendVoice`, and falls back to planned text if delivery fails before any visible text was delivered. Providers run after configured and programmatic voice handlers in the priority chain above.
|
|
129
149
|
|
|
150
|
+
Active Telegram-turn voice artifacts carry the turn/session currentness check through synthesis, file conversion, recording actions, and upload. Authority loss suppresses not-yet-issued transport effects, later voice replies, provider fallbacks, and final-text fallback. It does not undo an already-issued request or interrupt a provider already synthesizing.
|
|
151
|
+
|
|
130
152
|
## Voice Markup
|
|
131
153
|
|
|
132
154
|
Assistant replies can include hidden voice actions as a positional compact cell or JSON object:
|
|
@@ -119,6 +119,7 @@ Hidden/default semantics are represented by absence:
|
|
|
119
119
|
|
|
120
120
|
- `threads.automaticCleanup` defaults to `true`; graceful Pi quit deletes the instance's bound Threaded Mode tab without prompting but preserves the owner slot as independent restart intent. Set it to `false`, or use `🧹 Thread cleanup` in Telegram Settings, to preserve the tab too. A confirmed `/telegram-disconnect`, unlike quit, clears restart ownership. Settings views and cleanup reload shared config before evaluating this switch, so another live Pi instance's update takes effect without restarting. Confirmed leader/follower teardown persists an exact target/runtime-generation cleanup intent before Telegram deletion; an interrupted attempt remains retryable by the current or successor leader under current authority and clears only after confirmed deletion. A same-profile replacement leader first adopts any still-active binding and cancels its superseded cleanup, so startup never deletes and recreates a reusable thread. If a follower's graceful envelope is missed, the leader may create the same fenced cleanup only after its heartbeat is stale, the OS confirms the exact registered PID no longer exists, cleanup remains enabled, and no replacement registration can overtake deletion. Heartbeat loss alone, live/unknown process liveness, IPC failure, and auth failure remain non-destructive. Invalid-config recovery makes the setting unresolved and therefore skips destructive cleanup. Manual `/telegram-disconnect` keeps its confirmation and teardown behavior regardless of this setting.
|
|
121
121
|
- Every complete intermediate assistant text block from a Telegram-originated turn is delivered once to its immutable target before the existing final reply; final and terminal-partial segments stay with settlement to prevent duplicate replies. While Telegram transport remains authorized, local/autonomous work also projects every completed public block, including commentary and the final block. This connected companion projection is always active and excludes token deltas, hidden reasoning, tool calls/arguments/results, empty blocks, unknown sources, and stale authority. Projection uses the configured Rich or HTML assistant renderer and binds admitted work to the exact target, profile/token transport generation, direct leader epoch or follower registration generation, and session generation. Retired top-level and `assistant.proactivePush` keys are ignored; the nested key is removed during config normalization.
|
|
122
|
+
- `assistant.draftPreviews` defaults to `true` when absent; explicit `true` or `false` values, including compatibility aliases, remain authoritative. Reading the default does not persist or migrate configuration. Telegram Settings writes the canonical field.
|
|
122
123
|
- `assistant.activity` accepts exactly `"quiet"`, `"thinking"`, `"tools"`, or `"verbose"`; omitted values default to `"verbose"`, explicit values remain unchanged, and invalid values fail closed to `"quiet"`. Each Pi process reloads the shared file-backed value at `agent-start`, so multi-instance activity isolation never relies on a stale process-local config snapshot. `thinking` shows only provider-exposed thinking, `tools` shows only completed tool activity, and `verbose` shows both. Thinking uses persistent ordinary HTML `sendMessage`/`editMessageText` disclosure with a standard expandable blockquote, a `🧠` header carrying the current Pi thinking level, and bounded redacted text whose inline Markdown renders as Telegram HTML. Tools use native Rich Messages with one header followed by separate closed details and JSON pre blocks for bounded redacted arguments, retained updates, and results/errors. Thinking disables link previews on every HTML send/edit and neutralizes HTTP(S) auto-link detection; Rich tool output disables automatic entity detection, with the same protections retained by its HTML fallback. Consecutive tools coalesce only inside the same ordered activity segment and bounded message. Legacy `assistant.activityVerbosity` is read only when `assistant.activity` is absent and is removed by the next Activity Settings write.
|
|
123
124
|
- Voice Reply `hidden`: no `voice.replyMode` key is persisted; legacy `manual` resolves to this silent default. `mirror` adds `[voice] delivery: automatic voice` only to voice/audio-input turns, while `always` adds the same effective line to every Telegram turn.
|
|
124
125
|
- Agent activity status is not configurable. While Telegram transport remains authorized, Telegram uses native `sendChatAction(typing)` / product `...active` status as the automatic in-chat work signal for unsettled agent and compaction work. Extension-owned blocking UI prompts pause it and completion resumes it while either work owner remains active.
|
|
@@ -553,13 +553,33 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
553
553
|
editInteractiveMessage,
|
|
554
554
|
recordRuntimeEvent,
|
|
555
555
|
});
|
|
556
|
+
const nativeMarkdownDraftSender =
|
|
557
|
+
TelegramApi.createTelegramAssistantDraftSender({
|
|
558
|
+
getAssistantRenderingMode: configControls.getAssistantRenderingMode,
|
|
559
|
+
renderMarkdownToHtmlDraft: Replies.renderTelegramMarkdownToHtmlDraft,
|
|
560
|
+
sendMessageDraft,
|
|
561
|
+
sendRichMessageDraft,
|
|
562
|
+
});
|
|
563
|
+
const previewRuntime = Preview.createTelegramAssistantPreviewRuntime({
|
|
564
|
+
getActiveTurn: activeTurnRuntime.get,
|
|
565
|
+
isAssistantMessage: Replies.isAssistantAgentMessage,
|
|
566
|
+
getMessageText: Replies.getAgentMessageText,
|
|
567
|
+
getDefaultReplyToMessageId: activeTurnRuntime.getReplyToMessageId,
|
|
568
|
+
sendDraft: nativeMarkdownDraftSender,
|
|
569
|
+
canSend: configControls.areDraftPreviewsEnabled,
|
|
570
|
+
sendMarkdownReply,
|
|
571
|
+
recordRuntimeEvent,
|
|
572
|
+
...replyTransport,
|
|
573
|
+
});
|
|
556
574
|
const {
|
|
557
575
|
activityRuntime,
|
|
558
576
|
activityVerbosityRuntime,
|
|
559
577
|
assistantOutputRuntime,
|
|
578
|
+
publicationRuntime,
|
|
560
579
|
} = Bindings.createTelegramActivityBindingRuntime({
|
|
561
580
|
generation: deliveryGenerationSeed,
|
|
562
581
|
assistantOutput: {
|
|
582
|
+
prepareTelegramPreview: previewRuntime.preparePublication,
|
|
563
583
|
authority: {
|
|
564
584
|
getPreferredTarget: proactivePushTargetGetter,
|
|
565
585
|
getFallbackChatId: proactivePushChatIdGetter,
|
|
@@ -616,27 +636,10 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
616
636
|
sendUserMessage,
|
|
617
637
|
recordRuntimeEvent,
|
|
618
638
|
});
|
|
619
|
-
const
|
|
620
|
-
TelegramApi.createTelegramAssistantDraftSender({
|
|
621
|
-
getAssistantRenderingMode: configControls.getAssistantRenderingMode,
|
|
622
|
-
renderMarkdownToHtmlDraft: Replies.renderTelegramMarkdownToHtmlDraft,
|
|
623
|
-
sendMessageDraft,
|
|
624
|
-
sendRichMessageDraft,
|
|
625
|
-
});
|
|
626
|
-
const previewRuntime = Preview.createTelegramAssistantPreviewRuntime({
|
|
627
|
-
getActiveTurn: activeTurnRuntime.get,
|
|
628
|
-
isAssistantMessage: Replies.isAssistantAgentMessage,
|
|
629
|
-
getMessageText: Replies.getAgentMessageText,
|
|
630
|
-
getDefaultReplyToMessageId: activeTurnRuntime.getReplyToMessageId,
|
|
631
|
-
sendDraft: nativeMarkdownDraftSender,
|
|
632
|
-
canSend: configControls.areDraftPreviewsEnabled,
|
|
633
|
-
sendMarkdownReply,
|
|
634
|
-
recordRuntimeEvent,
|
|
635
|
-
...replyTransport,
|
|
636
|
-
});
|
|
637
|
-
const { finalizeMarkdownPreview } =
|
|
639
|
+
const { finalizeMarkdownPreview, preparePreviewDelivery } =
|
|
638
640
|
Outbound.createTelegramOutboundTextPreviewRuntime({
|
|
639
641
|
finalizeMarkdownPreview: previewRuntime.finalizeMarkdown,
|
|
642
|
+
preparePreviewDelivery: previewRuntime.prepareDelivery,
|
|
640
643
|
execCommand: CommandTemplates.execCommandTemplate,
|
|
641
644
|
getHandlers: configStore.getOutboundHandlers,
|
|
642
645
|
recordRuntimeEvent,
|
|
@@ -1365,6 +1368,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1365
1368
|
activityRuntime,
|
|
1366
1369
|
activityVerbosityRuntime,
|
|
1367
1370
|
assistantOutputRuntime,
|
|
1371
|
+
publicationRuntime,
|
|
1368
1372
|
configStore,
|
|
1369
1373
|
abort,
|
|
1370
1374
|
typing,
|
|
@@ -1395,6 +1399,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1395
1399
|
deleteMessage: deleteTelegramMessage,
|
|
1396
1400
|
sendGuestReply,
|
|
1397
1401
|
finalizeMarkdownPreview,
|
|
1402
|
+
preparePreviewDelivery,
|
|
1398
1403
|
proactivePushTargetGetter,
|
|
1399
1404
|
getAssistantRenderingMode: configControls.getAssistantRenderingMode,
|
|
1400
1405
|
recordMessageOwnership: messageOwnershipRuntime.recordLocal,
|