assistant-cloud
Version:
Cloud integration for assistant-ui
132 lines • 6.45 kB
TypeScript
import type { ReadonlyJSONObject } from "assistant-stream/utils";
import type { AssistantCloud } from "./AssistantCloud.js";
import type { CloudMessage, CloudMessageRun } from "./AssistantCloudThreadMessages.js";
import type { CloudRunEndReason } from "./AssistantCloudThreadRuns.js";
/** How a write relates to the message's run: `running` opens or renews its lease, a reason ends it. */
export type CloudMessageRunPhase = "running" | CloudRunEndReason;
/** How long a running lease may go without a heartbeat before its message reads as stale. */
export declare const DEFAULT_RUN_STALE_AFTER_MS = 30000;
export type CloudMessagePersistenceOptions = {
/**
* Milliseconds without a heartbeat after which a running lease reads as
* stale, which is what a run whose tab went away looks like. Default
* 30 000. The server never expires a run: a late run that beats again
* reads as running again, and its writes still land.
*/
staleAfter?: number | undefined;
};
/** Where a listed message's run stands for a reader: still beating, or quiet for longer than `staleAfter`. */
export type CloudRunState = {
status: "in-progress";
staleInMs: number;
} | {
status: "stale";
};
/**
* Appends, updates and loads cloud messages while mapping local ids to cloud
* ids and chaining parent_id. A parent that is still being created is awaited,
* so concurrent appends land under the right parent. A parent whose create
* failed is created again before its child, and a child whose parent still
* cannot be created is stored as a root.
*
* A message appended as running holds a run lease, renewed by heartbeats on
* the server's interval until a write with an end reason ends it. Heartbeats
* keep running while the page is in the background. The server never
* expires a lease; readers show a run as stale once it has gone `staleAfter`
* without a beat, and a late run keeps writing its own message. A lease that
* is lost (ended elsewhere or refused) is final: the message gets no further
* writes from this persistence.
*/
export declare class CloudMessagePersistence {
private idMapping;
private failedAppends;
/** Every message this persistence opened a lease for; a repeated append of one writes its row instead of storing another. */
private runs;
private runWrites;
/** Writes waiting on an in-flight create, chained so they apply in append order. */
private pendingWrites;
/** The deletes each write waiting on an in-flight create saw start; each resolves to whether it deleted the message. */
private waiters;
private getCloud;
private staleAfter;
constructor(cloud: AssistantCloud, options?: CloudMessagePersistenceOptions);
constructor(getCloud: () => AssistantCloud, options?: CloudMessagePersistenceOptions);
/**
* Reads a listed message's run against `staleAfter`; `sinceReadMs` is the
* time since the list was read. Undefined for no run or an ended one.
*/
runStateOf(run: CloudMessageRun | undefined, sinceReadMs?: number): CloudRunState | undefined;
/**
* Persist a message to the cloud.
*
* @param threadId - Remote thread ID
* @param messageId - Local message ID (used for tracking)
* @param parentId - Local parent message ID (or null for first message)
* @param format - Message format (e.g., "aui/v0", "ai-sdk/v6")
* @param content - Message content (format-specific)
* @param run - `running` stores the message with an open run lease that is
* kept alive by heartbeats until a write with an end reason closes it.
* Once the lease is lost (it was ended elsewhere or the server refused
* it), later writes for the message are dropped.
*/
append(threadId: string, messageId: string, parentId: string | null, format: string, content: ReadonlyJSONObject, run?: CloudMessageRunPhase): Promise<void>;
private resolveParentId;
/**
* Update an already-persisted message in the cloud.
*/
update(threadId: string, messageId: string, _format: string, content: ReadonlyJSONObject, run?: CloudMessageRunPhase): Promise<void>;
private writeRun;
/**
* Moves a stored message's id mapping and run lease to a new local id, for
* runtimes that swap a placeholder's id once the backend names the message.
*/
rename(from: string, to: string): Promise<void>;
/** Whether a run lease this persistence opened for the message is still held. */
hasOpenRun(messageId: string): boolean;
private openRun;
private scheduleHeartbeat;
private heartbeat;
/** The lease is gone for good: stop heartbeating and drop every later write for the message. */
private loseRun;
private closeRun;
/**
* Delete mapped messages from the cloud and clear their local ID mappings.
*/
delete(threadId: string, messageIds: string[]): Promise<void>;
private deleteMessages;
/**
* Check if a message has been persisted (or is currently being persisted).
*/
isPersisted(messageId: string): boolean;
/**
* Get the remote ID for a local message ID (resolved).
* Returns undefined if not persisted.
*/
getRemoteId(messageId: string): Promise<string | undefined>;
getResolvedRemoteId(messageId: string): string | undefined;
record(localId: string, remoteId: string): void;
/**
* Load messages from the cloud and populate the ID mapping.
*
* The list endpoint caps a response at 200 rows, so pages are followed by
* message ID cursor until a short page and concatenated in server order.
*
* The ID mapping is populated so that `isPersisted()` returns true for
* loaded messages, preventing re-persistence of already-stored messages.
*
* A loaded ID that an append already maps keeps the remote ID from that append, and falls back to the loaded ID if the append fails.
*
* @param threadId - Remote thread ID
* @param format - Optional format filter
* @returns Array of cloud messages
*/
load(threadId: string, format?: string): Promise<CloudMessage[]>;
/**
* Reset the ID mapping (call when switching threads).
*
* Pending `load()` and `append()` calls are not cancelled and still settle
* normally, but their results no longer populate the ID mapping.
*/
reset(): void;
}
//# sourceMappingURL=CloudMessagePersistence.d.ts.map