/** * Durable, content-addressed attachment storage (v2 migration, phase A7). * * Inline base64 attachments on a {@link DeliveredMessage} are materialized at * admission into content-addressed refs: bytes live in an * {@link AttachmentStore} keyed by `(scope, sha-256 digest)` and the persisted * submission payload carries only the ref. `scope` is the storage partition — * the harness passes the instance's store session id * (`agent:::`), so attachments are isolated per instance * session and content-deduplicated within it. * * Materialization is deterministic (ids derive from the submission/dispatch * id + attachment index, digests from content), so an exact redelivery of the * same message produces an identical payload and admission idempotency is * preserved. Verify an implementation with * `defineAttachmentStoreContractTests` from `attachment-store-contract.ts`. */ import type { DeliveredMessage } from './delivered-message.js'; /** Content-addressed descriptor of one stored attachment. */ export interface AttachmentRef { /** Caller-assigned attachment id (deterministic at admission). */ id: string; mimeType: string; sizeBytes: number; /** Lowercase hex SHA-256 of the bytes — the content address. */ digest: string; /** Uploader-provided display name; not part of byte identity. */ filename?: string; /** Creation time used by retention coordinators. Older refs may omit it. */ createdAt?: string; } /** * Build an {@link AttachmentRef} for the given bytes, computing the SHA-256 * digest via WebCrypto (`crypto.subtle`) so the SDK stays runtime-agnostic. */ export declare function createAttachmentRef(input: { id: string; mimeType: string; bytes: Uint8Array; filename?: string; }): Promise; /** Lowercase hex SHA-256 of the bytes (WebCrypto). */ export declare function attachmentDigest(bytes: Uint8Array): Promise; /** * Verify that `bytes` match the ref's digest (and declared size), throwing * `AttachmentStoreError('DIGEST_MISMATCH')` otherwise. Every store's `put` * MUST run this check before persisting. */ export declare function verifyAttachmentBytes(ref: AttachmentRef, bytes: Uint8Array): Promise; export interface AttachmentPutInput { /** Storage partition — the instance's store session id. */ scope: string; ref: AttachmentRef; bytes: Uint8Array; } export interface StoredAttachment { ref: AttachmentRef; bytes: Uint8Array; } /** * Durable content-addressed attachment storage. * * - `put` is idempotent by `(scope, digest)` — re-putting the same content * succeeds without duplicating storage (the first stored ref metadata is * retained). It MUST verify the digest against the bytes and reject a * mismatch with `AttachmentStoreError('DIGEST_MISMATCH')` before writing. * - `get`/`getByAttachmentId` return `null` on a miss. * - `delete` removes one stored attachment and throws * `AttachmentStoreError('NOT_FOUND')` when nothing is stored under * `(scope, digest)`. */ export interface AttachmentStore { put(input: AttachmentPutInput): Promise; get(scope: string, digest: string): Promise; getByAttachmentId(scope: string, attachmentId: string): Promise; list(scope: string): Promise; delete(scope: string, digest: string): Promise; } export declare class AttachmentStoreError extends Error { readonly code: 'DIGEST_MISMATCH' | 'NOT_FOUND'; constructor(code: AttachmentStoreError['code'], message: string); } /** In-memory attachment store (dev / `runtime: 'stateless'` / tests). */ export declare class InMemoryAttachmentStore implements AttachmentStore { private readonly records; put(input: AttachmentPutInput): Promise; get(scope: string, digest: string): Promise; getByAttachmentId(scope: string, attachmentId: string): Promise; list(scope: string): Promise; delete(scope: string, digest: string): Promise; /** Serializable state used by durable adapters that wrap the reference semantics. */ exportPersistenceSnapshot(): unknown; importPersistenceSnapshot(snapshot: unknown): void; } /** True when the message carries at least one inline (base64) attachment. */ export declare function messageHasDataAttachments(message: DeliveredMessage): boolean; /** * Materialize a message's inline attachments into durable refs: decode the * base64 data, store the bytes under `scope`, and return a NEW message whose * attachments carry `{ type, mimeType, filename?, ref }` and no `data`. * * Deterministic by construction — attachment ids are `${idPrefix}_${index}` * and digests derive from content — so an exact redelivery of the same * message produces an identical materialized payload (admission idempotency). * Messages without inline attachments are returned unchanged (same * reference). */ export declare function materializeMessageAttachments(message: DeliveredMessage, store: AttachmentStore, options: { scope: string; idPrefix: string; maxCount?: number; maxAttachmentBytes?: number; maxTotalBytes?: number; }): Promise; export declare class AttachmentLimitError extends Error { readonly code = "ATTACHMENT_LIMIT_EXCEEDED"; constructor(message: string); } //# sourceMappingURL=attachment-store.d.ts.map