TypeScript
Package caspian. bun ≥ 1.3. Same program surface as Python — Caspian, rules, Thread — camelCase on the facade.
Install
bun add caspian-sdkApp code imports the facade. Do not import core from application code.
import { Caspian } from "caspian-sdk"
import type { Thread, Message, Action, OnMessageOptions } from "caspian-sdk"Caspian
const cx = new Caspian() // self-host; no Caspian key
const cx = new Caspian({ dispatch: true })
// hosted run() takes apiKey on the call, not the constructorcx.app | Inspectable App of rules. |
cx.channels.add(channel, options) | via: "hosted" | "self-host" is required. Snake_case option names match Python (bot_token); camelCase is accepted. inbound: false is send-only. |
cx.onMessage(options, handler) | Chainable. Also onMessage(handler). |
cx.onAction(options, handler) | Button / callback. Default overlap is drop. |
cx.use({ predicate, overlap?, ack? }, handler) | Raw core rule. Missing overlap → queue / 16. |
cx.tools(thread?, { preset }) | "messenger" or "outbound". |
await cx.interpret({ channel? }) | Memory interpreter, no HTTP. |
await cx.handle(channel, body, headers) | Self-host webhook body. |
await cx.poll(channel, options) | Long-poll (Telegram). Same pipeline as handle. |
await cx.listen(channel, { maxEvents? }) | Discord or Slack Socket Mode. Treat as the end of main. |
await cx.run({ apiKey, intervalMs?, maxIterations?, replay? }) | Hosted: poll /v1/events. replay re-reads from seq 0 (off by default). |
cx.webhooks.telegram(req) / cx.webhooks.caspian(req) | After bind / hosted bind. Caspian path checks X-Caspian-Signature. |
import { Caspian } from "caspian-sdk"
const cx = new Caspian()
await cx.channels.add("telegram", {
via: "self-host",
bot_token: process.env.TELEGRAM_BOT_TOKEN!,
})
cx.onMessage(
{ channel: "telegram", kind: "dm", overlap: "queue" },
async (thread, msg, { skipped }) => {
await thread.typing()
await thread.post(`echo:${msg.text}`)
},
)Handlers
(thread, msg, ctx) / (thread, action, ctx). ctx.skipped is the burst overlap collapsed before this turn. See Overlap.
Options: channel, kind, command, overlap (queue / debounce / drop / parallel — no stream yet), bound, ack. onAction adds data. Default overlap: messages queue, actions drop.
Thread
Same commands as Python, camelCase. Snake_case aliases exist (send_blocks, send_media, mark_read).
await thread.post(text, { actions })
await thread.send(text, { actions }) // not a reply to this turn
await thread.reply(replyTo, text, { actions })
await thread.sendBlocks(blocks, { text, actions })
await thread.sendMedia(attachment, { caption })
await thread.typing()
await thread.edit(messageId, text, { actions })
await thread.delete(messageId)
await thread.react(messageId, emoji)
await thread.pin(messageId) / unpin(messageId)
await thread.forward(toThreadId, messageId)
await thread.markRead(messageId?)
await thread.initiate(text, { actions })
await thread.schedule(text, sendAt, { actions })
await thread.history({ limit, before })
await thread.subscribe()
const stream = thread.stream({ minChars: 24, throttle: 0.5 })
await stream.append(chunk)
await thread.recent(n)
await thread.state.get(key)
await thread.state.set(key, value)thread.recent and thread.state are runner memory. A thread with no store returns [] / undefined.
Adapters
Import the pack when you need the driver, not only the facade:
import { telegram, telegramHttpLayer } from "caspian-sdk/telegram"
import { discord, discordHttpLayer } from "caspian-sdk/discord"
import { slack, slackHttpLayer } from "caspian-sdk/slack"Also: caspian/voice, caspian/email, caspian/sms, caspian/whatsapp, caspian/messenger, caspian/imessage, caspian/x, caspian/linear.
Errors
ProvisionError on paperwork. handle / listen / poll / run collect per-event HandleResult. Also AdapterError, DecodeError.