import { Media } from "./media"; import { Identity } from "./identity"; import { Tokenizable } from "./tokenizable"; import { ENCODE_METHOD, DECODE_METHOD } from "../utils/encoder_symbols"; import type { DateTime } from 'luxon'; import type { RawIdentity } from "./identity"; import type { AdkEncodableSnapshot } from "./encodable"; /** * The roles a {@link Message} author can hold. * * @remarks * Restricted to `user` and `assistant` — system instructions, developer directives, and * tool results are handled separately and never appear in the persisted message history. */ export type MessageRole = 'user' | 'assistant'; /** * Plain input object supplied to {@link Message} at construction time. * * @remarks * Validated against `rawMessageSchema` before the `Message` instance is created. * Temporal fields accept any value that Luxon can parse — ISO strings, Unix timestamps, * `Date` objects, or existing `DateTime` instances. * * At least one of `content` or `attachments` (non-empty) must be present — a message with * neither throws {@link @nhtio/adk!E_INVALID_INITIAL_MESSAGE_VALUE}. */ export interface RawMessage { /** Stable unique identifier for this message. */ id: string; /** Whether this message is from the human participant or the model. */ role: MessageRole; /** * The message content as a plain string or an existing {@link @nhtio/adk!Tokenizable} instance. * * @remarks * Optional — but required when `attachments` is absent or empty. The cross-field rule on * `rawMessageSchema` enforces that at least one of `content` or `attachments` is present. */ content?: string | Tokenizable; /** * Media attachments carried by this message — images, audio, video, documents. * * @remarks * Optional and symmetric across roles: both `user` and `assistant` messages may carry * attachments. Each attachment carries its own `trustTier` and `modalityHazard`, which the * renderer uses to wrap the asset in its own trust envelope independent of the message * envelope. How a renderer orders text vs attachments in the on-the-wire content array is * a renderer-policy concern, not a contract of {@link Message}. */ attachments?: Media[]; /** * The identity of the participant who authored this message. * * @remarks * Optional. When omitted, the `role` value is used as both the system-facing `identifier` * and the model-facing `representation`. Three accepted forms when provided: * - A plain `string` — used as both `identifier` and `representation`. * - A {@link @nhtio/adk!RawIdentity} object — validated and wrapped into an {@link @nhtio/adk!Identity}. * - An existing {@link @nhtio/adk!Identity} instance — passed through unchanged. */ identity?: string | RawIdentity | Identity; /** When this message was created. */ createdAt: string | number | Date | DateTime; /** When this message was last modified. */ updatedAt: string | number | Date | DateTime; } /** * An immutable, validated conversation message from a human participant or the model. * * @remarks * Covers only `user` and `assistant` roles — system instructions, developer directives, and * tool results are not represented here. Constructed from a {@link RawMessage} via * `rawMessageSchema`. Temporal fields are normalised to Luxon `DateTime` instances at * construction time. Both `content` and `identity.representation` are {@link @nhtio/adk!Tokenizable} so * token cost can be estimated inline. * * A message may carry `content` (text), `attachments` (media), or both. The cross-field rule * on `rawMessageSchema` enforces that at least one is present. Downstream code that reaches * for `message.content` must handle the attachments-only case where `content` is `undefined`. */ export declare class Message { #private; /** * Validator schema that accepts a {@link RawMessage} object. * * @remarks * Reusable fragment for any schema that needs to validate or nest a message entry — for * example, a collection schema that holds an array of messages. */ static schema: import("@nhtio/validation").ObjectSchema; /** * Returns `true` if `value` is a {@link Message} instance. * * @remarks * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety — `instanceof` would fail for instances * created in a different module copy or VM context. * * @param value - The value to test. * @returns `true` when `value` is a {@link Message} instance. */ static isMessage(value: unknown): value is Message; /** Stable unique identifier for this message. */ readonly id: string; /** Whether this message is from the human participant or the model. */ readonly role: MessageRole; /** * The message content as a {@link @nhtio/adk!Tokenizable} for inline token estimation, or `undefined` * for attachments-only messages. * * @remarks * `undefined` when the message was constructed with only `attachments`. Render code that * needs the text portion must guard for the missing case rather than blindly calling * `message.content.toString()`. */ readonly content: Tokenizable | undefined; /** * Media attachments carried by this message. * * @remarks * Always defined as a frozen array — empty when the message has no attachments. Both * `user` and `assistant` messages may carry attachments. Each entry carries its own * `trustTier` and `modalityHazard`; the renderer wraps each in its own trust envelope * independent of the message envelope. */ readonly attachments: ReadonlyArray; /** The identity of the participant who authored this message. */ readonly identity: Identity; /** When this message was created. */ readonly createdAt: DateTime; /** When this message was last modified. */ readonly updatedAt: DateTime; /** * @param raw - The raw message input validated against `rawMessageSchema`. * @throws {@link @nhtio/adk!E_INVALID_INITIAL_MESSAGE_VALUE} when `raw` does not satisfy the schema — * including the cross-field rule that at least one of `content` or `attachments` must be * present and non-empty. */ constructor(raw: RawMessage); /** * Serialise this Message into an `@nhtio/encoder` snapshot. * * @remarks * Emits a {@link RawMessage}-shaped object holding the live nested primitives — `content` * ({@link @nhtio/adk!Tokenizable}), `attachments` ({@link @nhtio/adk!Media}[]), `identity` * ({@link @nhtio/adk!Identity}), and Luxon temporal fields — which the encoder recurses into. A * text-only message round-trips trivially; a message carrying {@link @nhtio/adk!Media} round-trips only * if each attachment's reader is describable (a `fromWebFile`-backed attachment throws * {@link @nhtio/adk!E_READER_NOT_DESCRIBABLE} at encode). The frozen attachments array is copied to a * plain array for the snapshot. Round-trips via {@link Message.[DECODE_METHOD]}. * * @returns A {@link RawMessage}-shaped snapshot. */ [ENCODE_METHOD](): AdkEncodableSnapshot; /** * Reconstruct a {@link Message} from a {@link Message.[ENCODE_METHOD]} snapshot. * * @param data - The snapshot produced by {@link Message.[ENCODE_METHOD]}. * @returns A fully-validated {@link Message}. */ static [DECODE_METHOD](data: AdkEncodableSnapshot): Message; }