{"_id":"@artyomspace/pi-telegram-connect","_rev":"4-6ec9753d5021fd20514effe0e3ab066e","name":"@artyomspace/pi-telegram-connect","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@artyomspace/pi-telegram-connect","version":"0.1.0","keywords":["pi-cli","pi-coding-agent","pi-extension","pi-package","telegram","telegram-bot","grammy","ai-agent","llm","extension"],"author":{"name":"artyomspace"},"license":"MIT","_id":"@artyomspace/pi-telegram-connect@0.1.0","maintainers":[{"name":"artyomspace","email":"i@artyom.space"}],"homepage":"https://github.com/artyom-ivanov/pi-telegram-connect#readme","bugs":{"url":"https://github.com/artyom-ivanov/pi-telegram-connect/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"0fb20f1d13543de647df82f02f475809f7fcf832","tarball":"https://registry.npmjs.org/@artyomspace/pi-telegram-connect/-/pi-telegram-connect-0.1.0.tgz","fileCount":72,"integrity":"sha512-fwR6x9lzcm2c7P0MwAiqd3wEL7dfcKhH72/+TMXpMSHzMun/qQnL8fAVNY5J9OzM5yUiEvuRILqTnK6GvCmAtw==","signatures":[{"sig":"MEQCIAcMq08G5IirgHT9vz7D5zBxbWpY1+D3Ep09RVwcV1+1AiBPPA4yF52ocQDSZiWiL7fbmUqTDr6uCl+zA4kDQykAuA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":194375},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.6.0"},"exports":{".":"./dist/index.js"},"gitHead":"ddc89da27504b8a6482d4798cafb68f018cac264","scripts":{"lint":"eslint 'src/**/*.ts' 'test/**/*.ts'","test":"vitest run","build":"tsc -p tsconfig.json","prebuild":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build"},"_npmUser":{"name":"artyomspace","email":"i@artyom.space"},"repository":{"url":"git+https://github.com/artyom-ivanov/pi-telegram-connect.git","type":"git"},"_npmVersion":"10.9.3","description":"Telegram bridge for the Pi coding agent: pair once, then chat with your local pi-CLI agent from your phone.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"grammy":"^1.30.0","marked":"^14.1.0","proper-lockfile":"^4.1.2","@grammyjs/runner":"^2.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@sinclair/typebox":"^0.32.0","@mariozechner/pi-ai":"*","@types/proper-lockfile":"^4.1.4","@typescript-eslint/parser":"^7.18.0","@mariozechner/pi-coding-agent":"*","@typescript-eslint/eslint-plugin":"^7.18.0"},"peerDependencies":{"@sinclair/typebox":"*","@mariozechner/pi-ai":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-telegram-connect_0.1.0_1777813318276_0.8140812756079328","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@artyomspace/pi-telegram-connect","version":"0.2.0","keywords":["pi-cli","pi-coding-agent","pi-extension","pi-package","telegram","telegram-bot","grammy","ai-agent","llm","extension"],"author":{"name":"artyomspace"},"license":"MIT","_id":"@artyomspace/pi-telegram-connect@0.2.0","maintainers":[{"name":"artyomspace","email":"i@artyom.space"}],"homepage":"https://github.com/artyom-ivanov/pi-telegram-connect#readme","bugs":{"url":"https://github.com/artyom-ivanov/pi-telegram-connect/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"5ee065584be69be8e243519ab44e959c1f99b827","tarball":"https://registry.npmjs.org/@artyomspace/pi-telegram-connect/-/pi-telegram-connect-0.2.0.tgz","fileCount":72,"integrity":"sha512-Vsb4RXoohjuNvmZ+Nb7DG7fxfvVQD7nSjvMO0CtCYQ9/ARoX3uK9V0aQzc6yyOCLRP7TId+Vx6AZgx2d0ElkPw==","signatures":[{"sig":"MEYCIQCMnmrcuK6gObSfRUUHSLYPEyoPxoLOyu3nIrzCWApLLgIhAL5iQQfr+fSF5vSpsLu2Z1Hj04jTtRobaarpLVBdHK1Y","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":194667},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.6.0"},"exports":{".":"./dist/index.js"},"gitHead":"89d2dda2f16412eca79d8e011e7e986938577fbc","scripts":{"lint":"eslint 'src/**/*.ts' 'test/**/*.ts'","test":"vitest run","build":"tsc -p tsconfig.json","prebuild":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build"},"_npmUser":{"name":"artyomspace","email":"i@artyom.space"},"repository":{"url":"git+https://github.com/artyom-ivanov/pi-telegram-connect.git","type":"git"},"_npmVersion":"10.9.3","description":"Telegram bridge for the Pi coding agent: pair once, then chat with your local pi-CLI agent from your phone.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"grammy":"^1.30.0","marked":"^14.1.0","proper-lockfile":"^4.1.2","@grammyjs/runner":"^2.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@sinclair/typebox":"^0.32.0","@earendil-works/pi-ai":"*","@types/proper-lockfile":"^4.1.4","@typescript-eslint/parser":"^7.18.0","@earendil-works/pi-coding-agent":"*","@typescript-eslint/eslint-plugin":"^7.18.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-telegram-connect_0.2.0_1779552311180_0.04962159116440623","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@artyomspace/pi-telegram-connect","version":"0.3.0","keywords":["pi-cli","pi-coding-agent","pi-extension","pi-package","telegram","telegram-bot","grammy","ai-agent","llm","extension"],"author":{"name":"artyomspace"},"license":"MIT","_id":"@artyomspace/pi-telegram-connect@0.3.0","maintainers":[{"name":"artyomspace","email":"i@artyom.space"}],"homepage":"https://github.com/artyom-ivanov/pi-telegram-connect#readme","bugs":{"url":"https://github.com/artyom-ivanov/pi-telegram-connect/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"d3680f1e6dd0004128eef738efd760dbd149a364","tarball":"https://registry.npmjs.org/@artyomspace/pi-telegram-connect/-/pi-telegram-connect-0.3.0.tgz","fileCount":72,"integrity":"sha512-u89vrbixgQKzzRUov1cvHVLapLPA3zIxdBxAeS+80F6zHkCzeAhNn6Jc4AkL560q40NOJUOdcdbLsRKaPqULHw==","signatures":[{"sig":"MEUCIFwt96YnTsWKkqAJbBxJYI99otRBa7O0uOH9CjdaNu1ZAiEA+tZ3XAmO2jH3YYY1Onxc2XqimecKIF2dVT2EdC9mjSE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":199176},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.6.0"},"exports":{".":"./dist/index.js"},"gitHead":"72da7559d112842f4b4bad97b8d273105c61e5cc","scripts":{"lint":"eslint 'src/**/*.ts' 'test/**/*.ts'","test":"vitest run","build":"tsc -p tsconfig.json","prebuild":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build"},"_npmUser":{"name":"artyomspace","email":"i@artyom.space"},"repository":{"url":"git+https://github.com/artyom-ivanov/pi-telegram-connect.git","type":"git"},"_npmVersion":"10.9.3","description":"Telegram bridge for the Pi coding agent: pair once, then chat with your local pi-CLI agent from your phone.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"grammy":"^1.30.0","marked":"^14.1.0","proper-lockfile":"^4.1.2","@grammyjs/runner":"^2.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@sinclair/typebox":"^0.32.0","@earendil-works/pi-ai":"*","@types/proper-lockfile":"^4.1.4","@typescript-eslint/parser":"^7.18.0","@earendil-works/pi-coding-agent":"*","@typescript-eslint/eslint-plugin":"^7.18.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-telegram-connect_0.3.0_1779626072887_0.903961351221678","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@artyomspace/pi-telegram-connect","version":"0.4.0","description":"Telegram bridge for the Pi coding agent: pair once, then chat with your local pi-CLI agent from your phone.","license":"MIT","author":{"name":"artyomspace"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":"./dist/index.js"},"engines":{"node":">=20.6.0"},"keywords":["pi-cli","pi-coding-agent","pi-extension","pi-package","telegram","telegram-bot","grammy","ai-agent","llm","extension"],"repository":{"type":"git","url":"git+https://github.com/artyom-ivanov/pi-telegram-connect.git"},"homepage":"https://github.com/artyom-ivanov/pi-telegram-connect#readme","bugs":{"url":"https://github.com/artyom-ivanov/pi-telegram-connect/issues"},"pi":{"extensions":["./dist/index.js"]},"scripts":{"prebuild":"rm -rf dist","build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","lint":"eslint 'src/**/*.ts' 'test/**/*.ts'","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build"},"dependencies":{"grammy":"^1.30.0","@grammyjs/runner":"^2.0.3","marked":"^14.1.0","proper-lockfile":"^4.1.2"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*","@earendil-works/pi-ai":"*","@sinclair/typebox":"*"},"devDependencies":{"@types/node":"^20.14.0","@types/proper-lockfile":"^4.1.4","@typescript-eslint/eslint-plugin":"^7.18.0","@typescript-eslint/parser":"^7.18.0","eslint":"^8.57.0","typescript":"^5.5.0","vitest":"^2.0.0","@earendil-works/pi-coding-agent":"*","@earendil-works/pi-ai":"*","@sinclair/typebox":"^0.32.0"},"_id":"@artyomspace/pi-telegram-connect@0.4.0","gitHead":"fef9688b97c61a536e2a25e1906db749bb69fc07","_nodeVersion":"22.19.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-V4BIx0jI1BdEPlg1knY2R1k/k3ZikaQS7BuFeUVYeHo3W9abXQdfjxz+IOAcCBZMC6IoU2HijyMlwNAsp4VOgg==","shasum":"2c671a76118e9b46855252908915c38469e87f4f","tarball":"https://registry.npmjs.org/@artyomspace/pi-telegram-connect/-/pi-telegram-connect-0.4.0.tgz","fileCount":76,"unpackedSize":208040,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCTfELpKda0wyu2jjpbfik3ZYmNxkWFBxPWEXaPLy+OjQIgY9R6DArsurbdnAzhC9hc/yqK/qGMesNZ5Rkr8Ppwf1U="}]},"_npmUser":{"name":"artyomspace","email":"i@artyom.space"},"directories":{},"maintainers":[{"name":"artyomspace","email":"i@artyom.space"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-telegram-connect_0.4.0_1779630125127_0.6901089518930519"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-03T13:01:58.109Z","modified":"2026-05-24T13:42:05.381Z","0.1.0":"2026-05-03T13:01:58.422Z","0.2.0":"2026-05-23T16:05:11.328Z","0.3.0":"2026-05-24T12:34:33.073Z","0.4.0":"2026-05-24T13:42:05.276Z"},"bugs":{"url":"https://github.com/artyom-ivanov/pi-telegram-connect/issues"},"author":{"name":"artyomspace"},"license":"MIT","homepage":"https://github.com/artyom-ivanov/pi-telegram-connect#readme","keywords":["pi-cli","pi-coding-agent","pi-extension","pi-package","telegram","telegram-bot","grammy","ai-agent","llm","extension"],"repository":{"type":"git","url":"git+https://github.com/artyom-ivanov/pi-telegram-connect.git"},"description":"Telegram bridge for the Pi coding agent: pair once, then chat with your local pi-CLI agent from your phone.","maintainers":[{"name":"artyomspace","email":"i@artyom.space"}],"readme":"# pi-telegram-connect\n\nA `pi`-CLI extension that bridges the [Pi coding agent](https://github.com/badlogic/pi-mono) to Telegram. Personal-use, single-owner: pair once, then chat with your local agent from Telegram.\n\n## Features\n\n- **Single-owner Telegram DM bridge** for one local `pi` session. Groups, supergroups, and channels are intentionally ignored.\n- **Real-time streaming** via `editMessageText` with a periodic throttle (about 3 seconds), plus a final flush at turn end.\n- **Typing indicator** while the agent is working.\n- **Tool progress preview**: before the first text delta, Telegram shows a `_Working..._` header with running/completed tool calls. The final tool footer is optional via `showToolFooter`.\n- **Markdown formatting** rendered as Telegram HTML (bold, italic, code, pre, links, blockquote, spoilers where supported); plaintext fallback if Telegram rejects the HTML.\n- **Long-message split**: replies over Telegram's message limit continue in new messages at safe boundaries.\n- **Silent reaction handling**: user reactions are forwarded to the agent as synthetic prompts; the prompt instructs the agent to answer with `[[skip]]` unless the reaction clearly asks for follow-up.\n- **Owner pairing** via 6-character alphanumeric code with 5-minute validity, 5-attempt lockout, and constant-time compare.\n- **Token validation** with Telegram `getMe` before pairing, so bad bot tokens fail immediately.\n- **Inbound media**: photos, voice messages, audio files, video, documents, and static stickers are downloaded to a sandboxed temp dir. Photos and first-time static stickers are also passed to the agent as image content.\n- **Voice vs audio distinction**: voice means a Telegram mic recording (Ogg/Opus); audio means an uploaded audio/music file. The agent is told they are different.\n- **Outbound media** via `telegram_attach`, auto-classified by extension as photo, video, voice, audio, or document.\n- **Sticker echo** via `telegram_send_sticker` for stickers the owner previously sent.\n- **Sticker cache** at `~/.pi/agent/telegram-connect-stickers.json`; it is cleared automatically when the bot token changes and can be reset manually.\n- **Reactions** via `telegram_react`, useful for lightweight acknowledgement while a long reply is still running.\n- **`/stop`** aborts the current Telegram turn and calls `ctx.abort()` on the pi side. **`/new`** starts a fresh pi session from Telegram (`/reset` accepted as an alias).\n\n## Install\n\n```bash\ngit clone https://github.com/artyom-ivanov/pi-telegram-connect\ncd pi-telegram-connect\nnpm install\nnpm run build\n\npi install /absolute/path/to/pi-telegram-connect\n```\n\n`pi` discovers extensions through its own settings (`~/.pi/agent/settings.json#packages`), not via npm globals. Use `pi install <path>` to register a local checkout, or `pi install npm:@artyomspace/pi-telegram-connect` once the package is published.\n\n## Quick Start\n\n1. Create a bot via [@BotFather](https://t.me/BotFather) and copy its token.\n2. In `pi`, start the bridge:\n\n   ```text\n   > /telegram-connect 123:ABC...\n   Bot started. DM this code to claim ownership: aB7xK3 (expires in 5 min).\n   ```\n\n3. DM the bot with the printed code. The bot replies: `✅ Ownership confirmed. Use /help to see commands.`\n4. Chat with it like you would in pi-CLI. The agent keeps its normal pi tools and, during Telegram-originated turns, also gets Telegram-specific tools.\n\nAfter a `pi` restart, run `/telegram-connect` with no args. It reuses the stored token and owner.\n\nTo skip pairing when you already know your numeric Telegram user ID:\n\n```text\n> /telegram-connect 123:ABC... --owner 840273\n```\n\n## Commands\n\nCLI commands inside `pi`:\n\n| Command | Description |\n| --- | --- |\n| `/telegram-connect [<token>] [--owner <user_id>]` | Start the bot. With no token, reuses stored config. With `--owner`, skips pairing. |\n| `/telegram-disconnect` | Stop the bot. Config and sticker cache are preserved. |\n| `/telegram-status` | Show whether the bot is running and the current owner. |\n| `/telegram-reset-stickers-cache` | Wipe the sticker cache; stickers are re-learned next time the owner sends them. |\n\nBot DM commands, owner only:\n\n| Command | Description |\n| --- | --- |\n| `/stop` | Abort the active turn and clear queued Telegram messages. |\n| `/new` | Start a fresh pi session (equivalent to `/new` in pi-CLI). Aborts the active turn first. |\n| `/reset` | Alias for `/new`. |\n\n## Agent Tools\n\nThese tools are registered with `pi.registerTool`, but they only work while the current turn came from Telegram:\n\n- **`telegram_attach({ paths: string[] })`**: queue up to 10 local files for delivery after the assistant's text reply finalizes. Paths must resolve under the current `pi` working directory or `~/.pi/agent/tmp/`. Type is inferred from extension: `.jpg`/`.jpeg`/`.png`/`.webp`/`.gif` as photo, `.mp4`/`.mov`/`.m4v` as video, `.ogg` as voice, `.mp3`/`.m4a`/`.flac`/`.wav` as audio, everything else as document.\n- **`telegram_send_sticker({ stickerId })`**: queue a previously seen sticker by `file_unique_id`. The ID appears in sticker markers injected into earlier Telegram prompts.\n- **`telegram_react({ emoji, messageId? })`**: set or clear one reaction. Omitting `messageId` targets the owner message that triggered the current turn. Pass `\"\"` to clear.\n\nPlain-text file paths in the assistant reply do not send files. The agent must save the artifact under an allowed root and call `telegram_attach`.\n\n## Configuration\n\nMain config is stored at `~/.pi/agent/telegram-connect.json` with mode `0600`.\n\n```jsonc\n{\n  \"version\": 2,\n  \"botToken\": \"123:ABC...\",\n  \"owner\": 840273,\n  \"pendingPairCode\": null,\n  \"showToolFooter\": false,\n  \"limits\": {\n    \"maxIncomingFileMb\": 20, // Telegram cloud Bot API getFile cap\n    \"maxOutgoingFileMb\": 50, // sendDocument cap used as the package-level upload limit\n    \"maxQueueDepth\": 32 // single global FIFO queue depth\n  }\n}\n```\n\nSticker cache is stored separately at `~/.pi/agent/telegram-connect-stickers.json`.\n\nInbound temp files land under:\n\n```text\n~/.pi/agent/tmp/telegram/<chat_id>/<thread_id>/<msg_id>-<filename>\n```\n\nFilenames are sanitized to `[A-Za-z0-9._-]`, reduced to a basename, capped at 80 characters, and verified to stay inside the temp root.\n\nA v1 to v2 migration runs automatically on first load. It preserves `botToken`, `owner`, and `pendingPairCode`, then drops old allowlist, group, session, and dead limit fields. Existing v2 configs missing new fields are filled from defaults.\n\n## Runtime Flow\n\n```text\nTelegram message\n  -> AccessControl (private chat + owner or pairing)\n  -> MessageQueue (single global FIFO)\n  -> MediaIngest (download to ~/.pi/agent/tmp/telegram/...)\n  -> StickerCache (lookup or insert static stickers)\n  -> pi.sendUserMessage(text or text + images)\n  -> pi message/tool events\n  -> Streamer (typing, working header, throttled edits, final flush)\n  -> queued attachments/stickers\n```\n\nThe extension injects a Telegram-specific system-prompt suffix only when processing a Telegram-originated turn. Plain pi-CLI prompts do not get Telegram tool instructions.\n\n## Limitations\n\n- **Personal-use only:** one bot, one owner, one local `pi` session.\n- **No group support:** groups, supergroups, and channels are silently dropped.\n- **Long polling only:** no webhook mode.\n- **No built-in transcription:** voice/audio/video files are saved locally and described in the prompt; the agent must inspect or transcribe them with its normal tools.\n- **Cloud Bot API file limits:** inbound files above `maxIncomingFileMb` are skipped; outbound files above `maxOutgoingFileMb` are refused before upload.\n- **Telegram-specific media limits still apply:** for example, photos have stricter limits than documents.\n- **Outbound path sandbox:** files must be under `process.cwd()` or `~/.pi/agent/tmp/`.\n- **Video and Lottie stickers:** emoji-only hints; they are not downloaded or passed as image content.\n- **Reactions:** Telegram accepts only its supported reaction emoji palette. Unsupported emoji produce a Telegram API error surfaced to the agent.\n\n## Development\n\n```bash\nnpm run typecheck\nnpm run lint\nnpm test\nnpm run build\n```\n\n`npm run prepublishOnly` runs the release gate: typecheck, lint, tests, and build.\n\nFor manual bot testing, see `scripts/e2e-manual.md`.\n\n## Release Checklist\n\n1. Update `CHANGELOG.md` and `package.json` version.\n2. Run `npm run prepublishOnly`.\n3. Inspect the packed artifact:\n\n   ```bash\n   npm pack --dry-run\n   ```\n\n4. Publish when the artifact contains only `dist`, `README.md`, `LICENSE`, `CHANGELOG.md`, and package metadata.\n\n## License\n\nMIT.\n","readmeFilename":"README.md"}