/** * ProjectionThreadMessageRepository - Projection repository interface for messages. * * Owns persistence operations for projected thread messages rendered in the * orchestration read model. * * @module ProjectionThreadMessageRepository */ import { ChatAttachment, MessageId, OrchestrationMessageRole, ThreadId, TurnId, IsoDateTime, } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import * as Context from "effect/Context"; import * as Struct from "effect/Struct"; import type * as Option from "effect/Option"; import type * as Effect from "effect/Effect"; import type { ProjectionRepositoryError } from "../Errors.ts"; export const ProjectionThreadMessage = Schema.Struct({ messageId: MessageId, threadId: ThreadId, turnId: Schema.NullOr(TurnId), role: OrchestrationMessageRole, text: Schema.String, attachments: Schema.optional(Schema.Array(ChatAttachment)), isStreaming: Schema.Boolean, createdAt: IsoDateTime, updatedAt: IsoDateTime, }); export type ProjectionThreadMessage = typeof ProjectionThreadMessage.Type; export const AppendStreamingProjectionThreadMessage = Schema.Struct( Struct.omit(ProjectionThreadMessage.fields, ["isStreaming"]), ); export type AppendStreamingProjectionThreadMessage = typeof AppendStreamingProjectionThreadMessage.Type; export const ListProjectionThreadMessagesInput = Schema.Struct({ threadId: ThreadId, }); export type ListProjectionThreadMessagesInput = typeof ListProjectionThreadMessagesInput.Type; export const GetProjectionThreadMessageInput = Schema.Struct({ messageId: MessageId, }); export type GetProjectionThreadMessageInput = typeof GetProjectionThreadMessageInput.Type; export const HasProjectionThreadAssistantMessageInput = Schema.Struct({ threadId: ThreadId, turnId: TurnId, streamingOnly: Schema.Boolean, }); export type HasProjectionThreadAssistantMessageInput = typeof HasProjectionThreadAssistantMessageInput.Type; export const DeleteProjectionThreadMessagesInput = Schema.Struct({ threadId: ThreadId, }); export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type; /** * ProjectionThreadMessageRepositoryShape - Service API for projected thread messages. */ export interface ProjectionThreadMessageRepositoryShape { /** * Insert or replace a projected thread message row. * * Upserts by `messageId`. */ readonly upsert: ( message: ProjectionThreadMessage, ) => Effect.Effect; /** Insert a streaming message or append text to its existing row. */ readonly appendStreaming: ( message: AppendStreamingProjectionThreadMessage, ) => Effect.Effect; /** * Read a projected thread message by id. */ readonly getByMessageId: ( input: GetProjectionThreadMessageInput, ) => Effect.Effect, ProjectionRepositoryError>; /** * Check for an assistant message in a turn without hydrating message text. */ readonly hasAssistantMessageForTurn: ( input: HasProjectionThreadAssistantMessageInput, ) => Effect.Effect; /** * List projected thread messages for a thread. * * Returned in ascending creation order. */ readonly listByThreadId: ( input: ListProjectionThreadMessagesInput, ) => Effect.Effect, ProjectionRepositoryError>; /** Read the latest user-message timestamp without loading message bodies. */ readonly getLatestUserMessageAt: ( input: ListProjectionThreadMessagesInput, ) => Effect.Effect; /** * Delete projected thread messages by thread. */ readonly deleteByThreadId: ( input: DeleteProjectionThreadMessagesInput, ) => Effect.Effect; } /** * ProjectionThreadMessageRepository - Service tag for message projection persistence. */ export class ProjectionThreadMessageRepository extends Context.Service< ProjectionThreadMessageRepository, ProjectionThreadMessageRepositoryShape >()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {}