UNPKG

@adonis-agora/agent

Version:

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

70 lines • 3.34 kB
import { AGENT_TABLES, ensureAgentTables } from './lucid-schema.js'; import { dialectOf } from './sql-dialect.js'; /** How many rows a raw write touched, whatever the driver wraps it in. */ function insertedRows(result) { // mysql2: `[ResultSetHeader, fields]`. Everything else here answers `RETURNING` with rows. const head = Array.isArray(result) ? result[0] : result; const affected = head?.affectedRows; if (typeof affected === 'number') return affected; if (Array.isArray(result)) return result.length; const rows = result?.rows; return Array.isArray(rows) ? rows.length : 0; } /** * A production {@link ConfirmTokenStore} backed by AdonisJS **Lucid** over the `agent_confirm_token` * table — what makes a confirmed write single use across replicas. Like * {@link import('./lucid.js').LucidAgentStore} it touches only the structural {@link LucidDatabaseLike} * slice, so `@adonisjs/lucid` stays an optional peer. * * `claim` is one `INSERT` that skips on a duplicate hash, so the primary key is the lock: of two * concurrent confirmations one inserts and the other touches no row. It is a statement that does not * FAIL on the duplicate (`ON CONFLICT DO NOTHING`; `INSERT IGNORE` on MySQL) rather than an insert * whose unique violation is caught, because on Postgres a failed statement aborts the transaction it * ran in. * * Only the token's SHA-256, the actor and the tool name are stored. `expires_at` is epoch-ms, like the * rest of the schema; call {@link purgeExpired} from a scheduled job to drop the dead rows. */ export class LucidConfirmTokenStore { db; autoCreateTables; constructor(db, options = {}) { this.db = db; this.autoCreateTables = options.autoCreateTables ?? true; } /** Provision the shared schema on first use (no-op when disabled), memoized across the stores. */ ready() { return this.autoCreateTables ? ensureAgentTables(this.db) : Promise.resolve(); } /** Provision the shared schema now. Idempotent. */ ensureSchema() { return this.ready(); } async claim(input) { await this.ready(); const table = AGENT_TABLES.confirmTokens; const bindings = [input.hash, input.actorRef, input.tool, input.expiresAt, Date.now()]; const sql = /mysql|maria/i.test(dialectOf(this.db)) ? `INSERT IGNORE INTO ${table} (hash, actor_ref, tool, expires_at, created_at) VALUES (?, ?, ?, ?, ?)` : `INSERT INTO "${table}" ("hash", "actor_ref", "tool", "expires_at", "created_at") VALUES (?, ?, ?, ?, ?) ON CONFLICT ("hash") DO NOTHING RETURNING "hash"`; return insertedRows(await this.db.rawQuery(sql, bindings)) === 1; } async release(hash) { await this.ready(); await this.db.from(AGENT_TABLES.confirmTokens).where('hash', hash).delete(); } async purgeExpired(now = Date.now()) { await this.ready(); const expired = await this.db .from(AGENT_TABLES.confirmTokens) .where('expires_at', '<', now) .select('hash'); if (expired.length === 0) return 0; await this.db.from(AGENT_TABLES.confirmTokens).where('expires_at', '<', now).delete(); return expired.length; } } //# sourceMappingURL=lucid-confirm-tokens.js.map