voice-bot implementation
Migration entry
The manifest entry exports migrate(db: PluginDb<never>): Promise<void> beside register.
The daemon loader always awaits it before registration for this reads: ['db'] plugin;
the DB CLI calls the same export without registering tools, routes, timers or services.
The export runs only the existing transactional plugin SQL through db.migrate.
A failed step rolls back its SQL and ledger entry and prevents registration. Repeated
calls skip applied versions. Runtime store construction never invokes migrations.
Tests that bypass the loader explicitly await this export before using their fixture.
Error rendering
VoiceCall transport failures use errorText from elowen-plugin-shared/errors. errorText(error) retains Error.message and otherwise uses String(error); conversion failures propagate. It formats only an existing failure and does not log, retry or invent a success result. VoiceCall applies its existing token redaction after conversion. With no failure there is no error conversion.
Call audit store
Registration awaits ctx.db() and openStore(db) before registering the tool
and account-removal hook, including when the endpoint is unconfigured.
The asynchronous PluginDb owns the quoted "plugin_voice-bot".calls table.
Migration 1 creates an identity id, a timestamptz created_at and the
created-at index.
The store exposes asynchronous reserve, settle and removeUser.
VoiceCall awaits ctx.currentAccountUserId() and captures the session before admission, so reserve binds the resolved account id rather than a Promise.
reserve takes a transaction advisory lock keyed by the schema and
.calls.rate-limit, then reads the rolling window and inserts the requested
attempt on that same connection. Concurrent daemon and runner calls share the
lock, including when no calls exist yet. A rejected admission returns the count
and oldest timestamp without adding a row. Failed and unknown attempts count
toward the hourly cap.
HTTP starts only after reservation commits, outside the database transaction. VoiceCall awaits settlement before returning the outcome, keeping a response capped at 8,000 characters. Database failures propagate and never produce a successful call result. The account-removal hook awaits deletion of the account's numbers and transcripts. Settlement after removal updates no row and does not recreate personal data. No core-table SQL or synchronous database handle is used.