@heddleagent/postgres
v9.0.0
Published
Official PostgreSQL adapters for selected Heddle-owned domain persistence ports
Maintainers
Readme
@heddleagent/postgres
Official PostgreSQL implementations for selected, public Heddle-owned durable ports. The package ships independent adapters behind explicit entrypoints:
The canonical source now lives in the private-first Heddle Execution Host repository. The npm package remains public and requires no repository access; source access is relevant only to complete self-host inspection and operation.
| Entrypoint | Domain contract | Status |
| --- | --- | --- |
| @heddleagent/postgres/heartbeat | Heartbeat task-store and administration contracts from @heddleagent/runtime/advanced | Supported |
| @heddleagent/postgres/execution-host/conversations | HostedConversationTurnLifecycleStore from @heddleagent/execution-host-client/conversation | Supported |
| @heddleagent/postgres/execution-host/heartbeat-admission | Durable hosted admission state from @heddleagent/execution-host-client/coordinator | Supported |
There is no generic root storage provider. Conversation sessions, artifacts, memory, telemetry, product history, and active execution are covered only when an explicit entrypoint says so.
Install
Install the adapter with its domain contract, Drizzle, and one supported
Drizzle PostgreSQL driver. Domain peers are optional at the package level so a
heartbeat consumer does not install the Execution Host client and vice versa.
For example, with pg:
npm install @heddleagent/postgres @heddleagent/execution-host-client drizzle-orm pgFor heartbeat task authority:
npm install @heddleagent/postgres @heddleagent/runtime drizzle-orm pgHeartbeat task authority
import {
createPostgresHeartbeatTaskAuthority,
heartbeatPostgresMigrationSqlUrls,
} from '@heddleagent/postgres/heartbeat';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const database = drizzle(pool);
const heartbeat = createPostgresHeartbeatTaskAuthority({
database,
namespace: authenticatedTenant.id,
executionLeaseMs: 20 * 60_000,
});
void heartbeatPostgresMigrationSqlUrls;The adapter owns row locking, due-task claims, execution fencing,
lease-backed recovery, checkpoints, run history, and atomic operator controls.
The factory accepts ordinary schema-typed Drizzle PostgreSQL databases; its
public database contract exposes only the query operations the adapter uses.
Successful custom handlers are stored as non-agent completed run records.
They retain the existing checkpoint, release only the matching execution
claim, and leave an enabled recurring task scheduled for its next interval.
Successful agent settlement may carry Runtime 9.1's optional one-off preferred
next-run timestamp. The adapter validates that timestamp and passes it to the
Runtime state projector while holding the latest task row and matching claim
fence. Runtime remains the only owner of deadline selection, including the
periodic floor, terminal state, and newer pending run-request precedence.
Code-owned catalogs may reconcile with
existingTaskPolicy: 'synchronize-configuration'. The administration adapter
persists each changed task inside the locked catalog transaction while
retaining its lease, checkpoint, scheduling position, pending request, and run
history. An unchanged catalog performs no task write, and enabling a blocked
task still requires the explicit resume operation.
Workers receive heartbeat.store and heartbeat.admission; trusted operator
routes receive heartbeat.administration. Fresh claims take shared namespace
and assigned-group admission locks inside the same transaction that claims the
task. Missing namespace admission retains legacy-ready behavior; a missing
assigned group fails closed. Only Heddle's exact durable recovery claim for the
current interrupted execution may bypass closed admission. The adopter owns
its pool, migration execution, trusted namespace derivation, worker lifecycle,
backups, and side-effect idempotency.
Execution Host heartbeat admission
import {
createPostgresHostedHeartbeatAdmissionStore,
} from '@heddleagent/postgres/execution-host/heartbeat-admission';
const admissionStore = createPostgresHostedHeartbeatAdmissionStore({
database,
namespace: authenticatedTenant.id,
});The hosted store owns durable desired state, preparation phase, stable
transition identity, retries, and compare-and-set settlement. It uses the same
heddle.heartbeat_admissions rows and advisory-lock identity as
createPostgresHeartbeatTaskAuthority(...).admission, so an admission change
and a fresh task claim serialize on one target. It never calls the adopter or
runs migrations.
Execution Host conversation lifecycle
import { DurableHostedConversationTurnService } from
'@heddleagent/execution-host-client/conversation';
import {
createPostgresHostedConversationTurnLifecycleStore,
} from '@heddleagent/postgres/execution-host/conversations';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const database = drizzle(pool);
const store = createPostgresHostedConversationTurnLifecycleStore({ database });
const turns = new DurableHostedConversationTurnService({
turns: executionHostTurnRunner,
store,
});The example omits application composition. The product authenticates callers, derives tenant/subject/session scope on the server, owns its history queries and retention, and closes the pool during application shutdown.
Ownership
The domain package owns lifecycle validation, requested/accepted/terminal ordering, safe terminal projection, expiry semantics, and the adapter-neutral conformance suite. This package owns the concrete SQL table, atomic row transitions, complete-scope fencing, database constraints, ordered migrations, and real-PostgreSQL certification.
The adopter owns:
- its PostgreSQL service, credentials, pool lifecycle, backups, encryption, availability, monitoring, and disaster recovery;
- production migration execution through its normal migration system;
- authenticated scope derivation and database access policy;
- product tables, relationships, history queries, retention, billing, and UI; and
- reconciliation scheduling for expired open turns when its product promise requires crash convergence.
The adapter never starts a pool, runs migrations at runtime, accepts an adopter-selected table name, or persists activity, tool inputs/results, hidden reasoning, raw errors, credentials, assertions, traces, or workspace content.
Migrations
Each entrypoint exports its ordered migration URL list. Heartbeat hosts must
adopt every URL in heartbeatPostgresMigrationSqlUrls. The former singular
baseline export is deliberately absent from the breaking release so an upgrade
cannot silently omit the required admission migration. The SQL files ship
under:
migrations/execution-host/conversations/
migrations/heartbeat/Adopt the relevant files into the application's reviewed migration process and
apply them before constructing an adapter. Runtime startup deliberately
performs no schema mutation. Each domain migration owns only its documented
heddle tables, constraints, and indexes.
Copy the migration into your application
The adapter exports migration URLs so your build or release tooling can locate the exact SQL shipped with the installed package:
node --input-type=module -e "import('@heddleagent/postgres/execution-host/conversations').then(({ executionHostConversationPostgresMigrationSqlUrls: urls }) => console.log(urls.map(String).join('\\n')))"For a normal node_modules install, the current file is also available at:
node_modules/@heddleagent/postgres/migrations/execution-host/conversations/0000_turn_lifecycle.sqlCopy every exported migration, in array order, into your application's own checked-in migration directory. Rename the file only to fit the application's sequence; do not rewrite the SQL. For example:
cp node_modules/@heddleagent/postgres/migrations/execution-host/conversations/0000_turn_lifecycle.sql \
apps/server/drizzle/0005_execution_host_conversations.sqlCommit that copy and let the application's existing migration command apply it
before deploying code that constructs the store. This explicit adoption is
required because the application—not a library running at startup—owns schema
review, rollout order, rollback policy, and production database credentials.
When upgrading @heddleagent/postgres, compare the exported ordered list with
the migrations already adopted and copy only newly published files.
Generate a Drizzle migration from the heartbeat schema
Drizzle Kit expects a filesystem path rather than a package import. Resolve the
public schema subpath in drizzle.config.ts; do not inspect the package's
internal dist/ layout:
import { createRequire } from 'node:module';
import { join } from 'node:path';
import { defineConfig } from 'drizzle-kit';
const require = createRequire(join(process.cwd(), 'package.json'));
export default defineConfig({
dialect: 'postgresql',
schema: require.resolve('@heddleagent/postgres/heartbeat/schema'),
out: './drizzle',
});The package's public export owns the resolved file location. This workflow
requires @heddleagent/[email protected] or newer.
Correctness promise
invocation_idis globally unique, so a duplicate request cannot execute twice under another scope.- Accepted and terminal writes lock the invocation row and fence by tenant, subject, and product-session scope.
- Exact repeats are idempotent; conflicting, wrong-scope, pre-acceptance, or late transitions fail atomically.
- Scoped expiry can interrupt only expired
requestedorrunningrows and cannot overwrite a terminal result. - SQL constraints independently enforce identifier, status, failure-code, payload-size, acceptance, and terminal-shape invariants.
- The package runs the canonical lifecycle conformance suite against a real PostgreSQL service and independent pools.
This adapter makes the generic lifecycle durable. It does not turn that table into a user-facing transcript or provide active-run replay/recovery.
