@llblab/pi-kit 0.1.13 → 0.3.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 +1 -0
- package/CHANGELOG.md +10 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-actors/AGENTS.md +5 -3
- package/node_modules/@llblab/pi-actors/BACKLOG.md +1 -1
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +15 -0
- package/node_modules/@llblab/pi-actors/README.md +5 -3
- package/node_modules/@llblab/pi-actors/dist/index.js +4 -1
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +2 -2
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +42 -19
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.d.ts +2 -1
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +7 -2
- package/node_modules/@llblab/pi-actors/dist/lib/limits.d.ts +9 -0
- package/node_modules/@llblab/pi-actors/dist/lib/limits.js +9 -0
- package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +10 -11
- package/node_modules/@llblab/pi-actors/dist/lib/observability.js +81 -56
- package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +31 -0
- package/node_modules/@llblab/pi-actors/dist/lib/pi.js +180 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +115 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +623 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.d.ts +3 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +341 -13
- package/node_modules/@llblab/pi-actors/dist/lib/runs-trace.d.ts +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/runs-trace.js +5 -3
- package/node_modules/@llblab/pi-actors/dist/lib/session-evidence.d.ts +16 -0
- package/node_modules/@llblab/pi-actors/dist/lib/session-evidence.js +143 -0
- package/node_modules/@llblab/pi-actors/dist/lib/temp.js +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/tools-inspect.js +3 -1
- package/node_modules/@llblab/pi-actors/dist/scripts/async-runner.mjs +5 -19
- package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +2 -2
- package/node_modules/@llblab/pi-actors/dist/skills/actors/references/runs.md +1 -1
- package/node_modules/@llblab/pi-actors/dist/skills/swarm/SKILL.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/README.md +1 -0
- package/node_modules/@llblab/pi-actors/docs/async-runs.md +8 -4
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +207 -0
- package/node_modules/@llblab/pi-actors/index.ts +4 -1
- package/node_modules/@llblab/pi-actors/lib/async-runs.ts +42 -21
- package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +8 -3
- package/node_modules/@llblab/pi-actors/lib/limits.ts +9 -0
- package/node_modules/@llblab/pi-actors/lib/observability.ts +97 -78
- package/node_modules/@llblab/pi-actors/lib/pi.ts +210 -0
- package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +800 -0
- package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +370 -18
- package/node_modules/@llblab/pi-actors/lib/runs-trace.ts +6 -4
- package/node_modules/@llblab/pi-actors/lib/session-evidence.ts +153 -0
- package/node_modules/@llblab/pi-actors/lib/temp.ts +1 -1
- package/node_modules/@llblab/pi-actors/lib/tools-inspect.ts +4 -1
- package/node_modules/@llblab/pi-actors/package.json +3 -3
- package/node_modules/@llblab/pi-actors/scripts/async-runner.mjs +5 -19
- package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +2 -2
- package/node_modules/@llblab/pi-actors/skills/actors/references/runs.md +1 -1
- package/node_modules/@llblab/pi-actors/skills/swarm/SKILL.md +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +4 -4
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +25 -0
- package/node_modules/@llblab/pi-telegram/README.md +4 -4
- package/node_modules/@llblab/pi-telegram/docs/activity.md +4 -4
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +10 -10
- package/node_modules/@llblab/pi-telegram/docs/compact-matrix-literal.md +16 -14
- package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +13 -22
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +6 -7
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +4 -5
- package/node_modules/@llblab/pi-telegram/docs/voice.md +12 -2
- package/node_modules/@llblab/pi-telegram/index.ts +0 -1
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +10 -5
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +16 -17
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +16 -6
- package/node_modules/@llblab/pi-telegram/lib/config.ts +25 -25
- package/node_modules/@llblab/pi-telegram/lib/media.ts +12 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +10 -73
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +9 -8
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +212 -82
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +78 -36
- package/node_modules/@llblab/pi-telegram/lib/status.ts +23 -4
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +10 -1
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1 -1
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +61 -205
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/references/capability-adapters.md +27 -0
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/references/layout-and-state.md +35 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +70 -110
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/configuration.md +15 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/delivery-and-threads.md +27 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +14 -0
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-actors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local Actor Kernel for Pi",
|
|
6
6
|
"keywords": [
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"image": "https://raw.githubusercontent.com/llblab/pi-actors/main/banner.jpg"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@earendil-works/pi-coding-agent": "
|
|
70
|
-
"@earendil-works/pi-tui": "
|
|
69
|
+
"@earendil-works/pi-coding-agent": ">=0.84.4",
|
|
70
|
+
"@earendil-works/pi-tui": ">=0.84.4"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@types/node": "latest",
|
|
@@ -284,9 +284,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
284
284
|
complete_allowed: missing.length === 0,
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
-
function getCommandDoneDelivery(result) {
|
|
288
|
-
return result.code !== 0 || activeSubagents > 0 ? "followup" : "log";
|
|
289
|
-
}
|
|
290
287
|
function progressRunning() {
|
|
291
288
|
progress("running", {
|
|
292
289
|
activeSubagents,
|
|
@@ -433,21 +430,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
433
430
|
...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
|
|
434
431
|
});
|
|
435
432
|
}
|
|
436
|
-
event("command.done", {
|
|
437
|
-
activeSubagents,
|
|
438
|
-
command_id: commandId,
|
|
439
|
-
code: result.code,
|
|
440
|
-
command: commandDetail,
|
|
441
|
-
killed: result.killed,
|
|
442
|
-
...captureDetails(result),
|
|
443
|
-
...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
|
|
444
|
-
...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
|
|
445
|
-
...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
|
|
446
|
-
...(commandSessionFiles(session.sessionDir).length > 0
|
|
447
|
-
? { session_files: commandSessionFiles(session.sessionDir) }
|
|
448
|
-
: {}),
|
|
449
|
-
...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
|
|
450
|
-
});
|
|
451
433
|
observation(
|
|
452
434
|
"command.done",
|
|
453
435
|
`Command ${summarizeCommandDetail(commandDetail)} completed with code ${result.code}`,
|
|
@@ -462,9 +444,13 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
462
444
|
...captureDetails(result),
|
|
463
445
|
...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
|
|
464
446
|
...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
|
|
447
|
+
...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
|
|
448
|
+
...(commandSessionFiles(session.sessionDir).length > 0
|
|
449
|
+
? { session_files: commandSessionFiles(session.sessionDir) }
|
|
450
|
+
: {}),
|
|
465
451
|
...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
|
|
466
452
|
},
|
|
467
|
-
|
|
453
|
+
"log",
|
|
468
454
|
result.code === 0 ? "info" : "error",
|
|
469
455
|
);
|
|
470
456
|
progressRunning();
|
|
@@ -85,7 +85,7 @@ There are two distinct multi-instance shapes:
|
|
|
85
85
|
|
|
86
86
|
In host-coordinator mode, the top-level agent receives declarative outcomes, preserves user authority and global context, delegates bounded concrete execution, and owns integration plus final validation. It is not merely another worker after delegation begins. One bounded implementation worker normally runs with reasoning off; consequential output receives a separate reasoning-enabled review. Several independent participants or reviewers additionally use `swarm`.
|
|
87
87
|
|
|
88
|
-
Delegation is not mandatory for every prompt. Work inline when one short bounded act has one natural validation boundary and spawning would add more coordination than isolation, latency hiding, clean context, or continued coordinator availability can repay. For admitted delegation, prefer
|
|
88
|
+
Delegation is not mandatory for every prompt. Work inline when one short bounded act has one natural validation boundary and spawning would add more coordination than isolation, latency hiding, clean context, or continued coordinator availability can repay. For admitted delegation, prefer the settled completion batch and durable Trace/artifacts; inspect on meaningful attention, operator request, or an evidence-based overdue timer rather than busy polling. Treat `attention: "steer"` as an actor-authored urgent semantic checkpoint at Pi's next safe boundary, never as a status-derived completion signal; the later root terminal still arrives through its ordinary completion batch.
|
|
89
89
|
|
|
90
90
|
## Run workflow
|
|
91
91
|
|
|
@@ -97,7 +97,7 @@ Run = Recipe + Trace + Control
|
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
1. Spawn with the exact logical Recipe identity and caller-owned values.
|
|
100
|
-
2. Retain the returned `run:<id>` and normally wait for
|
|
100
|
+
2. Retain the returned `run:<id>` and normally wait for its settled completion batch instead of polling.
|
|
101
101
|
3. Inspect `view=trace` when retained observations or attention matter.
|
|
102
102
|
4. Inspect `view=control` before diagnosing service readiness, stale work, or saturation.
|
|
103
103
|
5. Send `message` only for an action declared and consumed by that controlled Recipe.
|
|
@@ -14,7 +14,7 @@ A rare Skill Recipe may declare `singleton: true`. Do not pass `as`: the runtime
|
|
|
14
14
|
|
|
15
15
|
## Observe
|
|
16
16
|
|
|
17
|
-
Normally wait for
|
|
17
|
+
Normally wait for the settled completion batch. Inspect only when requested, when meaningful attention arrives, or when the Run is overdue or blocked:
|
|
18
18
|
|
|
19
19
|
```text
|
|
20
20
|
inspect target=run:<id> view=recipe
|
|
@@ -15,7 +15,7 @@ A swarm can be coordinated without an external gateway. In this model the curren
|
|
|
15
15
|
|
|
16
16
|
This resembles gateway orchestration in dependency direction but not in ownership: the coordinator is itself an agent instance with inspectable Runs, not an infrastructure service that implicitly creates sessions. Preserve that distinction in prompts, docs, recovery, and target routing.
|
|
17
17
|
|
|
18
|
-
Once work is delegated, keep the coordinator available for decisions and integration instead of duplicating participant implementation. Wait for
|
|
18
|
+
Once work is delegated, keep the coordinator available for decisions and integration instead of duplicating participant implementation. Wait for the settled completion batch by default; use meaningful attention or evidence-based timers for overdue work rather than a tight inspection loop.
|
|
19
19
|
|
|
20
20
|
## Reasoning allocation
|
|
21
21
|
|
|
@@ -92,9 +92,9 @@ Use the relevant local skill before non-trivial work in its domain. Keep skill o
|
|
|
92
92
|
- Telegram extension side effects must not hold Pi's core lifecycle hostage after semantic completion. Preserve ordering in extension-owned background work, record failures, and fence target/profile/transport/session authority.
|
|
93
93
|
- Complete assistant/guest model answers use Telegram-native Rich Markdown. Harness-owned menus, status, diagnostics, thinking, and tool evidence remain explicit HTML/plain or their documented native surface. Preserve literal code and structurally safe chunking; never split invalid markup.
|
|
94
94
|
- `preview` owns streaming lifecycle only, not assistant rendering. Finalization waits for active preview flushes and must not issue pre/post-final draft-clear calls that create transient Telegram draft UI.
|
|
95
|
-
- Native `sendChatAction(typing)` is the automatic activity signal. Do not invent extra in-chat work indicators or emit activity for startup/connect/reload/recovery alone.
|
|
96
|
-
- Public activity handlers and companion delivery are asynchronous, target-bound, generation-fenced surfaces. Token deltas, hidden reasoning, unknown sources, and stale authority never enter public projection.
|
|
97
|
-
- UI labels, emoji semantics, navigation, settings controls, callback namespaces, voice behavior, command templates, and assistant markup follow the linked `/docs` contracts
|
|
95
|
+
- Native `sendChatAction(typing)` is the automatic activity signal for unsettled agent and compaction work while Telegram transport is authorized. Extension-owned blocking UI prompts pause it and completion resumes it while either work owner remains active. Do not invent extra in-chat work indicators or emit activity for startup/connect/reload/recovery alone.
|
|
96
|
+
- Public activity handlers and connected companion delivery are asynchronous, target-bound, generation-fenced surfaces. Connected companion projection has no independent opt-out: disconnect or authority loss is its boundary. Token deltas, hidden reasoning, unknown sources, and stale authority never enter public projection.
|
|
97
|
+
- UI labels, emoji semantics, navigation, settings controls, callback namespaces, voice behavior, command templates, and assistant markup follow the linked `/docs` contracts. Generated human-readable prompt-button labels use `emoji + space + text`; emoji-free text is only a reasoned no-semantic-marker fallback. Non-spatial generated controls default to top-level vertical cells, with nested rows reserved for unmistakably compact peers. Do not restate other evolving UI details here.
|
|
98
98
|
|
|
99
99
|
## 5. Domain Ownership Index
|
|
100
100
|
|
|
@@ -125,7 +125,7 @@ The detailed map is canonical in [`docs/architecture.md`](./docs/architecture.md
|
|
|
125
125
|
- Keep interfaces consistent with their owning exported contract. Use local structural `*Like`/view types only for deliberate narrow projections, not duplicate source-of-truth models.
|
|
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
|
-
- 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
|
|
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
129
|
- 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.
|
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## 0.42.0: Compact Action Grammar
|
|
6
|
+
|
|
7
|
+
- `Adaptive Action Grammar`: Extracts the first semantically valid JSON or positional CML payload from noisy `telegram_button` and `telegram_voice` envelopes, tolerates bounded trailing commas and unmatched matrix noise, keeps malformed named JSON on the JSON path, and retains legacy attribute parsing as undocumented compatibility.
|
|
8
|
+
- `Voice Action Cells`: Adds compact `{text}`, `{text|lang}`, and `{text|lang|rate}` voice actions while preserving JSON for multiline, named, and escape-heavy payloads; each voice comment remains one independent artifact and matrix-shaped voice payloads fail closed.
|
|
9
|
+
- `Thread-Aware Buttons`: Tags prompts created by generated-button callbacks with the same resolved Telegram Thread label as ordinary inbound turns, preserving current local labels, persisted bindings, target routing, queue ordering, and callback selection behavior.
|
|
10
|
+
- `Focused Agent Guidance`: Makes generated control surfaces proactively emit useful emoji-led prompt buttons, keeps Telegram action syntax under the bridge owner, and moves specialized delivery, configuration, diagnosis, layout, state, and capability guidance behind conditional references to reduce default context without dropping safety constraints.
|
|
11
|
+
|
|
12
|
+
## 0.41.1: Focused Operator Status Hotfix
|
|
13
|
+
|
|
14
|
+
- `Next Turn Feedback`: Replies one Pi-aligned fully bold abort notice to the aborted Telegram turn, falls back to the `/next` command for local work, preserves completed intermediate output, and suppresses pending aborted assistant text so one forced transition no longer produces a contradictory second reply.
|
|
15
|
+
- `Status Telemetry`: Keeps the Tokens row compact with input/output totals and moves cache read/write plus the latest cache-hit rate into a dedicated Cache row.
|
|
16
|
+
- `Reply Transcription`: Runs replied voice and other media through the inbound handler/provider pipeline and appends provenance-scoped outputs inside the reply context, including Guest Mode, instead of leaving quoted audio as an untranscribed attachment.
|
|
17
|
+
- `Information Hierarchy`: Brings access-denial and Threaded Mode chooser headings back under the common fully bold emoji-led sentence/colon contract.
|
|
18
|
+
|
|
19
|
+
## 0.41.0: Connected Companion Presence
|
|
20
|
+
|
|
21
|
+
- `Always-On Projection`: Treats authorized Telegram connection as the public-output boundary for local, autonomous, and unclassified Pi work, always delivering completed visible blocks in order instead of exposing an independent proactive-push opt-out.
|
|
22
|
+
- `Configuration`: Removes Proactive Push from Telegram Settings and the public config contract, atomically drops retired `assistant.proactivePush` values during normalization, and routes stale pre-upgrade callbacks back to the current Settings surface with an explanatory notice.
|
|
23
|
+
- `Work Presence`: Keeps native Telegram typing aligned with unsettled agent and compaction work, pauses it only while extension-owned local UI prompts await the operator, and resumes it after those prompts even when compaction begins after low-level `agent_end`.
|
|
24
|
+
- `Compaction Notices`: Preserves observed `Compaction started` and terminal notices across Pi 0.84.4's post-`agent_end` automatic-compaction order, flushing them after the final-answer delivery tail at `agent_settled`.
|
|
25
|
+
- `Generated Controls`: Requires semantic emoji-led human-readable labels and defaults non-spatial controls to full-width vertical cells, reserving nested horizontal rows for unmistakably compact peers while retaining reasoned no-marker and genuine spatial-grammar exceptions.
|
|
26
|
+
- `Thread Presence`: Aligns the Threaded Mode instance-connected notice with the common information hierarchy by bolding the complete emoji-led sentence while adding nested bold-italic emphasis to the instance name.
|
|
27
|
+
- `Status Parity`: Renames the ambiguous Usage row to Tokens and mirrors Pi's compact terminal telemetry in one line, including inline `R`/`W` cache tokens and `CH` for the latest request's cache-hit rate rather than a cumulative-session approximation.
|
|
28
|
+
- `Voice Setup`: Links maintained Groq Whisper, Mistral Voxtral, and keyless Edge TTS Skills from the voice guide, documenting their environment requirements and direct `telegram.json` command-template path without a companion extension or local speech model.
|
|
29
|
+
|
|
5
30
|
## 0.40.0: Pi 0.84.4 Lifecycle Alignment
|
|
6
31
|
|
|
7
32
|
- `Pi Compatibility`: Requires `@earendil-works/pi-coding-agent`, `pi-agent-core`, and `pi-ai` 0.84.4 or newer, aligning the bridge and its validation runtime with native compaction-failure, mid-run compaction, message-finalization, and UI-prompt lifecycle contracts.
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
It is a **runtime adapter**, not a remote terminal. Start or supervise work in the Pi TUI, then continue from Telegram while away from the keyboard. Each Telegram destination follows a running Pi instance and sends prompts into that instance's currently active session; it is not permanently bound to one session file or session identity. The bridge preserves Pi session semantics instead of pretending Telegram is a PTY, shell, process launcher, or session browser. That boundary is the product: Telegram gets safe runtime handles, not raw terminal power.
|
|
10
10
|
|
|
11
|
-
Every completed intermediate commentary block from a Telegram-originated turn is delivered once as its own message before the existing final reply.
|
|
11
|
+
Every completed intermediate commentary block from a Telegram-originated turn is delivered once as its own message before the existing final reply. While Telegram is connected, local, autonomous, and unclassified extension follow-up work also projects visible checkpoints and the final answer to the authorized Telegram target once and in order, preserving assistant-authored `telegram_button` comments as interactive prompt buttons. This connected companion projection is always active rather than configurable. Neither path mirrors local prompts, thinking, tool traffic, token deltas, or stale-generation work. The separate `Activity` setting defaults to `verbose` so new installations discover collapsed provider-exposed thinking and tool evidence immediately; operators can narrow it to one class or choose `quiet`. See [Outbound](docs/outbound.md#public-assistant-output) and the [configuration reference](docs/public-api.md#configuration-api).
|
|
12
12
|
|
|
13
|
-
This repository is an actively maintained fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
|
|
13
|
+
This repository is an actively maintained standalone fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
@@ -218,11 +218,11 @@ Inbound files land under `<agent-dir>/tmp/telegram` and default to a 50 MiB limi
|
|
|
218
218
|
|
|
219
219
|
### Voice And Media
|
|
220
220
|
|
|
221
|
-
Voice notes, audio, images, PDFs, and other media can pass through configured inbound handlers, programmatic handlers, or registered STT providers. Outbound voice can use configured `outboundHandlers` or registered TTS providers; `pi-telegram` owns reply policy and Telegram transport, while providers own synthesis. Configure provider-neutral local/API pipelines and ordered fallbacks through [`telegram.json` command templates](./docs/voice.md#choose-an-integration-path). The default `manual` reply mode still supports intentional voice delivery through explicit `telegram_voice` actions; `mirror` and `always` add automatic voice policy. Explicit actions
|
|
221
|
+
Voice notes, audio, images, PDFs, and other media can pass through configured inbound handlers, programmatic handlers, or registered STT providers. Outbound voice can use configured `outboundHandlers` or registered TTS providers; `pi-telegram` owns reply policy and Telegram transport, while providers own synthesis. Configure provider-neutral local/API pipelines and ordered fallbacks through [`telegram.json` command templates](./docs/voice.md#choose-an-integration-path). The default `manual` reply mode still supports intentional voice delivery through explicit `telegram_voice` actions; `mirror` and `always` add automatic voice policy. Explicit actions prefer positional `{text}`, `{text|lang}`, or `{text|lang|rate}` cells and use JSON for multiline content, named fields, or escaping.
|
|
222
222
|
|
|
223
223
|
### Buttons And Callbacks
|
|
224
224
|
|
|
225
|
-
Assistant replies can include top-level hidden `telegram_button` comments
|
|
225
|
+
Assistant replies can include top-level hidden `telegram_button` comments containing a JSON object, adaptive JSON/CML matrix, or positional Compact Matrix Literal (CML). One adaptive matrix may mix named JSON objects with positional CML cells; separators are optional and one trailing comma is tolerated, including inside JSON objects. Top-level cells become full-width rows while nested rows group one or more buttons horizontally without an artificial parser-level width cap; generated surfaces default to five columns and use six to eight only for short position-bearing labels. CML uses `{value}`, `{label|prompt}`, or `{label|prompt|selected_style}` with `primary`, `success`, or `danger`; the optional style requires an explicit prompt. It trims atom boundaries, preserves non-structural text literally, and decodes only `\|`, `\}`, and `\\`. Prefer one matrix comment for multiple buttons. Buttons use `label` plus `prompt`, or the compact `value` key when both are identical. The bridge strips the comments from visible text, renders inline buttons, and routes callbacks back into Pi as queued prompts or extension-owned callback actions. Button-only replies receive the standard `☑️ **Choose an option:**` heading as automatic visible fallback text. Once a generated prompt button is accepted, only that exact button switches to its optional `selected_style` (`primary` blue by default, `success` green, or `danger` red) without altering its agent-authored label or emoji; every style still queues the selected prompt.
|
|
226
226
|
|
|
227
227
|
### Threaded Mode And Multi-Instance Bus
|
|
228
228
|
|
|
@@ -242,7 +242,7 @@ The Delivery API independently serializes concrete Telegram operations per targe
|
|
|
242
242
|
|
|
243
243
|
### Core assistant-output projection
|
|
244
244
|
|
|
245
|
-
Activity's built-in assistant-output projection uses the same normalized `assistant-segment` boundary exposed to public handlers. For `telegram` activity it always projects complete `intermediate` commentary to the immutable originating target, while final and terminal-partial segments remain with active-turn settlement. For `local`, `autonomous`, or unclassified extension follow-up activity,
|
|
245
|
+
Activity's built-in assistant-output projection uses the same normalized `assistant-segment` boundary exposed to public handlers. For `telegram` activity it always projects complete `intermediate` commentary to the immutable originating target, while final and terminal-partial segments remain with active-turn settlement. For `local`, `autonomous`, or unclassified extension follow-up activity, every completed public block—including intermediate commentary/checkpoints and the final block—is projected whenever this Pi instance retains authorized connected transport. This closes actor-follow-up delivery without reclassifying it as direct user input, and the connected companion contract has no projection opt-out. It never projects text token deltas, reasoning events, tool events or payloads, or empty text.
|
|
246
246
|
|
|
247
247
|
The projection does not delay Activity dispatch or Pi lifecycle. Its ordered admission tail deduplicates normalized event identity, while existing routing and outbound owners revalidate the immutable admission-time target, profile/token transport generation, direct leader epoch or follower registration generation, and session generation immediately before each send. Active-turn final delivery waits for admitted commentary inside its existing background task. A replacement or stale owner drops queued work rather than rerouting it, and an already-started non-idempotent Bot API mutation follows the normal `commit-unknown` no-replay contract.
|
|
248
248
|
|
|
@@ -257,9 +257,9 @@ The bridge maps Pi hooks as follows:
|
|
|
257
257
|
- `message_update.assistantMessageEvent`: normalize text/reasoning/provider boundaries.
|
|
258
258
|
- `tool_execution_start/update/end`: emit executed tool events.
|
|
259
259
|
- `session_before_compact` / `session_compact` / `session_compact_failed`: emit successful compaction boundaries, abandon failed or cancelled work immediately, and preserve activity identity across retry compaction. Mid-run threshold compaction stays between tool results and the next assistant response, while terminal assistant output awaiting transport keeps later notices behind its final reply. A missing or unrecognized reason maps to `unknown` rather than guessing.
|
|
260
|
-
- `ui_prompt_start` / `ui_prompt_end`: emit one coalesced waiting span for extension-owned local UI, pause Telegram typing while Pi waits for the operator, and resume
|
|
261
|
-
- `agent_end`: emit low-level run completion but keep identity alive for retry
|
|
262
|
-
- `agent_settled`: emit terminal settlement, flush pending terminal segments,
|
|
260
|
+
- `ui_prompt_start` / `ui_prompt_end`: emit one coalesced waiting span for extension-owned local UI, pause Telegram typing while Pi waits for the operator, and resume typing when the prompt closes if agent or compaction work remains unsettled.
|
|
261
|
+
- `agent_end`: emit low-level run completion but keep identity and connected work presence alive for retry, compaction, or follow-up work.
|
|
262
|
+
- `agent_settled`: emit terminal settlement, flush pending terminal segments, release activity identity, and end agent-owned connected work presence.
|
|
263
263
|
- `session_shutdown`: stop dispatch, clear pending normalization state, and invalidate delivery generation through the existing delivery lifecycle.
|
|
264
264
|
|
|
265
265
|
## Diagnostics
|
|
@@ -79,7 +79,7 @@ The repository uses a **Flat Domain DAG**:
|
|
|
79
79
|
- `keyboard`: shared inline-keyboard reply-markup shape only; feature domains own labels, callback data, and behavior.
|
|
80
80
|
- `preview` / `replies` / `rendering`: throttled native Rich Markdown draft delivery, native final reply delivery, reply parameters, transport-limit chunking, and remaining Telegram HTML rendering for bridge-owned UI/compatibility surfaces.
|
|
81
81
|
- `delivery`: public extension operational-view delivery, active-turn/instance/aggregate/authorized target policy, logical chunk handles, per-target ordering, runtime generation fencing, and the process-local runtime membrane. Its bridge adapter composes the established UI/compat reply renderer with narrow bus-aware Telegram API and ownership ports; it never exposes bot clients or Pi contexts.
|
|
82
|
-
- `activity`: public normalized Pi lifecycle registration, activity/source identity, assistant segment and reasoning normalization, executed-tool events, non-blocking per-handler queues, delivery contexts, compatibility adapters, and shutdown fencing. The same domain extends assistant-output observation for
|
|
82
|
+
- `activity`: public normalized Pi lifecycle registration, activity/source identity, assistant segment and reasoning normalization, executed-tool events, non-blocking per-handler queues, delivery contexts, compatibility adapters, and shutdown fencing. The same domain extends assistant-output observation for connected companion projection: eligible completed local/autonomous public segments retain source order and deduplicate event identity. `bindings` assembles observation, authority, sender, and failure-projection ports; routing owns exact delivery authority, outbound composes established transformations and reply delivery, and Bot API domains implement transport. No separate proactive state-machine domain exists.
|
|
83
83
|
- `outbound-markup`: top-level assistant action comment parsing, attribute parsing, voice reply planning, and preview/delivery stripping.
|
|
84
84
|
- `outbound`: outbound text transformations, voice/button artifact delivery, and generated callback actions.
|
|
85
85
|
- `generative-apps`: managed deterministic application identity, canonical installation and explicit replacement, content-addressed module loading, state timelines, cross-process transition serialization, bounded executable-plus-argv adaptation, `telegram_bind`, and pre-model-queue `app::method` invocation. It does not own Telegram transport, arbitrary shell execution, or the external application adapted by one Generative App.
|
|
@@ -301,7 +301,7 @@ Immediate controls:
|
|
|
301
301
|
- `/start` opens the main inline application menu.
|
|
302
302
|
- `/model`, `/thinking`, `/queue`, and `/settings` are hidden shortcuts to menu sections.
|
|
303
303
|
- `/compact` opens an inline confirmation dialog and then runs compaction when the bridge is idle.
|
|
304
|
-
- `/next` dispatches the next queued turn, aborting Pi first when needed.
|
|
304
|
+
- `/next` dispatches the next queued turn, aborting Pi first when needed. When an active Telegram turn is aborted, its single Pi-aligned informational notice replies to a pre-abort snapshot of that turn; otherwise the command message is the fallback target. Aborted pending assistant text is not projected as a second reply, while already completed intermediate output remains visible.
|
|
305
305
|
- `/abort` aborts active work while preserving queued items. Abort-history preservation is enabled only for Telegram-owned active turns; later local/non-Telegram agent starts clear stale abort-history mode so the next Telegram prompt appends instead of absorbing old queued turns as history.
|
|
306
306
|
- `/stop` aborts and clears waiting Telegram queue items.
|
|
307
307
|
|
|
@@ -323,7 +323,7 @@ Native typing during compaction follows connected-instance activity rather than
|
|
|
323
323
|
- Automatic/session compaction with an active Telegram turn reuses that turn's target.
|
|
324
324
|
- Automatic/session compaction without an active Telegram turn uses the connected instance's assigned target; an unconnected instance sends nothing.
|
|
325
325
|
- Thread-targeted typing is sent to the concrete thread and mirrored to `All` as the aggregate activity surface; completion, native failure, timeout, and shutdown stop the keyed loop.
|
|
326
|
-
- Pi `ui_prompt_start` pauses
|
|
326
|
+
- Pi `ui_prompt_start` pauses typing while an extension-owned local prompt waits for the operator; `ui_prompt_end` emits the matching Activity boundary and resumes typing whenever agent or compaction work remains unsettled.
|
|
327
327
|
|
|
328
328
|
At every connected instance `agent_start`, the lifecycle binding starts Telegram's native `…typing` indicator in that instance's assigned target, whether the run came from Telegram, the local TUI, or an autonomous continuation such as Grow Loop. Terminal `Active` remains Telegram-turn-specific; the native indicator answers the separate question of whether the instance is doing agent work. Each loop keeps one action in flight, while the leader API runtime coalesces identical chat/thread/action calls across local and follower traffic for two seconds; expired gates prune opportunistically and at most 256 currently active keys are retained. A Telegram 429 response opens the exact action's shared `retry_after` suppression window without scheduling delayed retries or projecting expected activity throttling as a terminal status error. Assistant message start/update hooks still re-arm it during Telegram-owned turns so transient provider/model errors do not leave a continuing run without activity feedback, and agent/session completion stops it.
|
|
329
329
|
|
|
@@ -336,7 +336,7 @@ Assistant delivery guarantees:
|
|
|
336
336
|
- Model-authored Markdown is the source of truth; the bridge does not pre-render assistant Markdown to HTML unless the operator selects `assistant.rendering: "html"` for compatibility.
|
|
337
337
|
- Before native Rich Markdown delivery, the bridge normalizes known Bot-API-fragile source forms without changing visible meaning, including space-after-marker blockquotes and dollar-prefixed ticker atoms that Telegram may otherwise treat as unterminated math.
|
|
338
338
|
- Prompt context blocks use compact metadata (`[tag|key:value]`) as the stable inbound contract. `[telegram...]` names the current surface only: owner/current turns use `[telegram]` or `[telegram|thread:<name>]`; guest-mode turns use `[telegram|guest:<group-title-or-peer-username-or-id>]`. In a private Guest Mode turn the paired owner's `from` identity is never the guest: the remote private-chat identity wins, then non-owner caller metadata, with a non-bot replied peer available only as a final identity fallback when stronger conversation evidence is absent; username falls back to the remote display name and numeric id. Reply attribution still belongs independently in `[reply|from:...]`, and a replied bot can never define or replace the current `[telegram|guest:...]` location identity. Source authors for quoted/forwarded material and their files are carried by `[reply|from:<username-or-id>]`, `[forward|from:<username-or-id>]`, and `[attachments|from:<username-or-id>]`, while plain `[attachments]` remains current-turn attachments and is ordered before reply/forward/source context. Media embedded in inbound Telegram `rich_message` blocks is downloaded like ordinary message media and stays attached to its forward-source block instead of being mislabeled as current-user material.
|
|
339
|
-
- Quoted rich replies use Telegram `rich_message` blocks as the prompt-context source when available, so `[reply]` context receives rendered plain text instead of raw `InputRichMessage.markdown` fallback text.
|
|
339
|
+
- Quoted rich replies use Telegram `rich_message` blocks as the prompt-context source when available, so `[reply]` context receives rendered plain text instead of raw `InputRichMessage.markdown` fallback text. Replied media runs through the same inbound handlers and voice transcription providers as current-message media, with provenance-scoped `[outputs|from:…]` appended inside the reply block.
|
|
340
340
|
- Long native Markdown replies are split only at Telegram Rich Message transport limits; oversized fenced code, display-math, and fully wrapped inline-formatting blocks are rewrapped per chunk so persisted Rich Markdown chunks remain structurally valid.
|
|
341
341
|
- When Draft previews are enabled, streaming previews pass structurally closed assistant Markdown prefixes through to `sendRichMessageDraft` with ownership checks, voice suppression, and serialized flushes. Unclosed inline spans, links, fenced code, comments, and display-math blocks are held back until a safe boundary exists. Draft failures are recorded and the failing frame is skipped instead of degrading to raw plain-message previews, because partial Markdown can be invalid while the final message remains valid.
|
|
342
342
|
- Preview flushes are serialized so older edits cannot race newer drafts; final delivery waits for active draft flushes and does not perform a post-final draft-clear call.
|
|
@@ -352,10 +352,10 @@ Final delivery attaches reply metadata only where requested. Reply parameters ap
|
|
|
352
352
|
|
|
353
353
|
Outbound files staged during an active Telegram turn are delivered after that turn completes. They use `telegram_attach`, are checked atomically per tool call, and use configurable size limits before photo/document upload. When no Telegram turn is active, `telegram_attach` sends files immediately to the paired/default chat, an assigned follower thread, or an explicit `chat_id` plus optional `thread_id`; `telegram_message` provides direct local/TUI Markdown text delivery for explicit user requests and runs the same `telegram_button` markup planner so buttons attach to that text message. Direct local/TUI delivery is singleton-controlled: classic mode requires this Pi instance to own `/telegram-connect`, while Threaded Mode followers must be registered and route through the leader-owned transport. Already accepted active-turn reply/attachment delivery remains session-local.
|
|
354
354
|
|
|
355
|
-
Assistant-authored final-message actions use hidden top-level comments
|
|
355
|
+
Assistant-authored final-message actions use hidden top-level comments:
|
|
356
356
|
|
|
357
|
-
- `telegram_voice` accepts
|
|
358
|
-
- `telegram_button` accepts a JSON object, adaptive JSON/CML matrix, positional Compact Matrix Literal
|
|
357
|
+
- `telegram_voice` accepts one positional compact action cell or JSON object and creates one voice artifact through configured outbound handlers, programmatic voice handlers, or registered synthesis providers.
|
|
358
|
+
- `telegram_button` accepts a JSON object, adaptive JSON/CML matrix, or positional Compact Matrix Literal. Named JSON objects and positional cells may coexist, with commas optional only between completed matrix or row elements. Each top-level cell creates one full-width row, while a nested row groups buttons horizontally without an artificial parser-width cap; every callback enqueues its configured prompt text as a normal Telegram prompt turn. The JSON-first grammar, positional trim/escape rules, atomic rejection, and renderer-owned width policy are specified in [Adaptive Button Literal](./compact-matrix-literal.md).
|
|
359
359
|
|
|
360
360
|
Preview delivery strips top-level action comments before streaming draft Markdown. Comments inside code fences, quotes, lists, or indented examples stay literal.
|
|
361
361
|
|
|
@@ -390,13 +390,13 @@ The bridge does not mirror arbitrary `ctx.ui.confirm/input/select/custom` prompt
|
|
|
390
390
|
|
|
391
391
|
## Diagnostics And Operational Behavior
|
|
392
392
|
|
|
393
|
-
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. 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.
|
|
393
|
+
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.
|
|
394
394
|
|
|
395
395
|
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.
|
|
396
396
|
|
|
397
397
|
`/telegram-status` records grouped diagnostics for transport/API, polling/update, prompt dispatch, controls, typing, compaction, setup, session lifecycle, attachment queue/delivery, and recent redacted runtime events. Polling diagnostics expose the exact phase, phase start, current update, last successful `getUpdates` response, and stop reason; outbound success never substitutes for inbound progress. Expected preview noise such as unchanged edit responses is filtered out. The compact TUI status renders only `error`; detailed failure text remains in diagnostics and profile-scoped logs instead of expanding the status line.
|
|
398
398
|
|
|
399
|
-
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.
|
|
399
|
+
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.
|
|
400
400
|
|
|
401
401
|
`assistant.activity` is an independent bridge-owned projection over normalized Activity events. Each process reloads the shared file-backed setting at `agent-start` before activity admission, so multi-instance mode cannot continue projecting a stale broader process-local selection. Omitted values resolve to `verbose`, while invalid values fail closed to `quiet`; `thinking` and `tools` select one technical class, while `verbose` enables both. Provider-exposed thinking uses persistent ordinary HTML containing only a standard expandable blockquote with a bounded redacted latest-text window and inline Markdown rendered as Telegram HTML. Completed executed tools use native Rich Messages: each closed `<Tool>: <status>` root details node renders snake-case names as title words while preserving an uppercase two- or three-letter repeated prefix per word, then the native disclosure chevron reveals an open-by-default `arguments` child plus closed retained `update N` and `result`/`error` child details with lowercase monospaced, marker-free summaries and JSON pre blocks; known-safe Rich rejections fall back to the previous HTML disclosure. The projection captures the exact target and transport stamp at activity admission, serializes updates, preserves tool-start order, closes coalescing across assistant/thinking boundaries, bounds retained text/update memory plus edit frames and message/tool size, disables previews and HTTP(S) auto-link recognition inside technical evidence, and never replays a possibly committed send. Session generations own independent queues, so replacement drops queued old work without waiting on an old call. Both proactive prose and active-turn final delivery wait for the admitted activity queue inside their extension-owned delivery tasks, preserving technical-before-semantic ordering without delaying Pi lifecycle completion. Settlement, replacement, disconnect, failure, or stale authority clears only local ownership; already-sent activity messages remain in chat.
|
|
402
402
|
|
|
@@ -419,7 +419,7 @@ This is limited to Telegram-owned runs. If Pi is busy with non-Telegram work, th
|
|
|
419
419
|
|
|
420
420
|
Non-critical timers are `unref()`ed so print/headless processes are not kept alive only by Telegram housekeeping. This includes typing keepalive intervals, bounded typing-idle waits, deferred queue dispatch, media/text-group debounce windows, preview flush timers, and polling retry sleeps. Polling retry sleep is abort-aware, so shutdown does not wait for the normal retry delay after a polling error.
|
|
421
421
|
|
|
422
|
-
Non-interactive `pi -p` runs must remain passive unless Pi provides a live Telegram session lifecycle. Loading the extension with `telegram.json
|
|
422
|
+
Non-interactive `pi -p` runs must remain passive unless Pi provides a live Telegram session lifecycle. Loading the extension with `telegram.json` or existing lock state must not by itself keep the print-mode process alive or let a non-owner send companion Telegram output.
|
|
423
423
|
|
|
424
424
|
## Related
|
|
425
425
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Adaptive Button Literal
|
|
2
2
|
|
|
3
|
-
> Status: Portable CML v3 standard
|
|
3
|
+
> Status: Portable CML v3 button standard; `pi-telegram` also reuses its positional-cell mechanics for compact voice actions.
|
|
4
4
|
|
|
5
5
|
Adaptive Button Literal is one bounded-depth matrix grammar over a shared button AST. It accepts strict JSON button objects, positional Compact Matrix Literal (CML) cells, or both in the same matrix and row. Commas between completed matrix or row elements are optional, so producers can progressively compress representation without changing runtime meaning.
|
|
6
6
|
|
|
@@ -85,9 +85,9 @@ atom-unit := ordinary | "\|" | "\}" | "\\"
|
|
|
85
85
|
ws := *(SP | HTAB | CR | LF)
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
`boundary` occurs
|
|
88
|
+
`boundary` occurs after one complete element. It may contain one comma or no comma; one trailing comma before a closing row or matrix delimiter is also tolerated. Element delimiters keep empty adjacency unambiguous. Leading and repeated commas remain invalid.
|
|
89
89
|
|
|
90
|
-
A `json-object` is one complete
|
|
90
|
+
A `json-object` is one complete JSON object. Strict JSON is attempted first; a bounded recovery removes commas immediately before `}` or `]` outside strings and retries. Property names, strings, escaping, nested values, and all other internals remain strict; missing property commas are not invented.
|
|
91
91
|
|
|
92
92
|
Rows cannot contain rows. The grammar never recurses beyond one row inside the top-level matrix.
|
|
93
93
|
|
|
@@ -111,29 +111,31 @@ A conforming parser:
|
|
|
111
111
|
|
|
112
112
|
1. Attempts strict JSON first for sources beginning with `{` or `[`. Successful JSON is validated only against the existing button matrix schema and never reinterpreted.
|
|
113
113
|
2. If strict JSON parsing fails, parses the original source with the adaptive grammar.
|
|
114
|
-
3. Tries one complete strict JSON object at each cell boundary before positional interpretation.
|
|
115
|
-
4.
|
|
116
|
-
5.
|
|
114
|
+
3. Tries one complete strict JSON object, then bounded trailing-comma recovery, at each cell boundary before positional interpretation.
|
|
115
|
+
4. Keeps JSON-shaped named objects on the JSON path when validation fails instead of exposing their source as positional text.
|
|
116
|
+
5. Accepts at most one comma between elements or immediately before a closing row or matrix delimiter, while rejecting leading, repeated, or property-level omitted commas.
|
|
117
117
|
6. Rejects empty atoms, matrices, rows, and nesting deeper than one row.
|
|
118
118
|
7. Decodes only `\|`, `\}`, and `\\` in positional cells.
|
|
119
|
-
8.
|
|
120
|
-
9. Returns no partial rows or cells
|
|
121
|
-
10. Runs
|
|
119
|
+
8. Extracts the first complete valid payload from a tolerant comment envelope and ignores unrelated text or isolated unmatched matrix brackets around it.
|
|
120
|
+
9. Returns no partial rows or cells from a balanced malformed candidate.
|
|
121
|
+
10. Runs over host-bounded payloads with fixed grammar depth.
|
|
122
122
|
|
|
123
123
|
Malformed JSON-looking input receives no generic recovery. It is accepted only if it independently forms a complete valid adaptive literal.
|
|
124
124
|
|
|
125
|
-
## Telegram
|
|
125
|
+
## Telegram Profiles
|
|
126
126
|
|
|
127
|
-
For `telegram_button`
|
|
127
|
+
For `telegram_button` comments:
|
|
128
128
|
|
|
129
129
|
- JSON `value` keeps its existing label/prompt fallback semantics.
|
|
130
|
-
- Positional `{value}` is equivalent to JSON `{"value":"value"}
|
|
130
|
+
- Positional `{value}` is equivalent to JSON `{"value":"value"}`; a lone JSON `label` or `prompt` has the same both-fields shorthand semantics.
|
|
131
131
|
- Positional `{label|prompt}` is equivalent to JSON `{"label":"label","prompt":"prompt"}`.
|
|
132
132
|
- Positional `{label|prompt|selected_style}` is equivalent to the corresponding three-field JSON object.
|
|
133
133
|
- Top-level cells become full-width rows.
|
|
134
134
|
- Nested rows become horizontal keyboard rows.
|
|
135
135
|
- Invalid payloads are stripped with their recognized action comment and register no callbacks.
|
|
136
136
|
|
|
137
|
+
For `telegram_voice`, one positional cell maps `{text}`, `{text|lang}`, or `{text|lang|rate}` to one voice artifact. JSON object cells remain available for named fields, escaping, and multiline text. Voice comments do not accept matrix or row composition.
|
|
138
|
+
|
|
137
139
|
Example:
|
|
138
140
|
|
|
139
141
|
```html
|
|
@@ -161,12 +163,12 @@ Accepted classes include:
|
|
|
161
163
|
Rejected classes include:
|
|
162
164
|
|
|
163
165
|
- Empty payloads, matrices, rows, labels, prompts, or style atoms.
|
|
164
|
-
- Leading
|
|
166
|
+
- Leading or repeated element commas.
|
|
165
167
|
- Missing commas between properties inside a JSON object.
|
|
166
168
|
- Deeper row nesting.
|
|
167
169
|
- Missing, crossed, or mismatched delimiters.
|
|
168
170
|
- A third positional separator, unknown style, unknown escape, or trailing backslash.
|
|
169
|
-
- Internal control characters
|
|
171
|
+
- Internal control characters.
|
|
170
172
|
- Valid JSON that fails the existing JSON action schema.
|
|
171
173
|
|
|
172
174
|
Every rejected case proves zero callback registration.
|
|
@@ -67,7 +67,7 @@ Built-in placeholders for text handlers:
|
|
|
67
67
|
|
|
68
68
|
## Media/File Handlers
|
|
69
69
|
|
|
70
|
-
Media/file handlers keep the legacy attachment-handler behavior: downloaded files are matched by `mime`, `type`, or `match`, then each file runs the first successful matching handler. Downloaded files with `mime: "text/plain"` or any `text/*` MIME type have a built-in fail-open handler that reads UTF-8 content into `[outputs]` when no configured handler produced output. Composition is useful for pipelines such as voice transcription followed by machine translation, so the agent receives translated `[outputs]` instead of the raw STT language.
|
|
70
|
+
Media/file handlers keep the legacy attachment-handler behavior: downloaded files are matched by `mime`, `type`, or `match`, then each file runs the first successful matching handler. Attachments from a replied-to message use the same handler/provider pipeline independently; their `[outputs|from:…]` section remains inside that `[reply]` context after its attachment list instead of being attributed to the current message. Downloaded files with `mime: "text/plain"` or any `text/*` MIME type have a built-in fail-open handler that reads UTF-8 content into `[outputs]` when no configured handler produced output. Composition is useful for pipelines such as voice transcription followed by machine translation, so the agent receives translated `[outputs]` instead of the raw STT language.
|
|
71
71
|
|
|
72
72
|
Built-in placeholders for media/file handlers:
|
|
73
73
|
|
|
@@ -8,9 +8,9 @@ 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.
|
|
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.
|
|
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
15
|
Projected blocks use `assistant.rendering` independently of voice policy. Rich mode sends native Rich Markdown and HTML mode keeps the established HTML renderer; assistant-authored `telegram_button` comments are planned into prompt buttons before either renderer runs, while projection does not synthesize voice or attach queued files merely because Rich rendering is active. Ordered admission revalidates the exact target, profile/token transport generation, leader epoch or follower registration generation, and session generation before each send. Active-turn final delivery waits for admitted commentary inside its existing background delivery task, preserving commentary-before-final order without blocking Pi lifecycle completion. A `commit-unknown` outcome never permits replay.
|
|
16
16
|
|
|
@@ -129,43 +129,34 @@ Synthesis providers receive the extracted `telegram_voice` text plus optional `l
|
|
|
129
129
|
|
|
130
130
|
## Voice Markup
|
|
131
131
|
|
|
132
|
-
Assistant replies can include hidden voice actions
|
|
132
|
+
Assistant replies can include hidden voice actions as a positional compact cell or JSON object:
|
|
133
133
|
|
|
134
134
|
```md
|
|
135
135
|
Full text answer stays here.
|
|
136
136
|
|
|
137
|
-
<!-- telegram_voice {
|
|
137
|
+
<!-- telegram_voice {Short spoken companion summary.|ru|+30%} -->
|
|
138
138
|
|
|
139
|
-
<!-- telegram_voice text
|
|
139
|
+
<!-- telegram_voice {"text":"First line.\nSecond line.","lang":"ru"} -->
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
The bridge strips the comment from Telegram text. On `agent_end`, it maps each `telegram_voice` action to a provider call, generates one file per action, and sends each file as an independent Telegram-native voice message.
|
|
142
|
+
The bridge strips the comment from Telegram text. On `agent_end`, it maps each `telegram_voice` action to a provider call, generates one file per action, and sends each file as an independent Telegram-native voice message. Prefer `{text}`, `{text|lang}`, or `{text|lang|rate}`. Use JSON when multiline content, named fields, or escaping earns the extra syntax; equivalent JSON `text` or `value` supplies the spoken payload, with explicit `text` taking precedence. Each comment creates one voice artifact, so voice cells do not accept button-style matrix composition. The opening marker must start at column zero on a top-level line outside fenced code, quotes, lists, and indented examples; otherwise it remains literal Markdown.
|
|
143
143
|
|
|
144
144
|
## Buttons Markup
|
|
145
145
|
|
|
146
|
-
Assistant replies can include one or many button actions through
|
|
146
|
+
Assistant replies can include one or many button actions through a top-level `telegram_button` comment:
|
|
147
147
|
|
|
148
148
|
```md
|
|
149
149
|
I can continue.
|
|
150
150
|
|
|
151
|
-
<!-- telegram_button {"label":"Continue","prompt":"Continue with the current plan.","selected_style":"primary"} -->
|
|
152
|
-
|
|
153
|
-
<!-- telegram_button label="Show risks" prompt="List the main risks first." selected_style="danger" -->
|
|
154
|
-
|
|
155
|
-
<!-- telegram_button {"value":"Done"} -->
|
|
156
|
-
|
|
157
|
-
<!-- telegram_button [{"label":"⬆️ Up","prompt":"/"},[{"value":"⬅️ Previous"},{"value":"➡️ Next"}],{"label":"📁 etc","prompt":"/etc"}] -->
|
|
158
|
-
|
|
159
|
-
<!-- telegram_buttons [[{"value":"Approve"},{"value":"Reject"}]] -->
|
|
160
|
-
|
|
161
151
|
<!-- telegram_button [{⬆️ Up|/}[{⬅️|page-1}{➡️|page-3}]{📁 etc|/etc}] -->
|
|
152
|
+
|
|
153
|
+
<!-- telegram_button {"label":"▶️ Continue","prompt":"Continue with the current plan.","selected_style":"primary"} -->
|
|
162
154
|
```
|
|
163
155
|
|
|
164
156
|
Rules:
|
|
165
157
|
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
- Use `label` plus `prompt`, or the compact `value` key when both strings are identical. Explicit `label` or `prompt` takes precedence over its `value` fallback. Use JSON with `\n` escapes for multiline prompts.
|
|
158
|
+
- The payload may be a JSON object, adaptive JSON/CML matrix, or positional [Compact Matrix Literal](./compact-matrix-literal.md). Named JSON objects and positional cells may coexist in one matrix or row. Commas are optional between completed elements, and one trailing comma before a closing delimiter is tolerated; JSON object validation likewise tolerates trailing commas but does not invent missing values, property names, or internal separators. CML uses `{value}`, `{label|prompt}`, or `{label|prompt|selected_style}`; the optional third atom requires a prompt and accepts only `primary`, `success`, or `danger`. It trims every atom, preserves non-structural printable text, and decodes only `\|`, `\}`, and `\\`.
|
|
159
|
+
- Use `label` plus `prompt`, or the compact `value` key when both strings are identical. If only `label`, only `prompt`, or one-field `{value}` is present, that string supplies both visible label and queued prompt. An explicit counterpart takes precedence over `value`. Use JSON with `\n` escapes for multiline prompts.
|
|
169
160
|
- The opening marker must start at column zero on a top-level line outside fenced code, quotes, lists, and indented examples; otherwise it remains literal Markdown.
|
|
170
161
|
- Prefer one matrix comment for multiple buttons. Each top-level JSON object or CML cell becomes one full-width inline-keyboard row in source order; a nested row groups one or more buttons horizontally. The parser imposes no artificial per-row width cap; empty rows, malformed cells, unknown/trailing CML escapes, a third unescaped CML separator, empty atoms, unknown selected styles, and deeper nesting are rejected atomically. Generated surfaces default to five columns and expand to six through eight only for short position-bearing labels. Repeated singular comments remain valid.
|
|
171
162
|
- Button actions are stored in memory with short `callback_data`; Telegram never sees the full prompt in the button payload.
|
|
@@ -184,8 +175,8 @@ The extension injects prompt guidance by context:
|
|
|
184
175
|
- For ordinary local/TUI prompts, the compact routing note points to the bundled `telegram-bridge` Skill and forbids Telegram use unless explicitly requested.
|
|
185
176
|
- For Telegram-originated turns, the compact note routes the agent to `telegram-bridge`, which owns voice/button/direct-delivery/Threaded Mode/formatting/debug guidance.
|
|
186
177
|
- For Telegram-originated turns, write the full technical answer as normal Markdown.
|
|
187
|
-
- Add `telegram_voice` with
|
|
188
|
-
- Add `telegram_button` with a JSON object, JSON matrix, or
|
|
178
|
+
- Add `telegram_voice` with positional CML by default or JSON when multiline content, named fields, or escaping requires it. A companion summary is optional, no specific summary format is required.
|
|
179
|
+
- Add `telegram_button` with a JSON object, JSON matrix, or positional CML. Prefer one matrix for multiple controls. Use `label` plus `prompt`, or `value` when they are identical; `selected_style` is optional. A button-only reply may omit parent text because the bridge supplies `☑️ **Choose an option:**` automatically.
|
|
189
180
|
- For ordinary Telegram-turn replies, do not call transport tools for voice or buttons; the bridge owns delivery, while registered voice synthesis providers own TTS and OGG/Opus conversion. For explicit local/TUI direct sends, `telegram_message` may include top-level `telegram_button` comments in its Markdown text because those buttons are attached to that text message.
|
|
190
181
|
- Prefer meaningful visible parent text when it adds context; for a button-only answer, rely on the bridge's automatic `☑️ **Choose an option:**` fallback rather than manufacturing duplicate text.
|
|
191
182
|
|