Proposed. It places the tables of 0100 (decision 10) and
implements its decisions 2, 3 and 9. Linear STA-781.
Context
ADR 0100 names an agent runtime (prompt, stream, queue, resume), conversations encrypted per tenant and eight tables, but not where they live: in@stackra/ai, or in the OS ai service. Products other than the OS run agents too, and the
workflow package already ships its tables and models in its own ./node entry.
Decision
- The package owns the storage.
@stackra/ai/nodeships the models, the first migration and the Postgres drivers of theConversationStoreandRunStoreports; any service that installs the package gets the tables. The ai service binds the drivers, theToolDispatcher(tool table, IAM, the matrix and the action pipeline),AgentModels(the router over managed or own-key providers),InstructionSource(published prompt documents) andAgentQueue. - Names. The tables are
ai.conversations,ai.messages,ai.runs,ai.steps,ai.tool_calls,ai.approvals,ai.embeddingsandai.a2a_tasks: ADR 0100’sai_prefix is the module schema. Each is owned by a tenant under the forced equality policy. - Text only in messages, sealed. A message’s content is its AI SDK
ModelMessageas JSON, sealed byTenantKeyCastwith theFieldCipher(the tenant’s data key, ADR 0188) and bound toai.messages.contentand the owning tenant. Runs, steps and tool calls hold codes, counts and SHA-256 input hashes only. - The runtime.
Agent({ key, task, tools, maxSteps })extendsPromptable;AgentRuntimeruns the AI SDK loop, dispatches every tool call throughToolDispatcherwith the Idempotency-KeyrunId:toolCallId, parks the run on the first call the gate parks (without storing that call’s placeholder result) and resumes it with the approval id. - Out of scope here. A2A and the MCP gateway;
ai.a2a_tasksandai.embeddingsship as tables only.
Consequences
- One schema for every product’s agents; the OS ai service holds no table of its own for runs.
- Updates to a run row are flushed by the caller’s unit of work, so a model loop inside one action holds a
transaction; short write-now updates come when
@stackra/databaseoffers them. - A conversation sealed for one tenant cannot be opened in another, even by a copy of the row.
Enforcement
packages/application/ai/src/node/database/migrations/__tests__/0001-create-ai-tables.migration.test.ts;
packages/application/ai/src/node/casts/__tests__/tenant-key.cast.test.ts;
packages/application/ai/src/core/services/__tests__/agent-runtime.service.test.ts. Planned — an RLS isolation test
per AI table (ADR 0100 enforcement) once the database test harness runs.
Supersedes and sources
Supersedes: none. Sources: STA-781;notes/handover/stackra-package-needs.md (@stackra/ai row);
notes/handover/build-wave10-2026-10-25.md (ai section).