Skip to content

Commit 1f5dc74

Browse files
committed
docs: sync SDK docs with agent source + fix numeric validation
- Add 3 missing sdk.ton methods: createJettonTransfer, getPublicKey, getWalletVersion - Add SignedTransfer type documentation - Add sdk.bot and sdk.on sections to SKILL.md - Fix masking count 5 → 10 to match MASKING_KEEP_RECENT_COUNT - Fix isNaN → Number.isFinite in dedust, stonfi, swapcoffee, multisend
1 parent fb649f4 commit 1f5dc74

6 files changed

Lines changed: 857 additions & 232 deletions

File tree

CONTRIBUTING.md

Lines changed: 89 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -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.dexDEX 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.dnsTON 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
290338
Each 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
391439
sdk.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
440444
SDK write methods throw `PluginSDKError` with a `.code` property:

0 commit comments

Comments
 (0)