@llblab/pi-telegram 0.9.5 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -2
- package/BACKLOG.md +2 -2
- package/CHANGELOG.md +20 -0
- package/README.md +101 -116
- package/docs/architecture.md +1 -1
- package/docs/extension-sections.md +1 -1
- package/index.ts +3 -0
- package/lib/api.ts +62 -19
- package/lib/pi.ts +1 -1
- package/lib/polling.ts +1 -0
- package/lib/preview.ts +12 -2
- package/lib/queue.ts +25 -0
- package/lib/routing.ts +31 -0
- package/lib/runtime.ts +2 -1
- package/lib/updates.ts +97 -3
- package/package.json +11 -8
package/AGENTS.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## 1. Concept
|
|
13
13
|
|
|
14
|
-
`pi-telegram` is a
|
|
14
|
+
`pi-telegram` is a Telegram Runtime Adapter for π: a session-local operator console that turns a private Telegram DM into a runtime surface for prompt intake, streaming previews, queue management, model/thinking/settings controls, inbound/outbound handler pipelines, voice/buttons, artifacts, and extension callback interop. Treat it as a Telegram membrane around π, not a narrow message pipe.
|
|
15
15
|
|
|
16
16
|
## 2. Identity & Naming Contract
|
|
17
17
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
- `/tests/*.test.ts`: Domain-mirrored regression suites that follow the same flat naming as `/lib`
|
|
29
29
|
- `/docs/README.md`: Documentation index for technical project docs
|
|
30
30
|
- `/docs/architecture.md`: Runtime and subsystem overview for the bridge
|
|
31
|
-
- `/README.md`: User-facing project entry point
|
|
31
|
+
- `/README.md`: User-facing project entry point. Keep its rhythm as install → connect → use → core features → docs, with vivid examples that explain the runtime adapter/operator-console model without duplicating full docs.
|
|
32
32
|
- `/AGENTS.md`: Durable engineering and runtime conventions
|
|
33
33
|
- `/BACKLOG.md`: Canonical open work
|
|
34
34
|
- `/CHANGELOG.md`: Completed delivery history
|
|
@@ -122,6 +122,8 @@ The canonical detailed ownership map lives in [`docs/architecture.md`](./docs/ar
|
|
|
122
122
|
- When Telegram-visible behavior changes, sync `README.md` and the relevant `/docs` entry in the same pass
|
|
123
123
|
- When durable runtime constraints or repeat bug patterns emerge, record them here instead of burying them in changelog prose
|
|
124
124
|
- When fork identity changes, keep `README.md`, package metadata, and docs aligned so the published package does not point back at stale upstream coordinates
|
|
125
|
+
- README positioning should emphasize `/start` as the primary Telegram operator menu and keep reaction shortcuts secondary. Reactions are useful queue affordances, but menu controls are the core CLI-to-Telegram bridge.
|
|
126
|
+
- Document configuration knobs without UI in the root README when they affect bootstrap, networking, or transport limits; currently this includes token env bootstrap, Node env proxy mode, and inbound/outbound size limits.
|
|
125
127
|
- Work only inside this repository during development tasks; updating the installed Pi extension checkout is a separate manual operator step, not part of normal in-repo implementation work
|
|
126
128
|
|
|
127
129
|
## 8. Integration Protocols
|
package/BACKLOG.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Open Work
|
|
4
4
|
|
|
5
|
-
- Implement Telegram Extension Sections Platform for the 0.10.0 line.
|
|
5
|
+
- [ ] Implement Telegram Extension Sections Platform for the 0.10.0 line.
|
|
6
6
|
- Exit: Runtime registry, main-menu integration, `section:` callback routing, safe section context ports, diagnostics, docs, and at least one small demo/fixture prove ordinary pi extensions can add Telegram menu sections without owning a second poller.
|
|
7
|
-
- Explore always-available outbound Telegram tools for queued artifacts and controls.
|
|
7
|
+
- [ ] Explore always-available outbound Telegram tools for queued artifacts and controls.
|
|
8
8
|
- Priority: Low.
|
|
9
9
|
- Idea: Provide tools such as `telegram_attach_file` and `telegram_attach_button` that can be called outside an active Telegram turn, using the paired chat/session as the delivery target when safe.
|
|
10
10
|
- Exit: Design note defines active-turn versus ambient delivery semantics, safety constraints, failure modes, and whether the current `telegram_attach` contract should stay turn-scoped or gain an ambient companion.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.7: Bot API 10.0 Alignment
|
|
4
|
+
|
|
5
|
+
- `[Dependencies]` Migrated peer dependencies and imports from `@mariozechner/*` to `@earendil-works/*` (`pi-agent-core`, `pi-ai`, `pi-coding-agent`). Impact: the extension now tracks the new `@earendil-works` package scope; transitive `@mariozechner` packages remain in the lockfile until their upstreams migrate.
|
|
6
|
+
- `[Package]` Added `engines: { "node": ">=22.0.0" }` to document the supported Node expectation while keeping dev dependencies on `latest` for early-stage iteration. Impact: users know the minimum Node version without constraining the development dependency matrix prematurely.
|
|
7
|
+
- `[Polling]` Added `"guest_message"` to `TELEGRAM_ALLOWED_UPDATES` so the bot receives guest-mode updates. Impact: without this, guest mentions are silently ignored by Telegram.
|
|
8
|
+
- `[Telegram API]` Updated `sendMessageDraft` wrapper for Bot API 10.0 semantics: removed the empty-text guard, made `text` optional, and added optional `parse_mode`, `entities`, and `message_thread_id` parameters. Impact: preview can now show a "Thinking…" placeholder with empty text, and callers can pass rich formatting through `parse_mode` or `entities`.
|
|
9
|
+
- `[Telegram API]` Added `answerGuestQuery` to the API runtime for Bot API 10.0 Guest Mode support. Impact: callers can reply to guest queries in chats where the bot is not a member. Uses `InlineQueryResultArticle` as the result payload per Bot API 10.0 contract.
|
|
10
|
+
- `[Updates]` Extended inbound update routing to recognize `guest_message` updates. Added `getAuthorizedTelegramGuestMessage`, guest flow action, execution plan, runtime handler, and prompt enqueue support. Unauthorized guest queries receive an "Access denied." reply via `answerGuestQuery`. Guest turns customize the agent-end delivery to use `answerGuestQuery` instead of normal reply transport. Impact: the bridge can now receive and route guest-mode mentions in group chats while preserving the existing private-message authorization model.
|
|
11
|
+
- `[Runtime]` Added typing-loop skip for guest turns (`chatId === 0`) to avoid spurious `sendChatAction` errors in the status bar.
|
|
12
|
+
- `[Tests]` Added regression tests for empty-text draft delivery, undefined-text draft delivery, rich preview with `parse_mode` and `entities`, guest query answers, guest extraction, guest flow classification, guest execution plan, guest deny reply, and guest message routing through the runtime.
|
|
13
|
+
- `[Preview]` Updated `sendDraft` interface in `lib/preview.ts` to accept optional text and formatting options, keeping the preview pipeline aligned with the new API wrapper.
|
|
14
|
+
|
|
15
|
+
## 0.9.6: Runtime Adapter Positioning
|
|
16
|
+
|
|
17
|
+
- `[Package]` Bumped package metadata to `0.9.6` and repositioned the package description from "Better Telegram DM bridge extension for π" to "Telegram Runtime Adapter for π". Impact: package metadata now reflects the runtime adapter/operator-console role rather than a narrow pipe metaphor.
|
|
18
|
+
- `[Telegram API]` Introduced `TELEGRAM_API_BASE` for the Bot API endpoint and documented native HTTP/HTTPS proxy operation through `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, and explicit `NODE_USE_ENV_PROXY=1` / `--use-env-proxy` enablement. Impact: users behind corporate proxies, local HTTP tunnels, or restricted networks get a zero-runtime-dependency proxy path without replacing native `fetch`; SOCKS5 remains outside the zero-dependency core.
|
|
19
|
+
- `[Dependencies]` Refreshed the lockfile transitive dependency set so `npm audit` clears current `fast-uri` and `fast-xml-builder` advisories inherited through development peer installs. Impact: the full `npm run validate` pipeline passes without changing runtime dependencies.
|
|
20
|
+
- `[README]` Restructured the user entrypoint around install → connect → use → core features → docs, then consolidated examples, terminology, proxy setup, `PI_CODING_AGENT_DIR`, and other environment-only configuration around the runtime-adapter/operator-console model. Impact: first-time users get a clearer path from installation to operation, while vivid examples and non-UI runtime knobs stay discoverable.
|
|
21
|
+
- `[Context]` Promoted the runtime-adapter/operator-console README rhythm, `/start` menu emphasis, and environment-only configuration rule into `AGENTS.md`. Impact: future documentation edits preserve the same positioning and env-knob coverage instead of drifting back toward a narrow bridge metaphor.
|
|
22
|
+
|
|
3
23
|
## 0.9.5: Telegram Delivery Resilience Hotfix
|
|
4
24
|
|
|
5
25
|
- `[Preview Delivery]` Preview flush failures from Telegram transport errors such as `fetch failed` / `ECONNRESET` are now caught and recorded as runtime diagnostics instead of escaping from the preview pipeline. Impact: transient Telegram connectivity failures no longer crash the extension during streamed preview edits.
|
package/README.md
CHANGED
|
@@ -2,24 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Telegram Runtime Adapter for π.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`pi-telegram` turns a private Telegram DM into a session-local operator console for π. It admits work, preserves context, streams readable replies, keeps busy sessions usable through queues, lets other extensions share one bot, and turns assistant-authored intent into native Telegram artifacts.
|
|
8
8
|
|
|
9
|
-
- [
|
|
10
|
-
- [Open Backlog](./BACKLOG.md)
|
|
11
|
-
- [Changelog](./CHANGELOG.md)
|
|
12
|
-
- [Documentation](./docs/README.md)
|
|
13
|
-
|
|
14
|
-
## Key Features
|
|
15
|
-
|
|
16
|
-
- **Telegram Controls**: `/start` opens the inline application menu with command help, available π prompt templates, status rows, model, thinking, and queue sections; the Status row reports `compacting` during Telegram `/compact`; `/stop`, `/abort`, `/next`, and `/continue` provide queue-clear, queue-preserve, force-next, and queued-resume semantics respectively; model-switch continuation turns still use the control lane when a restart needs to resume safely.
|
|
17
|
-
- **Interactive UI**: Manage your session directly from Telegram. Inline buttons expose an application menu for switching models, choosing model pages from the pagination indicator, adjusting reasoning (thinking) levels, and inspecting or mutating the waiting queue; model scope/pagination controls stay at the top of the model menu, the Queue button shows the current item count, and command emoji are reused on matching controls such as model and thinking.
|
|
18
|
-
- **In-flight Model Switching**: Change the active model mid-generation. The agent gracefully pauses, applies the new model, and restarts its response without losing context.
|
|
19
|
-
- **Smart Message Queue**: Messages sent while the agent is busy are queued and previewed in the π status bar, and queued turns can be reprioritized or removed with Telegram reactions or the queue section of the inline application menu.
|
|
20
|
-
- **Mobile-Optimized Rendering**: Tables and lists are formatted for narrow screens, table padding accounts for emoji grapheme and wide Unicode display width, and Telegram-originated runs prompt the assistant to prefer narrow table columns for phone readability. Markdown is correctly parsed and split to fit Telegram's limits without breaking HTML structures or code blocks, block spacing stays faithful to the original Markdown with readable heading separation, supported absolute links stay clickable, and unsupported link forms degrade safely.
|
|
21
|
-
- **File Handling & Attachments**: Send images and files to the agent, transcribe or transform inbound text/media with configured inbound handlers, or ask π to generate and return artifacts. Inbound downloads and outbound attachments are size-limited by default, and outbound files are delivered automatically via the `telegram_attach` tool.
|
|
22
|
-
- **Streaming Responses**: Closed Markdown blocks stream back as rich Telegram HTML while π is generating, and the still-growing tail stays readable until the final fully rendered reply lands.
|
|
9
|
+
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.
|
|
23
10
|
|
|
24
11
|
## Install
|
|
25
12
|
|
|
@@ -35,22 +22,16 @@ From git:
|
|
|
35
22
|
pi install git:github.com/llblab/pi-telegram
|
|
36
23
|
```
|
|
37
24
|
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
### Configuration Philosophy
|
|
41
|
-
|
|
42
|
-
The extension intentionally keeps rich visual/TUI configuration minimal for now. Rich setup screens may arrive later, but they are not the main configuration surface yet.
|
|
25
|
+
## Connect
|
|
43
26
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
### 1. Telegram Bot
|
|
27
|
+
### 1. Create a Telegram bot
|
|
47
28
|
|
|
48
29
|
1. Open [@BotFather](https://t.me/BotFather)
|
|
49
30
|
2. Run `/newbot`
|
|
50
31
|
3. Pick a name and username
|
|
51
32
|
4. Copy the bot token
|
|
52
33
|
|
|
53
|
-
### 2. Configure the
|
|
34
|
+
### 2. Configure the bot token in π
|
|
54
35
|
|
|
55
36
|
Start π, then run:
|
|
56
37
|
|
|
@@ -66,63 +47,97 @@ Paste your bot token when prompted. If a bot token is already saved in `~/.pi/ag
|
|
|
66
47
|
/telegram-connect
|
|
67
48
|
```
|
|
68
49
|
|
|
69
|
-
The
|
|
50
|
+
The adapter is session-local: only one π instance polls Telegram at a time. `/telegram-connect` records polling ownership in `~/.pi/agent/locks.json`; live ownership moves require confirmation, while `/new` and same-`cwd` process restarts resume automatically.
|
|
70
51
|
|
|
71
|
-
### 4. Pair your account
|
|
52
|
+
### 4. Pair your Telegram account
|
|
72
53
|
|
|
73
54
|
1. Open the DM with your bot in Telegram
|
|
74
55
|
2. Send `/start`
|
|
75
56
|
|
|
76
|
-
The first user to message the bot becomes the exclusive owner of the
|
|
57
|
+
The first user to message the bot becomes the exclusive owner of the adapter. Messages from other users are ignored.
|
|
77
58
|
|
|
78
|
-
|
|
59
|
+
### Environment-only configuration
|
|
79
60
|
|
|
80
|
-
|
|
61
|
+
Most day-to-day controls live in the Telegram menu or π commands. A few important runtime knobs intentionally stay in environment variables because they affect bootstrap, networking, or transport limits before a menu can help:
|
|
81
62
|
|
|
82
|
-
|
|
63
|
+
- **Bot token bootstrap**: `/telegram-setup` can prefill from `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, or `TELEGRAM_KEY` when no token is already saved.
|
|
64
|
+
- **HTTP/HTTPS proxy**: native `fetch` can use `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` when Node's environment proxy mode is enabled. Use `NODE_USE_ENV_PROXY=1` or start Node with `--use-env-proxy`. SOCKS5 is not part of the zero-dependency core. If you need it, run a local HTTP-to-SOCKS bridge or system tunnel and point `HTTP_PROXY` / `HTTPS_PROXY` at the HTTP endpoint.
|
|
65
|
+
- **Agent data root / temp location**: `PI_CODING_AGENT_DIR` changes the base agent directory used for `telegram.json`, locks, generated outbound-handler artifacts, and Telegram temp files. When unset, the adapter uses `~/.pi/agent`, so inbound Telegram files land in `~/.pi/agent/tmp/telegram`.
|
|
66
|
+
- **Inbound file limit**: `PI_TELEGRAM_INBOUND_FILE_MAX_BYTES` or `TELEGRAM_MAX_FILE_SIZE_BYTES` changes the default 50 MiB Telegram download limit.
|
|
67
|
+
- **Outbound attachment limit**: `PI_TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES` or `TELEGRAM_MAX_ATTACHMENT_SIZE_BYTES` changes the default 50 MiB `telegram_attach` delivery limit.
|
|
83
68
|
|
|
84
|
-
Use
|
|
69
|
+
## Use
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- **`/continue`**: Enqueue a priority `continue` prompt. It waits like normal Telegram work when π is busy and can trigger prompt/skill handling that listens for `continue`.
|
|
90
|
-
- **`/abort`**: Abort the active run without touching the queue.
|
|
91
|
-
- **`/stop`**: Abort the active run and clear all waiting Telegram queue items.
|
|
71
|
+
Once paired, chat with your bot in Telegram. Text, images, files, replies, edits, media groups, and configured handler output are forwarded into π as Telegram-originated turns.
|
|
72
|
+
|
|
73
|
+
What it feels like:
|
|
92
74
|
|
|
93
|
-
|
|
75
|
+
- Open `/start` and get a Telegram control panel for the running π session: status, prompt templates, model, thinking, settings, and queue.
|
|
76
|
+
- Fire off three tasks while π is busy. They become visible queue items instead of terminal noise.
|
|
77
|
+
- Open Queue from the menu, inspect waiting work, delete stale prompts, or move important work forward.
|
|
78
|
+
- Switch models from Telegram mid-run; the adapter schedules a safe continuation instead of tearing state apart.
|
|
79
|
+
- Send a voice note; an inbound handler transcribes it; π answers in the same chat.
|
|
80
|
+
- Drop a screenshot and ask, "what is broken here?" The image payload reaches π with the local file context.
|
|
81
|
+
- Ask for a generated file; when π calls `telegram_attach`, the artifact returns to Telegram with the next reply.
|
|
94
82
|
|
|
95
|
-
|
|
83
|
+
### Telegram controls
|
|
96
84
|
|
|
97
|
-
|
|
85
|
+
Use these inside the Telegram DM with your bot. The main entrypoint is `/start`: it opens the operator menu and exposes many of the important agent controls that normally live in the CLI, adapted for Telegram.
|
|
98
86
|
|
|
99
|
-
|
|
87
|
+
- **`/start`**: Pair the first Telegram user when needed, register bot commands, and open the inline application menu with command help, prompt-template commands, status rows, model controls, thinking controls, settings, and queue controls.
|
|
88
|
+
- **`/compact`**: Start session compaction when the session is idle.
|
|
89
|
+
- **`/next`**: Dispatch the next queued turn, aborting π first if needed.
|
|
90
|
+
- **`/continue`**: Enqueue a priority `continue` prompt.
|
|
91
|
+
- **`/abort`**: Abort the active run without touching the queue.
|
|
92
|
+
- **`/stop`**: Abort the active run and clear waiting Telegram queue items.
|
|
93
|
+
|
|
94
|
+
Hidden compatibility shortcuts: `/help` and `/status` open the main application menu, `/model` opens model controls, `/thinking` opens reasoning controls, `/queue` opens queue controls, and `/settings` opens bridge settings.
|
|
95
|
+
|
|
96
|
+
Prompt-template commands are discovered from π prompt templates, mapped to Telegram-safe aliases (`fix-tests.md` becomes `/fix_tests`), shown in `/start`, and expanded before queueing.
|
|
97
|
+
|
|
98
|
+
### π commands
|
|
100
99
|
|
|
101
100
|
Run these inside π, not Telegram:
|
|
102
101
|
|
|
103
102
|
- **`/telegram-setup`**: Configure or update the Telegram bot token.
|
|
104
|
-
- **`/telegram-
|
|
105
|
-
- **`/telegram-settings`**: Open local
|
|
106
|
-
- **`/telegram-connect`**: Start polling Telegram updates in the current π session, acquire the singleton lock, or interactively move ownership here from another live instance.
|
|
103
|
+
- **`/telegram-connect`**: Start polling Telegram updates in the current π session and acquire the singleton lock.
|
|
104
|
+
- **`/telegram-settings`**: Open local settings and toggle proactive push using the same `telegram.json` flag as the Telegram `/settings` menu.
|
|
107
105
|
- **`/telegram-disconnect`**: Stop polling in the current π session and release the singleton lock.
|
|
106
|
+
- **`/telegram-status`**: Inspect adapter status, connection, polling, execution, queue, and recent redacted runtime/API failure events.
|
|
107
|
+
|
|
108
|
+
### Files and artifacts
|
|
109
|
+
|
|
110
|
+
Send files or images directly to the bot. Inbound downloads are saved under `<agent-dir>/tmp/telegram` and default to a 50 MiB limit. The agent dir is `~/.pi/agent` unless `PI_CODING_AGENT_DIR` overrides it.
|
|
111
|
+
|
|
112
|
+
If you ask π for a generated file, π can call the `telegram_attach` tool and the adapter sends the file with the next Telegram reply. Outbound attachments also default to a 50 MiB limit. Environment variables for both limits are listed in [Environment-only configuration](#environment-only-configuration).
|
|
108
113
|
|
|
109
|
-
|
|
114
|
+
## Core features
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
- Very long text messages that Telegram appears to split automatically are coalesced through a short conservative debounce and forwarded to π as one prompt when the first chunk is near Telegram's text limit, currently using a 3600-character threshold. Commands, bot messages, media groups, and normal short follow-ups are not coalesced.
|
|
113
|
-
- `👍`, `⚡️`, `❤️`, `🕊`, and `🔥` move a waiting prompt into the priority prompt queue, behind control actions but ahead of default prompts. Removing the last priority reaction sends it back to its normal queue position, and adding a priority reaction again gives it a fresh priority position.
|
|
114
|
-
- `👎`, `👻`, `💔`, `💩`, and `🗑` remove a waiting turn from the queue. Telegram Bot API does not expose ordinary DM message-deletion events through the polling path used here, so queue removal is bound to removal reactions.
|
|
115
|
-
- Reactions apply to any waiting Telegram turn, including text, voice, files, images, and media groups. For media groups, a reaction on any message in the group applies to the whole queued turn.
|
|
116
|
-
- If you edit a Telegram message while it is still waiting in the queue, the queued turn is updated instead of creating a duplicate prompt. Edits after a turn has already started may not affect the active run.
|
|
117
|
-
- Telegram replies to earlier text or caption messages are forwarded as `[reply]` context for normal prompts, while slash commands still parse from the new message text only.
|
|
118
|
-
- Inbound images, albums, and files are saved to `~/.pi/agent/tmp/telegram`. Unhandled local file paths are included in the prompt, handled attachment output is injected into the prompt text, and inbound images are forwarded to π as image inputs. Inbound downloads default to a 50 MiB limit and can be adjusted with `PI_TELEGRAM_INBOUND_FILE_MAX_BYTES` or `TELEGRAM_MAX_FILE_SIZE_BYTES`.
|
|
119
|
-
- Queue reactions depend on Telegram delivering `message_reaction` updates for your bot and chat type.
|
|
116
|
+
### Operator menu and controls
|
|
120
117
|
|
|
121
|
-
|
|
118
|
+
The inline application menu is the primary operator surface. It exposes status, prompt-template commands, model selection, thinking level selection, settings, and queue inspection/mutation: a Telegram-shaped subset of the important handles normally available from the CLI. A typical control loop stays inside Telegram: open `/start`, inspect status, jump into Queue, delete stale work, switch model, return to the main menu, and keep the π session running without touching the terminal.
|
|
122
119
|
|
|
123
|
-
|
|
120
|
+
### Queue runtime
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
Messages sent while π is busy enter the prompt queue and are processed in order. Control actions and model-switch continuation turns use higher-priority lanes so operational commands can resume before normal prompts.
|
|
123
|
+
|
|
124
|
+
The menu is the primary way to inspect and mutate the queue. Reactions are an extra shortcut when Telegram delivers `message_reaction` updates for the chat: `👍`, `⚡️`, `❤️`, `🕊`, and `🔥` promote waiting work; `👎`, `👻`, `💔`, `💩`, and `🗑` remove it. The same rules apply to text, voice, files, images, and media groups.
|
|
125
|
+
|
|
126
|
+
### Streaming and Telegram HTML rendering
|
|
127
|
+
|
|
128
|
+
Closed Markdown blocks stream back as rich Telegram HTML while π is generating. The growing tail stays conservative until the final rendered reply lands. Long replies are split below Telegram limits without intentionally breaking HTML structures, links, code blocks, blockquotes, lists, or code fences.
|
|
129
|
+
|
|
130
|
+
Rendering is phone-aware: tables and lists stay narrow, table padding accounts for emoji graphemes and wide Unicode display width, unsupported link forms degrade safely, and block spacing stays faithful to the original Markdown.
|
|
131
|
+
|
|
132
|
+
### Media, replies, edits, and split text
|
|
133
|
+
|
|
134
|
+
Telegram replies to earlier text or caption messages are forwarded as `[reply]` context for normal prompts, while slash commands still parse from the new message text only. If a Telegram message is edited while still waiting in the queue, the queued turn is updated instead of duplicated. Very long text messages that Telegram appears to split automatically are coalesced through a conservative debounce when the first chunk is near Telegram's text limit.
|
|
135
|
+
|
|
136
|
+
### Inbound handlers
|
|
137
|
+
|
|
138
|
+
`telegram.json` can define ordered `inboundHandlers` for Telegram → π preprocessing: text translation, voice transcription, OCR, PDF extraction, or any command-template pipeline. Matching handlers run before the turn enters the queue; failed handlers record diagnostics and fall back safely. Legacy `attachmentHandlers` still work as a deprecated compatibility alias appended after `inboundHandlers`.
|
|
139
|
+
|
|
140
|
+
A practical voice setup is simple: Telegram `.ogg` arrives, STT runs locally or through your chosen command, stdout is injected as `[outputs]`, and π receives the result as usable prompt context.
|
|
126
141
|
|
|
127
142
|
```json
|
|
128
143
|
{
|
|
@@ -149,19 +164,9 @@ Run these inside π, not Telegram:
|
|
|
149
164
|
}
|
|
150
165
|
```
|
|
151
166
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
### Requesting Files
|
|
155
|
-
|
|
156
|
-
If you ask π for a file or generated artifact (e.g., _"generate a shell script and attach it"_), π can call the `telegram_attach` tool, and the extension will send the file alongside its next Telegram reply. `telegram_attach` is the only π tool registered by `pi-telegram`; use it for ordinary files, not for Telegram-native voice or buttons. Outbound attachments default to a 50 MiB limit and can be adjusted with `PI_TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES` or `TELEGRAM_MAX_ATTACHMENT_SIZE_BYTES`.
|
|
157
|
-
|
|
158
|
-
### Assistant-Authored Outbound Actions
|
|
159
|
-
|
|
160
|
-
Assistant replies can include hidden outbound blocks. `telegram_voice` and `telegram_button` are not π tools; they are assistant-authored HTML comments that the bridge removes from Telegram text and handles after `agent_end`. Recognized blocks must start at column zero on a top-level line outside fenced code, quotes, and lists, so documentation examples remain literal. The agent writes normal Markdown; the extension owns voice generation, button markup, callback routing, and delivery.
|
|
161
|
-
|
|
162
|
-
#### Voice
|
|
167
|
+
### Outbound handlers, voice, and buttons
|
|
163
168
|
|
|
164
|
-
|
|
169
|
+
Assistant replies can include hidden outbound blocks. `telegram_voice` and `telegram_button` are not π tools; they are assistant-authored HTML comments that the adapter removes from Telegram text and handles after `agent_end`. Recognized blocks must start at column zero on a top-level line outside fenced code, quotes, and lists.
|
|
165
170
|
|
|
166
171
|
```md
|
|
167
172
|
Full technical answer stays readable as text.
|
|
@@ -170,27 +175,14 @@ Full technical answer stays readable as text.
|
|
|
170
175
|
Text to synthesize as a Telegram voice message.
|
|
171
176
|
-->
|
|
172
177
|
|
|
173
|
-
<!--
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Outbound `type: "text"` handlers can transform final text/Markdown before Telegram rendering and delivery, using stdin and `{text}` as input and non-empty stdout as replacement text. They are a good fit for machine translation, tone normalization, redaction, glossary expansion, or any other final text rewrite that should happen outside the agent prompt. The transform also applies when the bridge finalizes an already streamed rich preview, so Telegram may briefly show the pre-transform preview before the final edited message lands. Inline button labels are transformed too, while callback data and prompts stay unchanged.
|
|
179
|
-
|
|
180
|
-
```json
|
|
181
|
-
{
|
|
182
|
-
"outboundHandlers": [
|
|
183
|
-
{
|
|
184
|
-
"type": "text",
|
|
185
|
-
"template": "/path/to/translate --lang {lang=ru} --text {text}"
|
|
186
|
-
}
|
|
187
|
-
]
|
|
188
|
-
}
|
|
178
|
+
<!-- telegram_button label="Show risks"
|
|
179
|
+
List the main risks first.
|
|
180
|
+
-->
|
|
189
181
|
```
|
|
190
182
|
|
|
191
|
-
Outbound
|
|
183
|
+
Outbound `type: "text"` handlers can transform final text/Markdown before Telegram rendering and delivery. Outbound `type: "voice"` handlers can translate, synthesize, and convert hidden `telegram_voice` text into Telegram-native OGG/Opus voice through the same command-template contract used by inbound handlers.
|
|
192
184
|
|
|
193
|
-
A composed voice
|
|
185
|
+
A composed voice pipeline can translate, synthesize, and convert in one pass:
|
|
194
186
|
|
|
195
187
|
```json
|
|
196
188
|
{
|
|
@@ -208,43 +200,36 @@ A composed voice setup can translate the hidden `telegram_voice` text, synthesiz
|
|
|
208
200
|
}
|
|
209
201
|
```
|
|
210
202
|
|
|
211
|
-
|
|
203
|
+
The agent writes intent; the adapter owns transport. Text remains readable, voice becomes native Telegram media, and buttons route back as queued prompts.
|
|
212
204
|
|
|
213
|
-
|
|
205
|
+
### Extension interop
|
|
214
206
|
|
|
215
|
-
|
|
216
|
-
I can continue.
|
|
207
|
+
Unknown inline-button callbacks are forwarded to π as `[callback] <data>` when they do not belong to pi-telegram, so other extensions can namespace and handle Telegram buttons without polling the bot themselves. Layered extensions that need synchronous update handling can register a runtime interceptor on the shared update registry.
|
|
217
208
|
|
|
218
|
-
|
|
219
|
-
List the main risks first.
|
|
220
|
-
-->
|
|
221
|
-
|
|
222
|
-
<!-- telegram_button label=Continue prompt="Continue with the current plan." -->
|
|
209
|
+
### Proactive push
|
|
223
210
|
|
|
224
|
-
|
|
225
|
-
```
|
|
211
|
+
`telegram.json` can set `proactivePush: true` to send successful local non-Telegram final replies to the paired Telegram chat when no Telegram turn is active. Local prompt text is not mirrored because the bot does not own terminal user messages. The mode is off by default and can be toggled from settings.
|
|
226
212
|
|
|
227
|
-
|
|
213
|
+
## Docs
|
|
228
214
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
```
|
|
215
|
+
- [Project Context](./AGENTS.md): durable engineering conventions and architecture constraints.
|
|
216
|
+
- [Open Backlog](./BACKLOG.md): planned work and known follow-ups.
|
|
217
|
+
- [Changelog](./CHANGELOG.md): completed delivery history.
|
|
218
|
+
- [Documentation Index](./docs/README.md): technical docs hub.
|
|
219
|
+
- [Architecture](./docs/architecture.md): runtime and subsystem overview.
|
|
220
|
+
- [Inbound Handlers](./docs/inbound-handlers.md): Telegram → π preprocessing.
|
|
221
|
+
- [Outbound Handlers](./docs/outbound-handlers.md): final text, voice, and artifact pipelines.
|
|
222
|
+
- [Command Templates](./docs/command-templates.md): portable command-template contract.
|
|
223
|
+
- [Callback Namespaces](./docs/callback-namespaces.md): callback interop for layered extensions.
|
|
224
|
+
- [External Handlers](./docs/external-handlers.md): shared update interception.
|
|
225
|
+
- [Extension Sections Draft](./docs/extension-sections.md): future Telegram extension sections platform.
|
|
226
|
+
- [Locks](./docs/locks.md): singleton polling ownership.
|
|
242
227
|
|
|
243
228
|
## Notes
|
|
244
229
|
|
|
245
|
-
-
|
|
246
|
-
-
|
|
247
|
-
- Temporary inbound Telegram files are cleaned up on later session starts
|
|
230
|
+
- The extension intentionally keeps rich visual/TUI configuration minimal for now. For advanced setup, ask an agent to read this README and the docs, then update `~/.pi/agent/telegram.json` for your workflow.
|
|
231
|
+
- Replies to Telegram prompts are sent as Telegram replies to the source message when possible; if the source message is unavailable, delivery falls back to a normal message.
|
|
232
|
+
- Temporary inbound Telegram files are cleaned up on later session starts.
|
|
248
233
|
|
|
249
234
|
## License
|
|
250
235
|
|
package/docs/architecture.md
CHANGED
|
@@ -24,7 +24,7 @@ Naming rule: because the repository already scopes this codebase to Telegram, ex
|
|
|
24
24
|
Current runtime areas use these ownership boundaries:
|
|
25
25
|
|
|
26
26
|
- `index.ts`: single composition root for live π/Telegram ports, session state, API-bound transport adapters, and status updates.
|
|
27
|
-
- `api`: Bot API transport shapes/helpers, retries, file download, temp-dir lifecycle, inbound limits, chat actions, lazy bot-token clients,
|
|
27
|
+
- `api`: Bot API transport shapes/helpers, retries, file download, temp-dir lifecycle, inbound limits, chat actions, lazy bot-token clients, runtime error recording, and the `TELEGRAM_API_BASE` constant for the Bot API endpoint.
|
|
28
28
|
- `config` / `setup`: persisted bot/session pairing state, authorization, first-user pairing, token prompting, env fallback, validation, and config persistence.
|
|
29
29
|
- `locks` / `polling`: singleton `locks.json` ownership, takeover/restart semantics, long-poll controller state, update offset persistence, and poll-loop runtime wiring.
|
|
30
30
|
- `updates` / `routing`: update classification/execution planning, paired authorization, reactions, edits, callbacks, and inbound route composition.
|
|
@@ -114,7 +114,7 @@ unregister();
|
|
|
114
114
|
Sections are registered by normal pi extensions:
|
|
115
115
|
|
|
116
116
|
```ts
|
|
117
|
-
import type { ExtensionAPI } from "@
|
|
117
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
118
118
|
import { registerTelegramSection } from "@llblab/pi-telegram/lib/extension-sections.ts";
|
|
119
119
|
|
|
120
120
|
export default function (pi: ExtensionAPI) {
|
package/index.ts
CHANGED
|
@@ -148,6 +148,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
148
148
|
downloadFile: downloadTelegramBridgeFile,
|
|
149
149
|
editMessageText: editTelegramMessageText,
|
|
150
150
|
answerCallbackQuery,
|
|
151
|
+
answerGuestQuery,
|
|
151
152
|
prepareTempDir,
|
|
152
153
|
} = Api.createDefaultTelegramBridgeApiRuntime({
|
|
153
154
|
getBotToken: configStore.getBotToken,
|
|
@@ -333,6 +334,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
333
334
|
updateStatus,
|
|
334
335
|
dispatchNextQueuedTelegramTurn,
|
|
335
336
|
answerCallbackQuery,
|
|
337
|
+
answerGuestQuery,
|
|
336
338
|
sendTextReply,
|
|
337
339
|
setMyCommands,
|
|
338
340
|
getCommands,
|
|
@@ -494,6 +496,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
494
496
|
sendMarkdownReply,
|
|
495
497
|
sendTextReply,
|
|
496
498
|
sendQueuedAttachments: queuedAttachmentSender,
|
|
499
|
+
answerGuestQuery,
|
|
497
500
|
planOutboundReply: outboundReplyPlanner,
|
|
498
501
|
sendOutboundReplyArtifacts: outboundReplyArtifactSender,
|
|
499
502
|
isCurrentOwner: lockOwnershipGuard.ownsContext,
|
package/lib/api.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { join, resolve } from "node:path";
|
|
|
12
12
|
import { Readable, Transform } from "node:stream";
|
|
13
13
|
import { pipeline } from "node:stream/promises";
|
|
14
14
|
|
|
15
|
+
export const TELEGRAM_API_BASE = "https://api.telegram.org";
|
|
16
|
+
|
|
15
17
|
export const TELEGRAM_FILE_MAX_BYTES = 50 * 1024 * 1024;
|
|
16
18
|
|
|
17
19
|
export function getTelegramInboundFileByteLimitFromEnv(
|
|
@@ -149,12 +151,25 @@ export interface TelegramMessageReactionUpdated {
|
|
|
149
151
|
date: number;
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
export interface TelegramGuestMessage {
|
|
155
|
+
message_id: number;
|
|
156
|
+
from?: TelegramUser;
|
|
157
|
+
chat: TelegramChat;
|
|
158
|
+
date: number;
|
|
159
|
+
text?: string;
|
|
160
|
+
caption?: string;
|
|
161
|
+
guest_query_id: string;
|
|
162
|
+
guest_bot_caller_user?: TelegramUser;
|
|
163
|
+
guest_bot_caller_chat?: TelegramChat;
|
|
164
|
+
}
|
|
165
|
+
|
|
152
166
|
export interface TelegramUpdate {
|
|
153
167
|
update_id: number;
|
|
154
168
|
message?: TelegramMessage;
|
|
155
169
|
edited_message?: TelegramMessage;
|
|
156
170
|
callback_query?: TelegramCallbackQuery;
|
|
157
171
|
message_reaction?: TelegramMessageReactionUpdated;
|
|
172
|
+
guest_message?: TelegramGuestMessage;
|
|
158
173
|
deleted_business_messages?: { message_ids?: unknown };
|
|
159
174
|
}
|
|
160
175
|
|
|
@@ -182,6 +197,15 @@ export type TelegramEditMessageTextBody = Record<string, unknown> & {
|
|
|
182
197
|
parse_mode?: "HTML";
|
|
183
198
|
};
|
|
184
199
|
|
|
200
|
+
export type TelegramSendMessageDraftBody = Record<string, unknown> & {
|
|
201
|
+
chat_id: number;
|
|
202
|
+
draft_id: number;
|
|
203
|
+
text?: string;
|
|
204
|
+
parse_mode?: string;
|
|
205
|
+
entities?: unknown[];
|
|
206
|
+
message_thread_id?: number;
|
|
207
|
+
};
|
|
208
|
+
|
|
185
209
|
interface TelegramApiResponse<T> {
|
|
186
210
|
ok: boolean;
|
|
187
211
|
result?: T;
|
|
@@ -231,6 +255,7 @@ export interface TelegramApiClient {
|
|
|
231
255
|
callbackQueryId: string,
|
|
232
256
|
text?: string,
|
|
233
257
|
) => Promise<void>;
|
|
258
|
+
answerGuestQuery?: (guestQueryId: string, text?: string) => Promise<void>;
|
|
234
259
|
}
|
|
235
260
|
|
|
236
261
|
export interface TelegramBridgeApiRuntimeDeps {
|
|
@@ -273,7 +298,12 @@ export interface TelegramBridgeApiRuntime {
|
|
|
273
298
|
sendMessageDraft: (
|
|
274
299
|
chatId: number,
|
|
275
300
|
draftId: number,
|
|
276
|
-
text
|
|
301
|
+
text?: string,
|
|
302
|
+
options?: {
|
|
303
|
+
parse_mode?: string;
|
|
304
|
+
entities?: unknown[];
|
|
305
|
+
message_thread_id?: number;
|
|
306
|
+
},
|
|
277
307
|
) => Promise<boolean>;
|
|
278
308
|
sendMessage: (body: TelegramSendMessageBody) => Promise<TelegramSentMessage>;
|
|
279
309
|
editMessageText: (
|
|
@@ -283,6 +313,7 @@ export interface TelegramBridgeApiRuntime {
|
|
|
283
313
|
callbackQueryId: string,
|
|
284
314
|
text?: string,
|
|
285
315
|
) => Promise<void>;
|
|
316
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
286
317
|
prepareTempDir: () => Promise<number>;
|
|
287
318
|
}
|
|
288
319
|
|
|
@@ -513,7 +544,7 @@ export async function callTelegram<TResponse>(
|
|
|
513
544
|
return callTelegramWithRetry(
|
|
514
545
|
method,
|
|
515
546
|
async () =>
|
|
516
|
-
fetch(
|
|
547
|
+
fetch(`${TELEGRAM_API_BASE}/bot${configuredBotToken}/${method}`, {
|
|
517
548
|
method: "POST",
|
|
518
549
|
headers: { "content-type": "application/json" },
|
|
519
550
|
body: JSON.stringify(body),
|
|
@@ -532,9 +563,7 @@ export async function fetchTelegramBotIdentity(
|
|
|
532
563
|
botToken: string,
|
|
533
564
|
fetchImpl: typeof fetch = fetch,
|
|
534
565
|
): Promise<TelegramBotIdentityResponse> {
|
|
535
|
-
const response = await fetchImpl(
|
|
536
|
-
`https://api.telegram.org/bot${botToken}/getMe`,
|
|
537
|
-
);
|
|
566
|
+
const response = await fetchImpl(`${TELEGRAM_API_BASE}/bot${botToken}/getMe`);
|
|
538
567
|
return response.json() as Promise<TelegramBotIdentityResponse>;
|
|
539
568
|
}
|
|
540
569
|
|
|
@@ -557,14 +586,11 @@ export async function callTelegramMultipart<TResponse>(
|
|
|
557
586
|
form.set(key, value);
|
|
558
587
|
}
|
|
559
588
|
form.set(fileField, fileBlob, fileName);
|
|
560
|
-
return fetch(
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
signal: options?.signal,
|
|
566
|
-
},
|
|
567
|
-
);
|
|
589
|
+
return fetch(`${TELEGRAM_API_BASE}/bot${configuredBotToken}/${method}`, {
|
|
590
|
+
method: "POST",
|
|
591
|
+
body: form,
|
|
592
|
+
signal: options?.signal,
|
|
593
|
+
});
|
|
568
594
|
},
|
|
569
595
|
options,
|
|
570
596
|
);
|
|
@@ -591,7 +617,7 @@ export async function downloadTelegramFile(
|
|
|
591
617
|
`${randomUUID()}-${sanitizeFileName(suggestedName)}`,
|
|
592
618
|
);
|
|
593
619
|
const response = await fetch(
|
|
594
|
-
|
|
620
|
+
`${TELEGRAM_API_BASE}/file/bot${configuredBotToken}/${file.file_path}`,
|
|
595
621
|
{ signal: options?.signal },
|
|
596
622
|
);
|
|
597
623
|
if (!response.ok) {
|
|
@@ -730,13 +756,18 @@ export function createTelegramBridgeApiRuntime(
|
|
|
730
756
|
}),
|
|
731
757
|
"typing",
|
|
732
758
|
),
|
|
733
|
-
sendMessageDraft: (chatId, draftId, text) => {
|
|
734
|
-
|
|
735
|
-
return callRecorded<boolean>("sendMessageDraft", {
|
|
759
|
+
sendMessageDraft: (chatId, draftId, text, options) => {
|
|
760
|
+
const body: Record<string, unknown> = {
|
|
736
761
|
chat_id: chatId,
|
|
737
762
|
draft_id: draftId,
|
|
738
|
-
|
|
739
|
-
|
|
763
|
+
};
|
|
764
|
+
if (text !== undefined) body.text = text;
|
|
765
|
+
if (options?.parse_mode !== undefined)
|
|
766
|
+
body.parse_mode = options.parse_mode;
|
|
767
|
+
if (options?.entities !== undefined) body.entities = options.entities;
|
|
768
|
+
if (options?.message_thread_id !== undefined)
|
|
769
|
+
body.message_thread_id = options.message_thread_id;
|
|
770
|
+
return callRecorded<boolean>("sendMessageDraft", body);
|
|
740
771
|
},
|
|
741
772
|
sendMessage: (body) =>
|
|
742
773
|
callRecorded<TelegramSentMessage>("sendMessage", body),
|
|
@@ -753,6 +784,18 @@ export function createTelegramBridgeApiRuntime(
|
|
|
753
784
|
answerCallbackQuery: (callbackQueryId, text) => {
|
|
754
785
|
return deps.client.answerCallbackQuery(callbackQueryId, text);
|
|
755
786
|
},
|
|
787
|
+
answerGuestQuery: (guestQueryId, text) => {
|
|
788
|
+
const body: Record<string, unknown> = { guest_query_id: guestQueryId };
|
|
789
|
+
if (text !== undefined) {
|
|
790
|
+
body.result = {
|
|
791
|
+
type: "article",
|
|
792
|
+
id: "1",
|
|
793
|
+
title: text.length > 64 ? text.slice(0, 61) + "..." : text,
|
|
794
|
+
input_message_content: { message_text: text },
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
return callRecorded<void>("answerGuestQuery", body);
|
|
798
|
+
},
|
|
756
799
|
prepareTempDir: () =>
|
|
757
800
|
prepareTelegramTempDir(deps.tempDir, deps.tempFileMaxAgeMs),
|
|
758
801
|
};
|
package/lib/pi.ts
CHANGED
package/lib/polling.ts
CHANGED
package/lib/preview.ts
CHANGED
|
@@ -61,7 +61,12 @@ export interface TelegramPreviewRuntimeDeps<
|
|
|
61
61
|
sendDraft: (
|
|
62
62
|
chatId: number,
|
|
63
63
|
draftId: number,
|
|
64
|
-
text
|
|
64
|
+
text?: string,
|
|
65
|
+
options?: {
|
|
66
|
+
parse_mode?: string;
|
|
67
|
+
entities?: unknown[];
|
|
68
|
+
message_thread_id?: number;
|
|
69
|
+
},
|
|
65
70
|
) => Promise<unknown>;
|
|
66
71
|
sendMessage: (
|
|
67
72
|
chatId: number,
|
|
@@ -158,7 +163,12 @@ export interface TelegramPreviewControllerDeps<
|
|
|
158
163
|
sendDraft: (
|
|
159
164
|
chatId: number,
|
|
160
165
|
draftId: number,
|
|
161
|
-
text
|
|
166
|
+
text?: string,
|
|
167
|
+
options?: {
|
|
168
|
+
parse_mode?: string;
|
|
169
|
+
entities?: unknown[];
|
|
170
|
+
message_thread_id?: number;
|
|
171
|
+
},
|
|
162
172
|
) => Promise<unknown>;
|
|
163
173
|
sendMessage: (
|
|
164
174
|
chatId: number,
|
package/lib/queue.ts
CHANGED
|
@@ -66,6 +66,7 @@ export interface TelegramQueueItemBase {
|
|
|
66
66
|
kind: TelegramQueueItemKind;
|
|
67
67
|
chatId: number;
|
|
68
68
|
replyToMessageId: number;
|
|
69
|
+
guestQueryId?: string;
|
|
69
70
|
queueOrder: number;
|
|
70
71
|
queueLane: TelegramQueueLane;
|
|
71
72
|
laneOrder: number;
|
|
@@ -113,6 +114,7 @@ export interface TelegramActiveTurnStore<
|
|
|
113
114
|
clear: () => void;
|
|
114
115
|
getChatId: () => number | undefined;
|
|
115
116
|
getReplyToMessageId: () => number | undefined;
|
|
117
|
+
getGuestQueryId: () => string | undefined;
|
|
116
118
|
getSourceMessageIds: () => number[] | undefined;
|
|
117
119
|
}
|
|
118
120
|
|
|
@@ -203,6 +205,7 @@ export function createTelegramActiveTurnStore<
|
|
|
203
205
|
},
|
|
204
206
|
getChatId: () => activeTurn?.chatId,
|
|
205
207
|
getReplyToMessageId: () => activeTurn?.replyToMessageId,
|
|
208
|
+
getGuestQueryId: () => activeTurn?.guestQueryId,
|
|
206
209
|
getSourceMessageIds: () => activeTurn?.sourceMessageIds,
|
|
207
210
|
};
|
|
208
211
|
}
|
|
@@ -785,6 +788,7 @@ export interface TelegramAgentEndRuntimeDeps<
|
|
|
785
788
|
text: string,
|
|
786
789
|
) => Promise<unknown>;
|
|
787
790
|
sendQueuedAttachments: (turn: TTurn) => Promise<void>;
|
|
791
|
+
answerGuestQuery?: (guestQueryId: string, text?: string) => Promise<void>;
|
|
788
792
|
planOutboundReply?: (
|
|
789
793
|
markdown: string,
|
|
790
794
|
) => TelegramAgentEndOutboundReplyPlan<TReplyMarkup>;
|
|
@@ -832,6 +836,7 @@ export interface TelegramAgentEndHookRuntimeDeps<
|
|
|
832
836
|
>["sendMarkdownReply"];
|
|
833
837
|
sendTextReply: TelegramAgentEndRuntimeDeps<TTurn>["sendTextReply"];
|
|
834
838
|
sendQueuedAttachments: (turn: TTurn) => Promise<void>;
|
|
839
|
+
answerGuestQuery?: TelegramAgentEndRuntimeDeps<TTurn>["answerGuestQuery"];
|
|
835
840
|
planOutboundReply?: TelegramAgentEndRuntimeDeps<
|
|
836
841
|
TTurn,
|
|
837
842
|
TReplyMarkup
|
|
@@ -952,6 +957,7 @@ export function createTelegramAgentEndHook<
|
|
|
952
957
|
sendMarkdownReply: deps.sendMarkdownReply,
|
|
953
958
|
sendTextReply: deps.sendTextReply,
|
|
954
959
|
sendQueuedAttachments: deps.sendQueuedAttachments,
|
|
960
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
955
961
|
planOutboundReply: deps.planOutboundReply,
|
|
956
962
|
sendOutboundReplyArtifacts: deps.sendOutboundReplyArtifacts,
|
|
957
963
|
getDefaultChatId: deps.getDefaultChatId,
|
|
@@ -1007,6 +1013,25 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1007
1013
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1008
1014
|
return;
|
|
1009
1015
|
}
|
|
1016
|
+
if (turn.guestQueryId) {
|
|
1017
|
+
if (deps.isCurrentOwner && !deps.isCurrentOwner()) {
|
|
1018
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (assistant.errorMessage) {
|
|
1022
|
+
await deps.answerGuestQuery?.(
|
|
1023
|
+
turn.guestQueryId,
|
|
1024
|
+
"Telegram bridge: π failed while processing the request.",
|
|
1025
|
+
);
|
|
1026
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
if (finalText) {
|
|
1030
|
+
await deps.answerGuestQuery?.(turn.guestQueryId, finalText);
|
|
1031
|
+
}
|
|
1032
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1010
1035
|
if (endPlan.shouldClearPreview) {
|
|
1011
1036
|
await deps.clearPreview(turn.chatId);
|
|
1012
1037
|
}
|
package/lib/routing.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { TelegramBridgeRuntime } from "./runtime.ts";
|
|
|
17
17
|
import * as TextGroups from "./text-groups.ts";
|
|
18
18
|
import * as Turns from "./turns.ts";
|
|
19
19
|
import * as Updates from "./updates.ts";
|
|
20
|
+
import type { TelegramUser } from "./updates.ts";
|
|
20
21
|
|
|
21
22
|
export type TelegramRoutedMessage = Updates.TelegramUpdateMessage &
|
|
22
23
|
Media.TelegramMediaMessage &
|
|
@@ -80,6 +81,7 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
80
81
|
callbackQueryId: string,
|
|
81
82
|
text?: string,
|
|
82
83
|
) => Promise<void>;
|
|
84
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
83
85
|
sendTextReply: (
|
|
84
86
|
chatId: number,
|
|
85
87
|
replyToMessageId: number,
|
|
@@ -364,6 +366,33 @@ export function createTelegramInboundRouteRuntime<
|
|
|
364
366
|
...deps.telegramQueueStore,
|
|
365
367
|
updateStatus: deps.updateStatus,
|
|
366
368
|
});
|
|
369
|
+
const handleAuthorizedTelegramGuestMessage = async (
|
|
370
|
+
guestMessage: Updates.TelegramGuestMessage & { from: TelegramUser },
|
|
371
|
+
ctx: TContext,
|
|
372
|
+
): Promise<void> => {
|
|
373
|
+
const text = guestMessage.text ?? "";
|
|
374
|
+
const order = deps.bridgeRuntime.queue.allocateItemOrder();
|
|
375
|
+
const guestTurn: Queue.PendingTelegramTurn = {
|
|
376
|
+
kind: "prompt",
|
|
377
|
+
chatId: 0,
|
|
378
|
+
replyToMessageId: 0,
|
|
379
|
+
guestQueryId: guestMessage.guest_query_id,
|
|
380
|
+
sourceMessageIds: [],
|
|
381
|
+
queueOrder: order,
|
|
382
|
+
queueLane: "default",
|
|
383
|
+
laneOrder: order,
|
|
384
|
+
queuedAttachments: [],
|
|
385
|
+
content: [{ type: "text", text }],
|
|
386
|
+
historyText: text,
|
|
387
|
+
statusSummary: Turns.truncateTelegramQueueSummary(text),
|
|
388
|
+
};
|
|
389
|
+
const items = deps.telegramQueueStore.getQueuedItems();
|
|
390
|
+
deps.telegramQueueStore.setQueuedItems(
|
|
391
|
+
Queue.appendTelegramQueueItem(items, guestTurn),
|
|
392
|
+
);
|
|
393
|
+
deps.updateStatus(ctx);
|
|
394
|
+
deps.dispatchNextQueuedTelegramTurn(ctx);
|
|
395
|
+
};
|
|
367
396
|
return Updates.createTelegramPairedUpdateRuntime<TContext, TUpdate>({
|
|
368
397
|
getAllowedUserId: deps.configStore.getAllowedUserId,
|
|
369
398
|
setAllowedUserId: deps.configStore.setAllowedUserId,
|
|
@@ -377,9 +406,11 @@ export function createTelegramInboundRouteRuntime<
|
|
|
377
406
|
prioritizeQueuedTelegramTurnByMessageId:
|
|
378
407
|
deps.queueMutationRuntime.prioritizeByMessageId,
|
|
379
408
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
409
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
380
410
|
handleAuthorizedTelegramCallbackQuery: callbackHandler,
|
|
381
411
|
sendTextReply: deps.sendTextReply,
|
|
382
412
|
handleAuthorizedTelegramMessage: textDispatch.handleMessage,
|
|
383
413
|
handleAuthorizedTelegramEditedMessage: editRuntime.updateFromEditedMessage,
|
|
414
|
+
handleAuthorizedTelegramGuestMessage,
|
|
384
415
|
});
|
|
385
416
|
}
|
package/lib/runtime.ts
CHANGED
|
@@ -365,7 +365,8 @@ export function startTelegramTypingLoop(
|
|
|
365
365
|
state: TelegramBridgeRuntimeState,
|
|
366
366
|
deps: TelegramTypingLoopDeps,
|
|
367
367
|
): boolean {
|
|
368
|
-
if (state.typingInterval || deps.chatId === undefined
|
|
368
|
+
if (state.typingInterval || deps.chatId === undefined || deps.chatId === 0)
|
|
369
|
+
return false;
|
|
369
370
|
const sendTyping = (): void => {
|
|
370
371
|
void deps.sendTypingAction(deps.chatId as number);
|
|
371
372
|
};
|
package/lib/updates.ts
CHANGED
|
@@ -130,10 +130,19 @@ export interface TelegramCallbackQuery {
|
|
|
130
130
|
message?: TelegramUpdateMessage;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
export interface TelegramGuestMessage {
|
|
134
|
+
guest_query_id: string;
|
|
135
|
+
chat: TelegramChat;
|
|
136
|
+
from?: TelegramUser;
|
|
137
|
+
message_id?: number;
|
|
138
|
+
text?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
133
141
|
export interface TelegramUpdateRouting {
|
|
134
142
|
message?: TelegramUpdateMessage;
|
|
135
143
|
edited_message?: TelegramUpdateMessage;
|
|
136
144
|
callback_query?: TelegramCallbackQuery;
|
|
145
|
+
guest_message?: TelegramGuestMessage;
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
export function getAuthorizedTelegramCallbackQuery(
|
|
@@ -178,6 +187,16 @@ export function getAuthorizedTelegramEditedMessage(
|
|
|
178
187
|
return message;
|
|
179
188
|
}
|
|
180
189
|
|
|
190
|
+
export function getAuthorizedTelegramGuestMessage(
|
|
191
|
+
update: TelegramUpdateRouting,
|
|
192
|
+
): TelegramGuestMessage | undefined {
|
|
193
|
+
const guestMessage = update.guest_message;
|
|
194
|
+
if (!guestMessage || !guestMessage.from || guestMessage.from.is_bot) {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
return guestMessage;
|
|
198
|
+
}
|
|
199
|
+
|
|
181
200
|
// --- Flow ---
|
|
182
201
|
|
|
183
202
|
export interface TelegramMessageReactionUpdated {
|
|
@@ -198,6 +217,7 @@ export type TelegramUpdateFlowAction<
|
|
|
198
217
|
TelegramMessageReactionUpdated,
|
|
199
218
|
TCallbackQuery extends TelegramCallbackQuery = TelegramCallbackQuery,
|
|
200
219
|
TMessage extends TelegramUpdateMessage = TelegramUpdateMessage,
|
|
220
|
+
TGuestMessage extends TelegramGuestMessage = TelegramGuestMessage,
|
|
201
221
|
> =
|
|
202
222
|
| { kind: "ignore" }
|
|
203
223
|
| { kind: "deleted"; messageIds: number[] }
|
|
@@ -216,6 +236,11 @@ export type TelegramUpdateFlowAction<
|
|
|
216
236
|
kind: "edited-message";
|
|
217
237
|
message: TMessage & { from: TelegramUser };
|
|
218
238
|
authorization: TelegramAuthorizationState;
|
|
239
|
+
}
|
|
240
|
+
| {
|
|
241
|
+
kind: "guest";
|
|
242
|
+
guestMessage: TGuestMessage & { from: TelegramUser };
|
|
243
|
+
authorization: TelegramAuthorizationState;
|
|
219
244
|
};
|
|
220
245
|
|
|
221
246
|
export function buildTelegramUpdateFlowAction<
|
|
@@ -226,7 +251,8 @@ export function buildTelegramUpdateFlowAction<
|
|
|
226
251
|
): TelegramUpdateFlowAction<
|
|
227
252
|
NonNullable<TUpdate["message_reaction"]>,
|
|
228
253
|
NonNullable<TUpdate["callback_query"]>,
|
|
229
|
-
NonNullable<TUpdate["message"] | TUpdate["edited_message"]
|
|
254
|
+
NonNullable<TUpdate["message"] | TUpdate["edited_message"]>,
|
|
255
|
+
NonNullable<TUpdate["guest_message"]>
|
|
230
256
|
> {
|
|
231
257
|
const deletedMessageIds = extractDeletedTelegramMessageIds(update);
|
|
232
258
|
if (deletedMessageIds.length > 0) {
|
|
@@ -272,6 +298,19 @@ export function buildTelegramUpdateFlowAction<
|
|
|
272
298
|
),
|
|
273
299
|
};
|
|
274
300
|
}
|
|
301
|
+
const guestMessage = getAuthorizedTelegramGuestMessage(update);
|
|
302
|
+
if (guestMessage?.from) {
|
|
303
|
+
return {
|
|
304
|
+
kind: "guest",
|
|
305
|
+
guestMessage: guestMessage as NonNullable<TUpdate["guest_message"]> & {
|
|
306
|
+
from: TelegramUser;
|
|
307
|
+
},
|
|
308
|
+
authorization: getTelegramAuthorizationState(
|
|
309
|
+
guestMessage.from.id,
|
|
310
|
+
allowedUserId,
|
|
311
|
+
),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
275
314
|
return { kind: "ignore" };
|
|
276
315
|
}
|
|
277
316
|
|
|
@@ -282,6 +321,7 @@ export type TelegramUpdateExecutionPlan<
|
|
|
282
321
|
TelegramMessageReactionUpdated,
|
|
283
322
|
TCallbackQuery extends TelegramCallbackQuery = TelegramCallbackQuery,
|
|
284
323
|
TMessage extends TelegramUpdateMessage = TelegramUpdateMessage,
|
|
324
|
+
TGuestMessage extends TelegramGuestMessage = TelegramGuestMessage,
|
|
285
325
|
> =
|
|
286
326
|
| { kind: "ignore" }
|
|
287
327
|
| { kind: "deleted"; messageIds: number[] }
|
|
@@ -307,15 +347,31 @@ export type TelegramUpdateExecutionPlan<
|
|
|
307
347
|
message: TMessage & { from: TelegramUser };
|
|
308
348
|
shouldPair: boolean;
|
|
309
349
|
shouldDeny: boolean;
|
|
350
|
+
}
|
|
351
|
+
| {
|
|
352
|
+
kind: "guest";
|
|
353
|
+
guestMessage: TGuestMessage & { from: TelegramUser };
|
|
354
|
+
shouldDeny: boolean;
|
|
310
355
|
};
|
|
311
356
|
|
|
312
357
|
export function buildTelegramUpdateExecutionPlan<
|
|
313
358
|
TReactionUpdate extends TelegramMessageReactionUpdated,
|
|
314
359
|
TCallbackQuery extends TelegramCallbackQuery,
|
|
315
360
|
TMessage extends TelegramUpdateMessage,
|
|
361
|
+
TGuestMessage extends TelegramGuestMessage,
|
|
316
362
|
>(
|
|
317
|
-
action: TelegramUpdateFlowAction<
|
|
318
|
-
|
|
363
|
+
action: TelegramUpdateFlowAction<
|
|
364
|
+
TReactionUpdate,
|
|
365
|
+
TCallbackQuery,
|
|
366
|
+
TMessage,
|
|
367
|
+
TGuestMessage
|
|
368
|
+
>,
|
|
369
|
+
): TelegramUpdateExecutionPlan<
|
|
370
|
+
TReactionUpdate,
|
|
371
|
+
TCallbackQuery,
|
|
372
|
+
TMessage,
|
|
373
|
+
TGuestMessage
|
|
374
|
+
> {
|
|
319
375
|
switch (action.kind) {
|
|
320
376
|
case "ignore":
|
|
321
377
|
return { kind: "ignore" };
|
|
@@ -345,6 +401,12 @@ export function buildTelegramUpdateExecutionPlan<
|
|
|
345
401
|
shouldPair: action.authorization.kind === "pair",
|
|
346
402
|
shouldDeny: action.authorization.kind === "deny",
|
|
347
403
|
};
|
|
404
|
+
case "guest":
|
|
405
|
+
return {
|
|
406
|
+
kind: "guest",
|
|
407
|
+
guestMessage: action.guestMessage,
|
|
408
|
+
shouldDeny: action.authorization.kind === "deny",
|
|
409
|
+
};
|
|
348
410
|
}
|
|
349
411
|
}
|
|
350
412
|
|
|
@@ -387,6 +449,7 @@ export interface TelegramUpdateRuntimeDeps<
|
|
|
387
449
|
callbackQueryId: string,
|
|
388
450
|
text?: string,
|
|
389
451
|
) => Promise<void>;
|
|
452
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
390
453
|
handleAuthorizedTelegramCallbackQuery: (
|
|
391
454
|
query: TCallbackQuery,
|
|
392
455
|
ctx: TContext,
|
|
@@ -404,6 +467,10 @@ export interface TelegramUpdateRuntimeDeps<
|
|
|
404
467
|
message: TMessage,
|
|
405
468
|
ctx: TContext,
|
|
406
469
|
) => unknown;
|
|
470
|
+
handleAuthorizedTelegramGuestMessage?: (
|
|
471
|
+
guestMessage: TelegramGuestMessage & { from: TelegramUser },
|
|
472
|
+
ctx: TContext,
|
|
473
|
+
) => Promise<void>;
|
|
407
474
|
}
|
|
408
475
|
|
|
409
476
|
export interface TelegramUpdateRuntimeControllerDeps<
|
|
@@ -431,6 +498,7 @@ export interface TelegramUpdateRuntimeControllerDeps<
|
|
|
431
498
|
callbackQueryId: string,
|
|
432
499
|
text?: string,
|
|
433
500
|
) => Promise<void>;
|
|
501
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
434
502
|
handleAuthorizedTelegramCallbackQuery: (
|
|
435
503
|
query: TCallbackQuery,
|
|
436
504
|
ctx: TContext,
|
|
@@ -448,6 +516,10 @@ export interface TelegramUpdateRuntimeControllerDeps<
|
|
|
448
516
|
message: TMessage,
|
|
449
517
|
ctx: TContext,
|
|
450
518
|
) => unknown;
|
|
519
|
+
handleAuthorizedTelegramGuestMessage?: (
|
|
520
|
+
guestMessage: TelegramGuestMessage & { from: TelegramUser },
|
|
521
|
+
ctx: TContext,
|
|
522
|
+
) => Promise<void>;
|
|
451
523
|
}
|
|
452
524
|
|
|
453
525
|
export interface TelegramUpdateRuntimeController<
|
|
@@ -536,12 +608,15 @@ export function createTelegramPairedUpdateRuntime<
|
|
|
536
608
|
updateStatus: deps.updateStatus,
|
|
537
609
|
}).pairIfNeeded,
|
|
538
610
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
611
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
539
612
|
handleAuthorizedTelegramCallbackQuery:
|
|
540
613
|
deps.handleAuthorizedTelegramCallbackQuery,
|
|
541
614
|
sendTextReply: deps.sendTextReply,
|
|
542
615
|
handleAuthorizedTelegramMessage: deps.handleAuthorizedTelegramMessage,
|
|
543
616
|
handleAuthorizedTelegramEditedMessage:
|
|
544
617
|
deps.handleAuthorizedTelegramEditedMessage,
|
|
618
|
+
handleAuthorizedTelegramGuestMessage:
|
|
619
|
+
deps.handleAuthorizedTelegramGuestMessage,
|
|
545
620
|
});
|
|
546
621
|
}
|
|
547
622
|
|
|
@@ -582,12 +657,15 @@ export function createTelegramUpdateRuntime<
|
|
|
582
657
|
handleAuthorizedTelegramReactionUpdate: handleAuthorizedReactionUpdate,
|
|
583
658
|
pairTelegramUserIfNeeded: deps.pairTelegramUserIfNeeded,
|
|
584
659
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
660
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
585
661
|
handleAuthorizedTelegramCallbackQuery:
|
|
586
662
|
deps.handleAuthorizedTelegramCallbackQuery,
|
|
587
663
|
sendTextReply: deps.sendTextReply,
|
|
588
664
|
handleAuthorizedTelegramMessage: deps.handleAuthorizedTelegramMessage,
|
|
589
665
|
handleAuthorizedTelegramEditedMessage:
|
|
590
666
|
deps.handleAuthorizedTelegramEditedMessage,
|
|
667
|
+
handleAuthorizedTelegramGuestMessage:
|
|
668
|
+
deps.handleAuthorizedTelegramGuestMessage,
|
|
591
669
|
}),
|
|
592
670
|
};
|
|
593
671
|
}
|
|
@@ -712,6 +790,22 @@ export async function executeTelegramUpdatePlan<
|
|
|
712
790
|
await deps.handleAuthorizedTelegramCallbackQuery(plan.query, deps.ctx);
|
|
713
791
|
return;
|
|
714
792
|
}
|
|
793
|
+
if (plan.kind === "guest") {
|
|
794
|
+
if (plan.shouldDeny) {
|
|
795
|
+
await deps.answerGuestQuery(
|
|
796
|
+
plan.guestMessage.guest_query_id,
|
|
797
|
+
"Access denied.",
|
|
798
|
+
);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (deps.handleAuthorizedTelegramGuestMessage) {
|
|
802
|
+
await deps.handleAuthorizedTelegramGuestMessage(
|
|
803
|
+
plan.guestMessage,
|
|
804
|
+
deps.ctx,
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
715
809
|
const pairedNow = plan.shouldPair
|
|
716
810
|
? await deps.pairTelegramUserIfNeeded(plan.message.from.id, deps.ctx)
|
|
717
811
|
: false;
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"private": false,
|
|
5
|
-
"
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "Telegram Runtime Adapter for π",
|
|
6
9
|
"type": "module",
|
|
7
10
|
"keywords": [
|
|
8
11
|
"pi-package",
|
|
@@ -20,6 +23,9 @@
|
|
|
20
23
|
"bugs": {
|
|
21
24
|
"url": "https://github.com/llblab/pi-telegram/issues"
|
|
22
25
|
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.0.0"
|
|
28
|
+
},
|
|
23
29
|
"scripts": {
|
|
24
30
|
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
25
31
|
"typecheck": "tsc --noEmit",
|
|
@@ -37,9 +43,6 @@
|
|
|
37
43
|
"docs/",
|
|
38
44
|
"screenshot.png"
|
|
39
45
|
],
|
|
40
|
-
"publishConfig": {
|
|
41
|
-
"access": "public"
|
|
42
|
-
},
|
|
43
46
|
"pi": {
|
|
44
47
|
"extensions": [
|
|
45
48
|
"./index.ts"
|
|
@@ -47,9 +50,9 @@
|
|
|
47
50
|
"image": "https://github.com/llblab/pi-telegram/raw/main/screenshot.png"
|
|
48
51
|
},
|
|
49
52
|
"peerDependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
+
"@earendil-works/pi-agent-core": "*",
|
|
54
|
+
"@earendil-works/pi-ai": "*",
|
|
55
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
53
56
|
"@sinclair/typebox": "*"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|