/** * Hosted HTTP binding — {@link ThreadPersistencePort} over guuey's thread * API, `POST /v1/thread-store/` (guuey#208). The "eject the code, * keep your memory" leg of the ejected ladder: point a `ThreadStore` at * this binding with an end-user token and your self-hosted agent reads and * writes the SAME conversation rows guuey's hosted runtime does. * * Wire protocol (the `@guuey/state` `HttpKv` shape): * - Request: JSON body `{ context: { appId }, args: {...} }`, header * `Authorization: Bearer `. * - Success: `200 { result }`. * - Failure: `4xx/5xx { code, message }` → {@link HttpThreadStoreError}. * * Auth + tenancy: the token is an END-USER token for the app — one the * app's configured identity issuer minted (your own IdP, or guuey's per-app * widget issuer via `@guuey/widget-auth`). The server verifies it against * that issuer, derives the same `byo_…` userId the hosted runtime would, and * scopes every op to `(appId, userId)`: rows you write must carry that * userId, threads you touch must belong to it. `context` on the wire is * advisory — the token is authoritative. Call {@link scope} to learn the * derived userId (and the API's region) before `ensureThread`. * * Retry policy: reads (`getThread`/`listRecentMessages`/ * `findByClientMessageId`/`getSnapshot`/`scope`) are idempotent and retried * once on a network failure or a 5xx. Writes (`createThread`/`incrementSeq`/ * `putMessage`/`putSnapshot`) are NEVER retried automatically — * `incrementSeq` is not idempotent, and a blind retry after a server-side * apply would double-allocate a seq. */ import type { ThreadMessageRow, ThreadPersistencePort, ThreadRow, ThreadSnapshotRow } from "./rows.js"; /** What the token resolves to server-side — the scope every op is bound to. */ export interface ThreadScope { appId: string; /** The derived end-user id (`byo_…`) — the `userId` to build rows with. */ userId: string; /** The API's serving region — the `region` to mint fresh threads with. */ region: string; } export interface HttpThreadPersistenceOptions { /** API origin, e.g. `https://api.us-east-1.guuey.com` (no `/v1`). */ baseUrl: string; /** The guuey app whose conversations these are. */ appId: string; /** The end-user's bearer token (see the module doc). */ token: string; /** Injection seam for tests / custom transports. Defaults to global `fetch`. */ fetchImpl?: typeof fetch; } /** * Thrown for every non-2xx response and every exhausted network failure. * `code` is the server's flat error code (`UNAUTHORIZED`, `FORBIDDEN`, * `INVALID_ARGUMENT`, `INVALID_CONTEXT`, `THREAD_NOT_FOUND`, `CONFLICT`, * `NOT_FOUND`, `TRANSPORT`); `status` is the HTTP status (`0` for a network * failure that never produced a response). */ export declare class HttpThreadStoreError extends Error { readonly status: number; readonly code: string; constructor(status: number, code: string, message: string, options?: { cause?: unknown; }); } export declare class HttpThreadPersistence implements ThreadPersistencePort { private readonly baseUrl; private readonly appId; private readonly token; private readonly fetchImpl; constructor(opts: HttpThreadPersistenceOptions); /** The `(appId, userId, region)` the server bound this token to. */ scope(): Promise; getThread(threadId: string): Promise; createThread(row: ThreadRow): Promise; incrementSeq(threadId: string, preview: string | null, atIso: string): Promise; putMessage(row: ThreadMessageRow): Promise; listRecentMessages(threadId: string, limit: number): Promise; findByClientMessageId(threadId: string, clientMessageId: string): Promise; getSnapshot(threadId: string): Promise; putSnapshot(row: ThreadSnapshotRow): Promise; /** * One shared retry budget (`retried`) covers BOTH retry triggers — a * network failure and a 5xx response — so a retryable op makes at most * 2 total requests, never 3. */ private call; } //# sourceMappingURL=http.d.ts.map