import type { StateDatabase } from "./database.js"; import { type JsonValue } from "./json.js"; export declare const WORKFLOW_MESSAGE_SCHEMA: "pi-workflows.workflow-message.v1"; export declare const WORKFLOW_MESSAGE_CONTENT_SCHEMA: "pi-workflows.workflow-message-content.v1"; export declare const WORKFLOW_TURN_SCHEMA: "pi-workflows.workflow-turn.v1"; export type WorkflowMessageKind = "step" | "decision" | "notification" | "terminal" | "followUp"; export type WorkflowMessageStatus = "pending" | "sent" | "cancelled"; export type WorkflowStepReason = "initial" | "resumed" | "reminder"; export type WorkflowTurnState = "started" | "ended"; export type WorkflowTurnStopReason = "completed" | "aborted" | "error" | "lost"; /** * Named candidate filters for the session message walk. Each filter is a superset * of the rows one selection step can use, so the walk never reads rows that step * must skip and the caller still applies the exact rule to every row it receives. */ export type SessionSummaryFilter = "any" | "eligiblePending" | "piWork" | "cancelledStep" | "retainedTerminal"; export type WorkflowMessageContent = { schema: typeof WORKFLOW_MESSAGE_CONTENT_SCHEMA; customType: string; content: string; display: boolean; details: JsonValue; triggerTurn: boolean; }; export type WorkflowMessage = WorkflowMessageSummary & { schema: typeof WORKFLOW_MESSAGE_SCHEMA; content: WorkflowMessageContent; }; /** * One workflow message without its content. Selection and page reads use these * rows so a long session never loads every stored content blob. */ export type WorkflowMessageSummary = { workflowMessageId: string; runId: string; targetSessionId: string; kind: WorkflowMessageKind; sourceId: string; contentDigest: string; /** Mirrored from the content, so selection never reads the blob. */ triggerTurn: boolean; order: number; status: WorkflowMessageStatus; piSessionEntryId: string | null; createdAt: string; updatedAt: string; }; export type WorkflowTurn = { schema: typeof WORKFLOW_TURN_SCHEMA; workflowTurnId: string; workflowMessageId: string; runId: string; targetSessionId: string; state: WorkflowTurnState; stopReason: WorkflowTurnStopReason | null; responseSessionEntryId: string | null; startedAt: string; endedAt: string | null; }; export type WorkflowBranchEntry = { workflowMessageId: string; piSessionEntryId: string; }; export type CreateWorkflowMessageOptions = { workflowMessageId?: string; runId: string; targetSessionId: string; kind: WorkflowMessageKind; sourceId: string; idempotencyKey: string; content: WorkflowMessageContent; now?: number; }; /** Durable workflow content that the server requires Pi to add to one origin session. */ export declare class WorkflowMessageStore { readonly state: StateDatabase; constructor(state: StateDatabase); create(options: CreateWorkflowMessageOptions): WorkflowMessage; get(workflowMessageId: string): WorkflowMessage | undefined; require(workflowMessageId: string): WorkflowMessage; listSession(targetSessionId: string): WorkflowMessage[]; /** Message metadata for one session, in durable order, without content. */ listSessionSummaries(targetSessionId: string): WorkflowMessageSummary[]; /** * A bounded metadata batch of one session's messages in one status, without * content. The caller walks batches in the order it needs, so a long session * history never loads into memory at once. A named candidate filter keeps rows * that no selection step can use out of the walk, so the walk costs the position * of the answer instead of the size of the history. */ listSessionSummaryBatch(targetSessionId: string, options: { status: string | null; kind?: string; filter?: SessionSummaryFilter; cutoff?: number; newestFirst?: boolean; lastOrder?: number; limit: number; }): WorkflowMessageSummary[]; /** Read one session message's metadata by its id, without content. */ readSessionSummary(targetSessionId: string, workflowMessageId: string): WorkflowMessageSummary | undefined; /** Read one session message's metadata by run and kind, without content. */ readSessionSummaryByRun(targetSessionId: string, runId: string, kind: string): WorkflowMessageSummary | undefined; /** Message metadata count for one run, without reading content. */ countForRun(runId: string): number; /** * A bounded metadata page of one run's messages, without content. The caller * reads content only for the rows it shows. */ listRunSummaryPage(runId: string, range: { start: number; limit: number; }): WorkflowMessageSummary[]; /** Read one message's content by its recorded digest. */ materialize(summary: WorkflowMessageSummary): WorkflowMessage; listRun(runId: string): WorkflowMessage[]; /** Message metadata for one run, without content. */ listRunSummaries(runId: string): WorkflowMessageSummary[]; latestForSource(kind: WorkflowMessageKind, sourceId: string): WorkflowMessage | undefined; cancelPendingForSource(sourceId: string, kind?: WorkflowMessageKind, now?: number): number; /** * Make one delivered interactive prompt deliverable again. * * A prompt keeps its identity across a branch change while its request is still * pending. The delivery that left the branch therefore has to become pending * again, or the session waits forever for an answer to a prompt it can no longer * show. The caller names the exact message the recovery found delivered: a * decision prompt keeps its original identity, and a step prompt first returns as * a new resumed message that later keeps its own identity. Only that one message * changes, so a source can never hold two pending messages at once. A terminal or * notification message needs no re-delivery, so it does not come through here. */ reopenMessage(workflowMessageId: string, now?: number): number; adoptBranch(targetSessionId: string, entries: readonly WorkflowBranchEntry[], allowedMessageIds: ReadonlySet, now?: number): WorkflowMessage[]; startTurn(options: { workflowMessageId: string; workflowTurnId?: string; runId: string; targetSessionId: string; now?: number; }): WorkflowTurn; endTurn(options: { workflowMessageId: string; workflowTurnId: string; runId: string; targetSessionId: string; stopReason: WorkflowTurnStopReason; responseSessionEntryId?: string | null; now?: number; }): WorkflowTurn; cancelPendingForRun(runId: string, now?: number, kinds?: readonly WorkflowMessageKind[]): number; getTurn(workflowTurnId: string): WorkflowTurn | undefined; requireTurn(workflowTurnId: string): WorkflowTurn; openTurnForMessage(workflowMessageId: string): WorkflowTurn | undefined; latestTurnForMessage(workflowMessageId: string): WorkflowTurn | undefined; openTurnsForSession(targetSessionId: string): WorkflowTurn[]; private mapMessage; private mapMessageSummary; } export declare function workflowMessageIdFor(kind: WorkflowMessageKind, sourceId: string, idempotencyKey: string): string; export declare function isWorkflowMessageContent(value: unknown): value is WorkflowMessageContent; /** * Accept message content only when it matches its declared digest. Pi reads large * content through bounded chunks, so an incomplete or substituted value must not * reach delivery. */ export declare function verifyWorkflowMessageContent(value: unknown, contentDigest: string): WorkflowMessageContent | undefined;