Agent extends StatefulActor, and every actor
carries a private, durable SQLite database at this.ctx.storage.sql. There is
nothing to declare in telnyx.toml — the database is created on first use, and it works
inside an agent exactly as it does on a plain actor.
Which tier holds the data?
An agent has three durable tiers, all in the same actor:
State is read and written as one value; history is read back in order. The moment you
want “the last five orders over $10” — a lookup neither tier answers without a scan —
put rows in SQL.
Using it from an agent
exec() is synchronous — the database is a local file in the actor’s own process —
with positional ? binds and a cursor you drain with toArray():
this.ctx.storage.transactionSync(() => { ... }) to
commit them atomically.
Patterns that come up in agents:
- Webhook dedup.
INSERTthe event id into a table with aUNIQUEconstraint beforequeue()-ing work; a thrown constraint violation means you already handled that event. - Tool-call ledger. Record each tool invocation and result as a row; answer “what did you do?” or audit questions with a query instead of replaying history.
- Searchable history.
this.messagesis an ordered log, not an index. If the agent needs keyword lookup over past conversation, append each message to an SQL table too — in the same method that callsmessages.add()— and query it withLIKEplus an index.
Semantics and limits
The full contract — cursor rules, multi-statement batches,transactionSync, binding
types, and the limits (1 GB per actor database, 2 MiB per bound value, integer range) —
is on the actor SQL guide. It
applies verbatim inside an Agent: the SDK reserves
alarm() for its scheduler, but all of
ctx.storage stays yours.
For data that more than one function or caller must query — shared across agents, or
read from the CLI and REST API — use a standalone
SQL Database instead; the embedded database is strictly
per-instance.