UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

166 lines • 10.8 kB
import type { LucidRawRunner } from './lucid.js'; /** * The twelve agent table names. They match the cross-adapter snake_case contract the reference Drizzle * store uses, so a dashboard or migration can point at any adapter and see the same physical schema. */ export declare const AGENT_TABLES: { /** Independent action proposals, including durable execution work and its lease. */ readonly actionProposals: 'agent_action_proposal'; readonly threads: 'agent_thread'; readonly messages: 'agent_message'; readonly toolCalls: 'agent_tool_call'; readonly tokenUsage: 'agent_token_usage'; readonly modelPricing: 'agent_model_pricing'; readonly runs: 'agent_run'; /** Messages sent while a turn was running, waiting to run after it (the chat queue). */ readonly queuedMessages: 'agent_queued_message'; /** Confirm tokens already spent, by hash (`LucidConfirmTokenStore`, single-use confirmed writes). */ readonly confirmTokens: 'agent_confirm_token'; /** A run's live stream, buffered for replay by the Lucid token sink (`tokenSinks.lucid()`). */ readonly streamFrames: 'agent_stream_frame'; /** * Text channels' short-lived state (`lucidChannelStore`): the provider message ids already taken, * questions waiting for an answer, outcomes already relayed — each row expiring at `expires_at`. */ readonly channelState: 'agent_channel_state'; /** * Generative-UI telemetry: one row per UI tool call (`recordGenuiCalls`) — metadata only, the * call's input only when its tool samples payloads. Read by the dashboard's Generative UI page. */ readonly genuiCalls: 'agent_genui_call'; }; /** * One statement of this module, as `db`'s dialect has to read it. The statements are written once, * Postgres/SQLite-style; MySQL needs four things changed, and every one of them used to fail or * silently misbehave there: * * - identifiers in backticks — a `"quoted"` name is a string literal to MySQL, so every statement * failed to parse; * - `TEXT` as `LONGTEXT` — MySQL's `TEXT` holds 64 KB, and a long answer, a tool's output or a stream * frame past that failed the insert; * - no `IF NOT EXISTS` on `CREATE INDEX`, which MySQL does not have (the catalog is read first, see * {@link existingIndexes}); * - InnoDB with a case-sensitive collation: the foreign keys need InnoDB, and under MySQL's default * `utf8mb4_0900_ai_ci` an actor `alice` read the threads of an actor `ALICE`. Postgres and SQLite * compare those exactly; `utf8mb4_bin` makes MySQL do the same. */ export declare function forDialect(statement: string, mysql: boolean): string; /** * `CREATE TABLE IF NOT EXISTS` for the Lucid token sink's frame buffer, under `table` (the sink takes * a `tableName`). One row per frame of a run's stream, numbered from 1 in write order; the row whose * `frame` is `NULL` is the end marker. `created_at` is what the sink's TTL purge reads. No foreign * key: a sink is usable with any store, and a delegated run writes under its ANCESTOR's run id. */ export declare function streamFrameTableStatement(table?: string): string; /** * `CREATE TABLE IF NOT EXISTS` for text channels' state (`lucidChannelStore`), under `table`. The * primary key IS the dedupe lock: two deliveries of one message race on the insert and one loses. * No foreign key: the rows name provider ids, not agent rows. */ export declare function channelStateTableStatements(table?: string): string[]; /** * `CREATE TABLE IF NOT EXISTS` DDL for the twelve agent tables plus their indexes, one statement per * array element so each can be issued through Lucid's `rawQuery`. Portable across SQLite / Postgres / * MySQL: quoted identifiers, epoch-ms `BIGINT` timestamps, `INTEGER` booleans (0/1) and `TEXT` JSON * columns — no dialect-only types. A real deployment should prefer the bundled migration stub so the * schema is versioned; this helper lets a store stand itself up in tests and scripts. * * A tool call is addressed by the model-supplied `toolCallId`, kept as-is in `call_id`. The row's PK * is that id scoped to its assistant message (see `toolCallRowId` in `lucid.ts`): a provider only * promises the id is unique within one response, and some (short `call_<digits>` ids behind * OpenRouter) repeat one across runs. A row from before `call_id` existed has it NULL and its PK is * the bare model id. * * The `agent_run` table records each run (turn) lifecycle; `agent_message` / `agent_tool_call` / * `agent_token_usage` each carry a nullable `run_id` correlation column (logically referencing * `agent_run.id`, deliberately WITHOUT a DB-level foreign key — like the reference — so the additive * migration can `ALTER TABLE ADD COLUMN` portably and a row recorded before run tracking shipped can * keep a `null` run_id). */ export declare function createTableStatements(): string[]; /** * Whether `agent_tool_call` has its `call_id` column (#183). What `LucidAgentStore` checks when the * app owns the schema (`autoCreateTables: false`) and so may boot a new version before its migration * has added the column. Probed on {@link schemaRunner}'s client, outside any global transaction. */ export declare function hasToolCallIdColumn(db: LucidRawRunner): Promise<boolean>; /** * Whether `agent_message` and `agent_queued_message` have their `sent_at` columns — what * `LucidAgentStore` checks under `autoCreateTables: false`, where the app may boot this version before * its migration has added them. Probed like {@link hasToolCallIdColumn}. */ export declare function hasSentAtColumns(db: LucidRawRunner): Promise<boolean>; /** Rows of a raw query result, whatever the driver wraps them in (`{ rows }`, `[rows, fields]`, rows). */ export declare function rowsOf(result: unknown): Record<string, unknown>[]; /** * Idempotently provision the twelve agent tables through Lucid's async raw runner (`CREATE TABLE IF * NOT EXISTS`), then additively repair a database that predates run tracking by ALTERing in the * `run_id` columns its three older tables are missing. * * Both halves matter, and for different populations. A fresh database gets everything from the * `CREATE TABLE` statements and never reaches a repair branch — which is what makes a non-empty * `repairs` result mean "this schema was out of date", not "boot happened". A database created before * run tracking shipped has all three tables already, so every `CREATE TABLE IF NOT EXISTS` no-ops and * only the repair adds the columns the store now writes on every turn. * * Against a schema that is already current it issues no DDL that takes a table lock: an existing * index is found in the catalog instead of re-issued (see {@link existingIndexes}). And it is safe * with several processes provisioning at once (see {@link issue}). * * Returns the `<table>.<column>` repairs actually issued, so a caller can report them. Works on every * Lucid dialect. For an AdonisJS app prefer the published migration * (`node ace configure @adonis-agora/agent`); this helper is what that migration calls, and what the * stores call themselves when `autoCreateTables` is on. */ export declare function createAgentTables(db: LucidRawRunner): Promise<string[]>; /** * `DROP TABLE IF EXISTS` for the twelve agent tables, in reverse dependency order so a dialect that * enforces the `REFERENCES` clauses never refuses a drop for a child that still exists. The mirror of * {@link createAgentTables}, and what the published migration's `down()` calls. */ export declare function dropTableStatements(): string[]; /** Drop the twelve agent tables. Destructive and irreversible — this erases every thread and every ledger row. */ export declare function dropAgentTables(db: LucidRawRunner): Promise<void>; /** * The client schema DDL should run on: one of its OWN, outside a global transaction the caller has * open (`db.beginGlobalTransaction()` — what `testUtils.db().withGlobalTransaction()` does for a Japa * suite). Inside that transaction the DDL would be rolled back with the test while the process still * remembers the schema as provisioned, and on Postgres the column probes — which fail by design on a * missing column — would abort the whole transaction. * * Lucid's manager answers every `connection()` with the global transaction while one is registered, * so the registration is lifted for the one synchronous call that builds a plain client and put back * before anything else can run. * * SQLite is left where it is: it has one writer, so a second connection could only wait for the very * transaction it is trying to avoid (and with a pool of one there is no second connection at all). * Its DDL is transactional and a failed probe does not abort anything, so running inside is correct. * * Anything that is not the `Database` manager — a client from `db.connection(name)`, a fake — is * returned as is: a plain client is already outside, and a transaction client cannot be escaped. */ export declare function schemaRunner(db: LucidRawRunner): LucidRawRunner; /** * Idempotently ensure the twelve agent tables exist, memoized per db client. This is what the stores * run when `autoCreateTables` is on (the default): the agent provider calls each store's * `ensureSchema()` once as the app starts, and a store used without the provider (a script, a test * that builds one by hand) falls back to it on first use — whichever store touches the connection * first provisions the shared schema, so pricing seeds and governance reads work even before the * first agent run. The DDL runs on {@link schemaRunner}'s client, never inside a global transaction. */ export declare function ensureAgentTables(db: LucidRawRunner): Promise<void>; /** * Idempotently create the Lucid token sink's frame table, under whatever name the sink was given. * What the sink runs for itself (`autoCreateTables`), so it stands up next to ANY store — the * in-memory one included — without provisioning the other seven tables. {@link createAgentTables} * creates the same table under its default name, so the published migration covers it too. */ export declare function ensureStreamFrameTable(db: LucidRawRunner, table?: string): Promise<void>; /** * Idempotently create text channels' state table, under whatever name the store was given — what * `lucidChannelStore` runs for itself (`autoCreateTables`), so it stands up next to any agent store. * {@link createAgentTables} creates the same table under its default name, so the published * migration covers it too. */ export declare function ensureChannelStateTable(db: LucidRawRunner, table?: string): Promise<void>; //# sourceMappingURL=lucid-schema.d.ts.map