Migrate to 2.0
Tantra 2.0 adds tantra.acp, which serves a Runtime to any ACP client over an ACP v2–inspired protocol plus the tantra profile, and tantra-acp-client, its TypeScript client. It also adds Runtime.watch_tree, Runtime.patch_header, Runtime.lookup_ask and input attribution. Journal bodies and sequences are unchanged. PostgreSQL applies migrations 12 and 13; SQLite, memory and filesystem storage formats have no new migration.
Upgrade PostgreSQL
Section titled “Upgrade PostgreSQL”Every worker must run 2.0 before migration 12 is applied. On the migrated schema an older worker writes no journal_index row for AskRaised, and PostgresStore.lookup_ask has no fallback, so tantra.acp never issues those asks.
-
Stop every application worker and other writer using the database. Mixed-version writers are unsupported during migration.
-
Install
tantra-harness[postgres]==2.0.0in the application environment. -
Run setup once against the existing database and schema before starting Runtime:
from tantra import PostgresStorestore = PostgresStore(DATABASE_URL, schema=EXISTING_SCHEMA)await store.setup()await store.close() -
Start only workers running 2.0. Preserve the startup order: Store setup, then
await runtime.start(). Fail readiness if either operation fails.
Setup applies pending migrations through version 13.
- Migration 12 adds
journal_index.ask_idand the partial indexjournal_ask_idx, then backfills ask entries by decoding every stored event in pages of 1,000. It runs in the setup transaction and holds an exclusive lock onjournal_indexuntil it commits, so its time grows with total journal size and every append and indexed read waits for it. Allow a maintenance window. - Migration 13 only adds the partial index
coordinator_changes_tree_idx. It is built in the setup transaction, so change inserts wait for it briefly. Older workers ignore it.
Corrupt rows or migration failures roll back, and the schema version is published only after completion. Pending migrations from earlier releases also run, in order; see the 1.4 and 1.3 guides.
Coordinated deployments
Section titled “Coordinated deployments”2.0 records sender_id and origin on InputQueued. A retry compared with an event written before origin existed ignores both fields, so recovery and command retries over older journals stay duplicates. An older owner rejects retries of inputs that carry the new fields: drain older coordinated workers before rolling out 2.0.
CoordinatedStore now publishes a header change when an append changes an actor’s status, current turn or last turn.
Input attribution
Section titled “Input attribution”InputQueued.originisuser,spawn,message,finishedorturn_ended, and agent inputs carry the sending actor’s journal id insender_id. The input text and its prefixes are unchanged. Events written before these fields existed read both as None. See Events.Connection.send,prompt,answerandcancelacceptsubmitted_by, a string of 1–256 characters. It is recorded onInputQueued,AskAnsweredandCancellationRequested, and turns expose it asTurnContext.submitted_byand toolContext.submitted_by. It grants no access.
Tree state and headers
Section titled “Tree state and headers”Runtime.watch_tree(root_id)yieldsTreeChangesnapshots, the root’s title and model plus every actor status, on each tree change without reading journals. Applications that polltree_status()can follow it instead.- Route application title and model changes through
Runtime.patch_header(agent_id, title=..., model=...). It needs no execution ownership, so any Runtime may call it, and it signals tree watchers before it returns. A directstore.patch_headerreaches watchers only through a coordinator. On PostgreSQL, a model change raisesModelChangeBusywhile the tree has work; a title-only patch never does.
See Runtime & Connection.
Serve clients over ACP
Section titled “Serve clients over ACP”Applications that stream a Runtime to their own clients over an application-owned socket can serve it with tantra.acp instead. AcpServer(runtime, *, agent, scope, policy, ...) offers websocket(*, bind, allowed_origins, allow_missing_origin=False), an ASGI AcpWebSocket, plus serve_stdio(auth) and connect(transport, auth). The tantra profile adds journal cursors, client command IDs, one fenced writer per root, lazy child transcripts and a shared input queue. tantra-acp-client is the TypeScript client for the WebSocket endpoint.
- On shutdown, call
await endpoint.aclose()beforeawait runtime.aclose(). Runtime.lookup_ask(root_id, ask_id)returns the originalAskRaisedas aLocatedAsk, and theanswerpolicy check receives it inAcpAccess.ask. Never trust approval details supplied by a client.
See the ACP server reference and the tantra profile specification.
Custom stores
Section titled “Custom stores”tantra.testing.store_conformancenow requires a store to detect duplicate inputs by comparing events with==, so that a legacyInputQueuedequals its structured retry. Custom stores that compare serialized events fail it.lookup_ask(root_id, ask_id)is an optional store capability. Runtime falls back to reading history when it is absent; required Store and Coordinator protocols are unchanged.
Behavior changes
Section titled “Behavior changes”- Tool approval asks carry the call’s effective arguments in
extra["arguments"]. bashruns commands with stdin set to/dev/null.- Tool call IDs are unique within a session. A provider ID already used in the turn or the loaded history, including the
OpenAICompatiblefallbackcall_{index}, is rewritten to a deterministiccall_<24 hex>before it is journaled. IDs without a collision are unchanged. pg_notifychange payloads are now built by PostgreSQL: the same keys, with different whitespace.