/** * The Heart Runtime — the execution pathway through which all agent * computation flows. * * The agent's API keys, tool credentials, and data connections all live * inside the heart. No heart, no credentials, no computation. The agent * literally cannot function without it. * * Every computation that passes through the heart gets: * 1. A cryptographic seed woven into the input * 2. A heartbeat logged in the hash chain * 3. A birth certificate for any new data * * The output is inseparable from the heart that produced it. */ import { type CryptoProvider, type SignKeyPair } from "../core/crypto-provider.js"; import { type GenomeCommitment } from "../core/genome.js"; import { type Channel, type HandshakePayload, type BoxKeyPair } from "../core/channel.js"; import { HeartbeatChain, type Heartbeat } from "./heartbeat.js"; import { type BirthCertificate } from "./birth-certificate.js"; import { type HeartLineage, type LineageCertificate } from "./lineage.js"; import { type Caveat, type Delegation } from "./delegation.js"; import { type RevocationEvent, type RevocationReason } from "./revocation.js"; /** * Default maximum age of a session, in ms. Sessions older than this are * purged from the sessions Map and treated as not-found on any read. * * Without a cap, the sessions Map grows unbounded — every createSession call * allocates a HeartbeatChain + ephemeral keypair that persist until destroy(). * A long-lived heart handling many short sessions would leak memory and * accumulate revoked-but-not-cleaned session keys. One hour is long enough * for ordinary streaming interactions and short enough to bound growth. */ export declare const DEFAULT_SESSION_TTL_MS: number; export interface DataSourceConfig { name: string; url: string; headers?: Record; } export interface HeartConfig { genome: GenomeCommitment; signingKeyPair: SignKeyPair; modelApiKey: string; modelBaseUrl: string; modelId: string; toolCredentials?: Record; dataSources?: DataSourceConfig[]; profileStorePath?: string; /** * Lineage chain — if present, this heart was forked from a parent. * effectiveCapabilities(lineage) gates what this heart can do. */ lineage?: HeartLineage; /** Pre-existing revocation events this heart should honor. */ revocations?: RevocationEvent[]; /** * Pre-encrypted credentials to restore into the vault (persistence). * Used by loadSomaHeart() — callers should NOT set this directly. * @internal */ restoreCredentials?: Array<{ name: string; nonceB64: string; ciphertextB64: string; }>; /** * Prior heartbeat chain to continue from (persistence). * Used by loadSomaHeart() — callers should NOT set this directly. * @internal */ restoreHeartbeats?: Heartbeat[]; /** Crypto provider — swap algorithms without changing the protocol. */ cryptoProvider?: CryptoProvider; /** * Maximum age of a session in milliseconds. Defaults to * `DEFAULT_SESSION_TTL_MS` (1 hour). Sessions older than this are purged * and treated as not-found on any read — caps unbounded Map growth. */ sessionTtlMs?: number; } export interface GenerationInput { messages: Array<{ role: "system" | "user" | "assistant"; content: string; }>; temperature?: number; maxTokens?: number; } /** A token emitted by the heart during generation — interleaved with heartbeats. */ export interface HeartbeatToken { type: "token" | "heartbeat"; /** The token text (when type === "token"). */ token?: string; /** The heartbeat (when type === "heartbeat"). */ heartbeat?: Heartbeat; /** Timestamp of emission. */ timestamp: number; /** HMAC-SHA256(hmacKey, token || sequence || interaction_counter). Present when type === "token" and session has a key. */ hmac?: string; /** Monotonic per-interaction token counter. Present when type === "token" and session has a key. */ sequence?: number; } /** Result of a tool call routed through the heart. */ export interface HeartbeatResult { result: unknown; heartbeats: Heartbeat[]; birthCertificate: BirthCertificate; } /** * Progress emitter handed to tool executors. Each call records a * `tool_progress` heartbeat, giving observers sub-beat visibility into * the work a tool is doing — the blacksmith's strikes between input * and output. */ export type ToolProgressEmitter = (stage: string, detail?: string) => void; /** Function signature for a tool executor. */ export type ToolExecutor = (credential: string, args: Record, emit: ToolProgressEmitter) => Promise; /** Result of a data fetch routed through the heart. */ export interface HeartbeatData { content: string; heartbeats: Heartbeat[]; birthCertificate: BirthCertificate; } /** A session between two hearted parties. */ export interface HeartSession { sessionId: string; remoteDid: string; remoteGenome: GenomeCommitment; channel: Channel | null; sessionKey: Uint8Array | null; ephemeralKeyPair: BoxKeyPair; heartbeatChain: HeartbeatChain; interactionCounter: number; createdAt: number; } export declare class HeartRuntime { private readonly vault; private readonly heartbeatChain; private readonly genome; private readonly signingKeyPair; private readonly modelId; private readonly modelBaseUrl; private readonly dataSources; private readonly sessions; private readonly sessionTtlMs; private readonly provider; private readonly _lineage?; private readonly _effectiveCaps; private readonly revocations; private alive; constructor(config: HeartConfig); /** The heart's DID identity. */ get did(): string; /** The heart's genome commitment. */ get genomeCommitment(): GenomeCommitment; /** Whether the heart is still alive. */ get isAlive(): boolean; /** Get the global heartbeat chain (read-only). */ get heartbeats(): HeartbeatChain; /** The crypto provider this heart uses. */ get cryptoProvider(): CryptoProvider; /** * Compute the content hash used in birth certificates — exposed so providers * can hash their own cached content with the same scheme the heart uses. * This is the hash that flows through Soma Check's `X-Soma-Hash` header. */ hashContent(content: string): string; /** The lineage chain this heart carries (if it was forked from a parent). */ get lineage(): HeartLineage | undefined; /** * Capabilities granted to this heart by its lineage. * `null` = no restrictions (root heart). */ get capabilities(): string[] | null; /** * Check whether this heart is allowed to exercise a given capability * under its lineage. Root hearts (no lineage) return true for everything. */ can(capability: string): boolean; /** * Record an internal reasoning step. Agents should call this between * query-received and tool-call to make chain-of-thought visible in the * heartbeat chain — hashed, so the content stays private. */ recordReasoning(summary: string, sessionId?: string): Heartbeat; /** * Record a retry — any operation that was re-attempted. Makes the * blacksmith's missed strikes visible. */ recordRetry(operation: string, reason: string, attempt: number, sessionId?: string): Heartbeat; /** * Record a RAG lookup — what was retrieved to augment context. The * observer sees that retrieval happened and how many items were used, * without the retrieved content being exposed. */ recordRagLookup(queryHash: string, resultCount: number, sessionId?: string): Heartbeat; /** * Record that work was dispatched to a child or delegatee. * Makes multi-agent handoffs visible in the heartbeat chain. */ recordSubtaskDispatch(subjectDid: string, taskHash: string, sessionId?: string): Heartbeat; /** * Record that a child or delegatee returned a result for previously * dispatched work. */ recordSubtaskReturn(subjectDid: string, resultHash: string, sessionId?: string): Heartbeat; /** Pick the chain for a given session (global if none). */ private chainFor; /** * Fork a child heart: generate a fresh keypair, build the child's genome, * sign a lineage certificate binding the child to this heart, and return * the materials needed to construct the child HeartRuntime. * * The caller provisions the child's credentials (model API key, tool * credentials). This separation is intentional — a parent may fork many * children, each with different credentials. */ fork(opts: { systemPrompt: string; toolManifest: string; modelProvider?: string; modelId?: string; modelVersion?: string; runtimeId?: string; capabilities?: string[]; ttl?: number; budgetCredits?: number; }): { childKeyPair: SignKeyPair; childGenome: GenomeCommitment; lineageCertificate: LineageCertificate; childLineage: HeartLineage; }; /** * Create a delegation — grant a subject DID the right to exercise some * capabilities under caveats. The subject does NOT need to be a forked * child; delegation is a separate primitive. */ delegate(opts: { subjectDid: string; capabilities: string[]; caveats?: Caveat[]; parentId?: string | null; }): Delegation; /** * Sign a revocation event for a credential this heart previously issued. * The event is added to this heart's registry AND returned so the caller * can broadcast it to other parties. */ revoke(opts: { targetId: string; targetKind: "lineage" | "delegation" | "heart"; reason?: RevocationReason; detail?: string; }): RevocationEvent; /** Check whether a credential ID is revoked in this heart's registry. */ isRevoked(targetId: string): boolean; /** Import revocation events from an external source (e.g. a feed). */ addRevocations(events: RevocationEvent[]): number; /** Export this heart's revocation registry contents. */ exportRevocations(): RevocationEvent[]; /** * Serialize this heart's state to an encrypted blob. * Sessions are NOT serialized — they are ephemeral by design. * * Uses scrypt (memory-hard) by default. Override tuning via `scrypt`. */ serialize(password: string, opts?: { scrypt?: { N?: number; r?: number; p?: number; }; }): string; /** * Create a session with a remote party. * Generates an ephemeral key pair for the handshake. */ createSession(remoteDid: string, remoteGenome: GenomeCommitment): HeartSession; /** Get the handshake payload for a session. */ getHandshakePayload(sessionId: string): HandshakePayload; /** * Complete a session handshake with the remote party's handshake payload. * Establishes the encrypted channel and extracts the session key for seeding. */ completeHandshake(sessionId: string, remoteHandshake: HandshakePayload): void; /** * Get a session by ID. Returns undefined if the session has expired * past `sessionTtlMs` — expired sessions are purged on access so the * caller sees them as not-found. */ getSession(sessionId: string): HeartSession | undefined; /** * Internal session accessor. Purges the session if expired and returns * undefined in that case, otherwise returns the live session. All internal * session reads go through this so no code path observes a stale session. */ private activeSession; /** True if the session has outlived `sessionTtlMs`. */ private isSessionExpired; /** * Purge every expired session from the Map. Called opportunistically * from createSession so repeated creation can't leak memory even when * no reader ever touches the stale sessions. */ private purgeExpiredSessions; generate(input: GenerationInput, sessionId?: string): AsyncGenerator; callTool(name: string, args: Record, toolExecutor: ToolExecutor | ((credential: string, args: Record) => Promise), sessionId?: string): Promise; fetchData(sourceName: string, query: string, fetcher?: (url: string, headers: Record, query: string) => Promise, sessionId?: string): Promise; /** Stop the heart. All credentials are wiped. The agent can no longer compute. */ destroy(): void; private ensureAlive; } /** Create a heart runtime — the one-liner that gives an agent its heartbeat. */ export declare function createSomaHeart(config: HeartConfig): HeartRuntime; /** * Rehydrate a heart from an encrypted blob + password. * The vault's credentials are already encrypted with a key derived from the * signing secret key, so they are restored directly without re-entry. * * Sessions are not restored — they were ephemeral by design. */ export declare function loadSomaHeart(blob: string, password: string, opts?: { cryptoProvider?: CryptoProvider; }): HeartRuntime; //# sourceMappingURL=runtime.d.ts.map