@@ -65,6 +65,8 @@ Every plugin must include a `manifest.json` at the root of its folder. This file
6565| ` permissions ` | array | ** Yes** | Empty array ` [] ` by default. Add ` "bridge" ` if the plugin uses ` context.bridge ` directly. |
6666| ` secrets ` | object | No | Secret declarations — ` { "key": { "required": bool, "description": string } } ` . Validated at load time. |
6767| ` tags ` | array | No | Categories for discovery (e.g. ` ["defi", "ton", "trading"] ` ). |
68+ | ` bot ` | object | No | Bot features: ` { inline?: bool, callbacks?: bool, rateLimits?: { inlinePerMinute?, callbackPerMinute? } } ` |
69+ | ` hooks ` | array | No | Hook declarations: ` [{ name: string, priority?: number, description?: string }] ` |
6870| ` repository ` | string | No | URL to the plugin's source repository. |
6971| ` funding ` | string\| null | No | Funding URL or ` null ` . |
7072
@@ -143,6 +145,8 @@ The `context` object is still available in `execute` — the SDK is an addition,
143145| Method | Returns | Throws |
144146| -------- | -------- - | -------- |
145147| ` getAddress()` | ` string \| null` — bot' s wallet address | — |
148+ | `getPublicKey()` | `string \| null` — hex ed25519 public key, null if wallet not loaded | — |
149+ | `getWalletVersion()` | `string` — always `"v5r1"` | — |
146150| `getBalance(address?)` | `{ balance, balanceNano } \| null` — defaults to bot' s wallet | — |
147151| ` getPrice()` | ` { usd, source, timestamp } \| null` — TON / USD price | — |
148152| ` sendTON(to, amount, comment?)` | ` { txRef, amount }` — irreversible transfer | ` WALLET_NOT_INITIALIZED` , ` INVALID_ADDRESS` , ` OPERATION_FAILED` |
@@ -151,46 +155,50 @@ The `context` object is still available in `execute` — the SDK is an addition,
151155| ` getJettonBalances(address?)` | ` JettonBalance[]` — all jetton balances | — |
152156| ` getJettonInfo(jettonAddress)` | ` JettonInfo \| null` — metadata, supply, holders | — |
153157| ` sendJetton(jettonAddress, to, amount, opts?)` | ` { success, seqno }` | ` WALLET_NOT_INITIALIZED` , ` INVALID_ADDRESS` , ` OPERATION_FAILED` |
158+ | ` createJettonTransfer(jettonAddress, to, amount, opts?)` | ` SignedTransfer` — signed TEP - 74 BOC without broadcasting | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
154159| ` getJettonWalletAddress(ownerAddress, jettonAddress)` | ` string \| null` | — |
155160| ` getNftItems(address?)` | ` NftItem[]` — NFTs owned by address | — |
156161| ` getNftInfo(nftAddress)` | ` NftItem \| null` — NFT metadata and collection | — |
157162| ` toNano(amount)` | ` bigint` — converts TON to nanoTON | — |
158163| ` fromNano(amount)` | ` string` — converts nanoTON to TON | — |
159164| ` validateAddress(address)` | ` boolean` — checks if a TON address is valid | — |
165+ | ` getJettonPrice(jettonAddress)` | ` JettonPrice \| null` — USD / TON price, 24h / 7d / 30d changes | — |
166+ | ` getJettonHolders(jettonAddress, limit?)` | ` JettonHolder[]` — top holders by balance (max 100 ) | — |
167+ | ` getJettonHistory(jettonAddress)` | ` JettonHistory \| null` — volume, FDV , market cap, holders | — |
160168
161- ** Jetton analytics : **
169+ Read methods return ` null ` or ` [] ` on failure . Write methods throw ` PluginSDKError ` .
162170
163- | Method | Returns | Throws |
164- | -------- | -------- - | -------- |
165- | ` getJettonPrice(jettonAddress)` | ` JettonPrice \| null` — USD / TON price + 24h / 7d / 30d changes | — |
166- | ` getJettonHolders(jettonAddress, limit?)` | ` JettonHolder[]` — top holders, max 100 | — |
167- | ` getJettonHistory(jettonAddress)` | ` JettonHistory \| null` — volume, FDV , market cap | — |
171+ ` SignedTransfer` shape: ` { signedBoc, walletPublicKey, walletAddress, seqno, validUntil }` . Deprecated aliases ` boc` , ` publicKey` , and ` walletVersion` are also present for backwards compatibility. ` opts` for ` createJettonTransfer` accepts ` { comment?: string }` .
172+
173+ ### sdk .ton .dex — DEX aggregator
168174
169- ** DEX — ` sdk.ton.dex ` : **
175+ Compare and execute swaps across STON . fi and DeDust :
170176
171177| Method | Returns | Throws |
172178| -------- | -------- - | -------- |
173- | ` quote({ fromAsset, toAsset, amount, slippage? })` | ` DexQuoteResult ` — compares STON . fi + DeDust | — |
174- | ` quoteSTONfi(params)` | ` DexSingleQuote \| null` | — |
175- | ` quoteDeDust(params)` | ` DexSingleQuote \| null` | — |
176- | ` swap({ fromAsset, toAsset, amount, slippage?, dex? })` | ` DexSwapResult` — auto - selects best DEX | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
177- | ` swapSTONfi(params)` | ` DexSwapResult` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
178- | ` swapDeDust(params)` | ` DexSwapResult` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
179+ | ` dex. quote({ fromAsset, toAsset, amount, slippage? })` | ` { stonfi, dedust, recommended, savings } ` | ` OPERATION_FAILED ` |
180+ | ` dex. quoteSTONfi(params)` | ` DexSingleQuote \| null` | — |
181+ | ` dex. quoteDeDust(params)` | ` DexSingleQuote \| null` | — |
182+ | ` dex. swap({ fromAsset, toAsset, amount, slippage?, dex? })` | ` DexSwapResult` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
183+ | ` dex. swapSTONfi(params)` | ` DexSwapResult` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
184+ | ` dex. swapDeDust(params)` | ` DexSwapResult` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
179185
180- ** DNS — ` sdk.ton.dns` : **
186+ Assets are jetton master addresses (or ` "TON"` for native TON ). Slippage defaults to DEX - specific value.
187+
188+ ### sdk .ton .dns — TON DNS domains
189+
190+ Manage ` .ton` domains — check availability, auctions, linking, and TON Site records:
181191
182192| Method | Returns | Throws |
183193| -------- | -------- - | -------- |
184- | ` check(domain)` | ` DnsCheckResult` — availability, owner, auction status | — |
185- | ` resolve(domain)` | ` DnsResolveResult \| null` — wallet address | — |
186- | ` getAuctions(limit?)` | ` DnsAuction[]` — active auctions | — |
187- | ` startAuction(domain)` | ` DnsAuctionResult` — ~ 0.06 TON min bid | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
188- | ` bid(domain, amount)` | ` DnsBidResult` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
189- | ` link(domain, address)` | ` void` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
190- | ` unlink(domain)` | ` void` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
191- | ` setSiteRecord(domain, adnlAddress)` | ` void` — set TON Site ADNL record | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
192-
193- Read methods return ` null` or ` []` on failure . Write methods throw ` PluginSDKError` .
194+ | ` dns.check(domain)` | ` { domain, available, owner?, nftAddress? }` | — |
195+ | ` dns.resolve(domain)` | ` { domain, walletAddress, nftAddress, owner } \| null` | — |
196+ | ` dns.getAuctions(limit?)` | ` DnsAuction[]` (max 100 ) | — |
197+ | ` dns.startAuction(domain)` | ` { domain, success, bidAmount }` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
198+ | ` dns.bid(domain, amount)` | ` { domain, bidAmount, success }` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
199+ | ` dns.link(domain, address)` | ` void` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
200+ | ` dns.unlink(domain)` | ` void` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
201+ | ` dns.setSiteRecord(domain, adnlAddress)` | ` void` | ` WALLET_NOT_INITIALIZED` , ` OPERATION_FAILED` |
194202
195203### sdk .telegram — Telegram messaging
196204
@@ -209,9 +217,11 @@ Read methods return `null` or `[]` on failure. Write methods throw `PluginSDKErr
209217| ` searchMessages(chatId, query, limit?)` | ` SimpleMessage[]` | — |
210218| ` getReplies(chatId, messageId, limit?)` | ` SimpleMessage[]` | — |
211219| ` scheduleMessage(chatId, text, scheduleDate)` | ` number` — message ID | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
212- | ` getScheduledMessages(chatId)` | ` SimpleMessage[]` | — |
213- | ` deleteScheduledMessage(chatId, messageId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
214- | ` sendScheduledNow(chatId, messageId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
220+ | ` getScheduledMessages(chatId)` | ` SimpleMessage[]` | — |
221+ | ` deleteScheduledMessage(chatId, messageId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
222+ | ` sendScheduledNow(chatId, messageId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
223+ | ` getDialogs(limit?)` | ` Dialog[]` — conversations (max 100 ) | — |
224+ | ` getHistory(chatId, limit?)` | ` SimpleMessage[]` — message history (max 100 ) | — |
215225| ` getMe()` | ` { id, username?, firstName?, isBot } \| null` | — |
216226| ` isAvailable()` | ` boolean` | — |
217227| ` getRawClient()` | GramJS ` TelegramClient \| null` — escape hatch | — |
@@ -241,10 +251,8 @@ Read methods return `null` or `[]` on failure. Write methods throw `PluginSDKErr
241251| ` createQuiz(chatId, question, answers, correctIndex, explanation?)` | ` number` — message ID | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
242252| ` banUser(chatId, userId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
243253| ` unbanUser(chatId, userId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
244- | ` muteUser(chatId, userId, untilDate)` | ` void` — untilDate is Unix timestamp, 0 = forever | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
254+ | ` muteUser(chatId, userId, untilDate)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
245255| ` kickUser(chatId, userId)` | ` void` — ban + immediate unban | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
246- | ` getDialogs(limit?)` | ` Dialog[]` — max 100 | — |
247- | ` getHistory(chatId, limit?)` | ` SimpleMessage[]` — max 100 | — |
248256
249257** Stars & gifts: **
250258
@@ -257,17 +265,12 @@ Read methods return `null` or `[]` on failure. Write methods throw `PluginSDKErr
257265| ` getResaleGifts(giftId, limit?)` | ` StarGift[]` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
258266| ` buyResaleGift(giftId)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
259267| ` getStarsTransactions(limit?)` | ` StarsTransaction[]` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
260-
261- ** Collectibles & NFT gifts: **
262-
263- | Method | Returns | Throws |
264- | -------- | -------- - | -------- |
265- | ` transferCollectible(msgId, toUserId)` | ` TransferResult` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
266- | ` setCollectiblePrice(msgId, price)` | ` void` — 0 to unlist | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
267- | ` getCollectibleInfo(slug)` | ` CollectibleInfo \| null` | — |
268- | ` getUniqueGift(slug)` | ` UniqueGift \| null` | — |
269- | ` getUniqueGiftValue(slug)` | ` GiftValue \| null` | — |
270- | ` sendGiftOffer(userId, giftSlug, price, opts?)` | ` void` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
268+ | ` transferCollectible(msgId, toUserId)` | ` TransferResult` — ` { msgId, transferredTo, paidTransfer }` | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
269+ | ` setCollectiblePrice(msgId, price)` | ` void` — set/ remove resale price | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
270+ | ` getCollectibleInfo(slug)` | ` CollectibleInfo \| null` — Fragment collectible info | — |
271+ | ` getUniqueGift(slug)` | ` UniqueGift \| null` — NFT gift details | — |
272+ | ` getUniqueGiftValue(slug)` | ` GiftValue \| null` — market valuation | — |
273+ | ` sendGiftOffer(userId, giftSlug, price, opts?)` | ` void` — make buy offer | ` BRIDGE_NOT_CONNECTED` , ` OPERATION_FAILED` |
271274
272275** Stories: **
273276
@@ -285,6 +288,51 @@ await sdk.telegram.sendMessage(chatId, "Pick one:", {
285288});
286289` ` `
287290
291+ ### sdk .bot — Inline bot integration
292+
293+ If your plugin uses a Telegram bot for inline queries or button callbacks, declare ` bot` in your manifest and use the Bot SDK :
294+
295+ ` ` ` json
296+ {
297+ "bot": { "inline": true, "callbacks": true, "rateLimits": { "inlinePerMinute": 30 } }
298+ }
299+ ` ` `
300+
301+ | Member | Type | Description |
302+ | -------- | ------ | ------------ - |
303+ | ` isAvailable` | ` boolean` (getter) | Whether the inline bot router is connected |
304+ | ` username` | ` string` (getter) | Bot username from Grammy |
305+ | ` onInlineQuery(handler)` | ` void` | Register inline query handler — ` handler(ctx) → InlineResult[]` |
306+ | ` onCallback(pattern, handler)` | ` void` | Register callback handler for glob pattern — ` handler(ctx) → void` |
307+ | ` onChosenResult(handler)` | ` void` | Register chosen result handler |
308+ | ` editInlineMessage(id, text, opts?)` | ` void` | Edit an inline message (opts: ` { keyboard?, parseMode? }` ) |
309+ | ` keyboard(rows: ButtonDef[][])` | ` BotKeyboard` | Create keyboard with ` .toGrammy()` and ` .toTL()` serializers |
310+
311+ ` sdk.bot` is ` null` if no bot is configured or manifest doesn' t declare `bot`.
312+
313+ ### sdk.on — Plugin hooks
314+
315+ Register hooks to observe or intercept agent events. Declare hooks in your manifest:
316+
317+ ```json
318+ {
319+ "hooks": [{ "name": "tool:after", "priority": 50, "description": "Audit tool calls" }]
320+ }
321+ ```
322+
323+ ```js
324+ export const tools = (sdk) => {
325+ sdk.on("tool:after", async (event) => {
326+ sdk.log.info(`Tool ${event.toolName} returned in ${event.durationMs}ms`);
327+ }, { priority: 50 });
328+ return [/* tools */];
329+ };
330+ ```
331+
332+ **13 hook types:** `tool:before`, `tool:after`, `tool:error`, `prompt:before`, `prompt:after`, `session:start`, `session:end`, `message:receive`, `response:before`, `response:after`, `response:error`, `agent:start`, `agent:stop`
333+
334+ **Priority:** negative = security gates, 0 = default, 50+ = audit/logging, 100+ = reserved.
335+
288336### sdk.db — Isolated database
289337
290338Each plugin gets its own SQLite database at `~/.teleton/plugins/data/{plugin-name}.db`. To enable it, export a `migrate` function:
@@ -391,50 +439,6 @@ sdk.log.error("failed"); // ❌ [my-plugin] failed
391439sdk.log.debug("details"); // 🔍 [my-plugin] details (only if DEBUG or VERBOSE env)
392440```
393441
394- ### sdk .bot — Inline mode
395-
396- Enables inline query and callback button handling . Requires ` bot` in manifest:
397-
398- ` ` ` js
399- export const manifest = {
400- name: "my-bot",
401- version: "1.0.0",
402- bot: { inline: true, callbacks: true, rateLimits: { inlinePerMinute: 30, callbackPerMinute: 60 } },
403- };
404- ` ` `
405-
406- ` sdk.bot` is ` null` unless the manifest declares ` bot` capabilities.
407-
408- | Property / Method | Returns | Description |
409- | ------------------ - | -------- - | ------------ - |
410- | ` isAvailable` | ` boolean` | Whether bot client is connected (getter) |
411- | ` username` | ` string` | Bot username (getter) |
412- | ` onInlineQuery(handler)` | ` void` | Register inline query handler |
413- | ` onCallback(pattern, handler)` | ` void` | Register callback handler (glob pattern) |
414- | ` onChosenResult(handler)` | ` void` | Handle chosen inline results |
415- | ` editInlineMessage(id, text, opts?)` | ` Promise<void>` | Edit inline message (GramJS → Grammy fallback) |
416- | ` keyboard(rows)` | ` BotKeyboard` | Build keyboard with auto- prefixed callbacks |
417-
418- ` ` ` js
419- sdk.bot.onInlineQuery(async (ctx) => {
420- return [{ id: "1", type: "article", title: ctx.query, content: { text: ctx.query } }];
421- });
422-
423- sdk.bot.onCallback("pick:*", async (ctx) => {
424- await ctx.answer("Selected!");
425- await ctx.editMessage("Done!");
426- });
427-
428- const kb = sdk.bot.keyboard([
429- [{ text: "Buy", callback: "buy", style: "success" }],
430- [{ text: "Cancel", callback: "cancel", style: "danger" }],
431- ]);
432- // kb.toTL() — GramJS (colored buttons)
433- // kb.toGrammy() — Grammy Bot API (standard buttons)
434- ` ` `
435-
436- Button styles: ` "success"` (green), ` "danger"` (red), ` "primary"` (blue) — GramJS only, graceful fallback.
437-
438442### Error handling with SDK
439443
440444SDK write methods throw `PluginSDKError` with a `.code` property:
0 commit comments