/**
* 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