This repository accepts plugins that comply with the Teleton SDK v2 capability model. A plugin is not marketplace-ready merely because its JavaScript imports successfully: its manifest, tool contract, dependency tree and security boundaries must all pass the repository validators.
- Node
^22.22.2,^24.15.0, or>=26.0.0. - ESM JavaScript only.
index.js,manifest.jsonandREADME.mdin each plugin directory.package-lock.jsonwheneverpackage.jsonexists.- Strict semver versions.
scopeandcategoryon every tool.- Public SDK capabilities only.
plugins/my-plugin/
├── index.js
├── manifest.json
├── README.md
├── package.json # optional
└── package-lock.json # required with package.json
SDK plugins must export a runtime manifest from index.js:
export const manifest = {
name: "my-plugin",
version: "1.0.0",
sdkVersion: "^2.0.0",
description: "One concise sentence",
defaultConfig: {},
secrets: {
api_key: {
required: false,
description: "API key for the upstream service",
},
},
};The runtime and disk manifests must declare the same version and SDK range. A plugin that does not
use the SDK may omit sdkVersion in both places.
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "One concise sentence",
"author": { "name": "author", "url": "https://github.com/author" },
"license": "MIT",
"entry": "index.js",
"teleton": ">=0.9.0",
"sdkVersion": "^2.0.0",
"tools": [
{ "name": "my_plugin_lookup", "description": "Look up an item" }
],
"permissions": [],
"tags": ["utility"]
}The directory name, manifest id, runtime manifest name, compatibility entry and registry ID must
match.
export const tools = (sdk) => [
{
name: "my_plugin_lookup",
description: "Look up an item without changing external state",
scope: "always",
category: "data-bearing",
parameters: {
type: "object",
properties: {
query: { type: "string", description: "Item to look up" },
},
required: ["query"],
},
async execute(params) {
try {
const response = await fetch(
`https://api.example.com/items?q=${encodeURIComponent(params.query)}`,
{ signal: AbortSignal.timeout(15_000) }
);
if (!response.ok) return { success: false, error: `Upstream returned ${response.status}` };
return { success: true, data: await response.json() };
} catch (error) {
return { success: false, error: String(error?.message ?? error).slice(0, 500) };
}
},
},
];Rules:
- Tool names match
^[a-z][a-z0-9_]{0,63}$and are globally unique. scopeis one of the SDK v2 values:open,always,dm-only,group-only,admin-only,allowlist, ordisabled.categoryisdata-bearingfor reads oractionfor side effects.- Parameters use JSON Schema and bound arrays, strings, amounts and result counts.
- Network calls use explicit timeouts and bounded response handling.
- Returned errors are concise and do not include credentials or large upstream bodies.
- Actions must remain safe under retry and require Teleton's explicit approval flow.
Use only APIs declared by @teleton-agent/sdk@^2. The canonical reference is the
SDK package README.
Forbidden:
sdk.telegram.getRawClient();context.bridge,ctx.bridge, or private bridge/client traversal;- direct imports of Teleton's private runtime modules;
- direct reads of
wallet.json, wallet mnemonics or authentication stores; - arbitrary filesystem access to Teleton state;
- secrets from global environment variables.
Use instead:
sdk.telegramfor supported Telegram operations;sdk.tonfor supported wallet and TON operations;sdk.secretsfor declared credentials;sdk.storageor the isolatedsdk.dbfor state;sdk.pluginConfigfor plugin-specific configuration;sdk.logfor structured logs.
If the public SDK lacks a required capability, do not bypass it. Mark the plugin quarantined in
compatibility.json and keep it out of registry.json until a safe capability exists.
Optional exports are migrate, start, stop, onMessage, and onCallbackQuery.
migrate(db)receives only the plugin's isolated database.start(ctx)receivessdk, isolateddb, sanitizedconfig,pluginConfig, andlog.- Raw bridge access is not available.
- Cleanup timers and resources in
stop().
Declare secrets in the runtime manifest and read them through sdk.secrets:
const token = sdk.secrets.get("api_key");
if (!token) return { success: false, error: "API key is not configured" };Never commit credentials, log them, include them in tool results, or read unrelated process environment variables.
Plugin dependencies are installed with lifecycle scripts disabled:
npm ci --ignore-scripts --no-audit --no-fundCommit the generated lockfile. HIGH or CRITICAL audit findings block CI, including for quarantined plugins retained in the repository.
Every plugin has exactly one entry in compatibility.json:
supported: validated against SDK v2;quarantined: preserved but deliberately incompatible with SDK v2;marketplace: true: allowed only for supported production plugins.
registry.json is the generated installable marketplace, not an archive. Examples and quarantined
plugins do not belong there. Never edit it or the generated README catalog sections manually; after
changing a manifest or compatibility.json, run:
npm run generateWith teleton-agent checked out as ../teleton-agent:
npm ci --ignore-scripts
npm run install:plugins
npm run validate
npm test
npm --prefix ../teleton-agent run build:sdk
npm run validate:runtime
npm run audit:pluginsCI repeats these checks using the current teleton-agent dev branch on Node 22 and Node 24.
- Plugin ID and tool names are globally unique.
- Runtime and disk manifests agree.
- Every tool has a valid scope, category and bounded schema.
- No raw Telegram client, bridge, wallet file or mnemonic access.
- Secrets are declared and use
sdk.secrets. - Dependency lockfile is committed and audit is clean.
- README documents configuration, tools and side effects.
-
compatibility.jsonis updated. -
npm run generateupdated the generated registry and README sections. - All required local checks pass.