trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
132 lines • 5.02 kB
TypeScript
/**
* Canonical op serialization and hashing.
*
* ADR 0021. The op hash was previously unverifiable: the kernel hashed the
* caller's payload object verbatim while each backend independently
* reconstructed a differently-shaped payload to store. Two writers, two shapes,
* no way to recompute a hash from storage.
*
* This module is the single serializer. `hashKernelOp` (mint) and every
* backend's `append()` (persist) must both go through it — neither may
* hand-roll an object literal. That property is what makes the hash a real
* content address rather than a unique-ish identifier.
*
* @module trellis/core
*/
import type { Fact, Link } from '../store/eav-store.js';
import type { KernelOp } from './backend.js';
/**
* Preimage version. v1 = the legacy, unrecoverable `|`-joined format; ops
* predating this module carry no `v` and are grandfathered as opaque history
* (their preimages cannot be reconstructed — that was the bug).
*/
export declare const OP_PREIMAGE_VERSION = 2;
/**
* Op-level provenance (ADR 0021 §2, Phase B).
*
* The slot exists in the canonical shape from Phase A onward, normalized to
* `null` when absent. Phase B populates it. Because both phases run the same
* normalization, populating it later changes values but not shape — Phase-A ops
* keep verifying, and provenance lands without a second preimage break.
*/
export interface OpProvenance {
actorType: 'user' | 'machine' | 'ai';
origin: 'cli' | 'sdk' | 'http' | 'mcp' | 'sync' | 'migration' | 'cron';
}
/**
* Per-surface provenance defaults (ADR 0021 §2).
*
* `origin` is genuinely knowable — it is the transport the op arrived on.
* `actorType` is an *assertion by the minting process*, not a proof; binding it
* to a device key is ADR 0020's territory (see ADR 0021 "Deferred"). Treat
* these as best-available claims:
*
* - `mcp` ⇒ `ai` — MCP is the agent tool protocol; the caller is an agent.
* - `agent` ⇒ `ai` over `origin: 'sdk'` — an in-process AI agent calling the
* kernel directly (the harness, plan execution, agent memory). Keyed by
* actor rather than transport because `sdk` is the transport and `ai` is the
* part that carries information.
* - `cli` ⇒ `user` — a person ran a command. Note this is the weakest claim
* here: agents shell out to the CLI too, and nothing distinguishes them.
* - everything else ⇒ `machine` — the honest "a program did this, we do not
* know on whose behalf".
*/
export declare const PROVENANCE: {
readonly cli: {
readonly actorType: "user";
readonly origin: "cli";
};
readonly http: {
readonly actorType: "machine";
readonly origin: "http";
};
readonly mcp: {
readonly actorType: "ai";
readonly origin: "mcp";
};
readonly sdk: {
readonly actorType: "machine";
readonly origin: "sdk";
};
readonly agent: {
readonly actorType: "ai";
readonly origin: "sdk";
};
readonly sync: {
readonly actorType: "machine";
readonly origin: "sync";
};
readonly migration: {
readonly actorType: "machine";
readonly origin: "migration";
};
readonly cron: {
readonly actorType: "machine";
readonly origin: "cron";
};
};
/** The caller-supplied body of a mutation, before normalization. */
export interface OpPayloadInput {
facts?: Fact[];
links?: Link[];
deleteFacts?: Fact[];
deleteLinks?: Link[];
provenance?: OpProvenance;
}
/**
* Serialize a newly-minted op body to its canonical JSON string.
*
* This exact string is what backends write to the `payload` column, so the
* stored bytes are the hashed bytes.
*/
export declare function canonicalOpBody(payload: OpPayloadInput): string;
/**
* Serialize a persisted op's body, preserving its own preimage version.
*
* Used by `append()`. Ops arriving via `appendBatch` from a peer may be v1;
* those must round-trip as v1 (opaque history), not be restamped as v2.
*/
export declare function canonicalOpBodyFromOp(op: KernelOp): string;
/** Header fields that sit outside the body but inside the preimage. */
export interface OpHeader {
kind: string;
timestamp: string;
agentId: string;
previousHash?: string;
}
/** Compute an op's content hash from its header and caller payload (mint path). */
export declare function hashKernelOp(header: OpHeader, payload: OpPayloadInput): Promise<string>;
export interface VerifyResult {
valid: boolean;
/** True for pre-ADR-0021 ops, whose preimages are unrecoverable by construction. */
legacy: boolean;
}
/**
* Recompute a persisted op's hash and compare.
*
* v1 ops (no `v` in the stored body) are grandfathered: reported
* `{valid: true, legacy: true}` rather than failed, since their preimages
* cannot be reconstructed. v2 ops must genuinely verify.
*/
export declare function verifyOpHash(op: KernelOp): Promise<VerifyResult>;
//# sourceMappingURL=canonical-op.d.ts.map