UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

224 lines • 9.96 kB
import type { AttachmentStagingDescription, AttachmentStagingStore, ListStagedAttachmentsInput, ResolveAttachmentInput, StageAttachmentInput, StagedAttachment } from '../spi/attachment-staging.js'; import type { Actor, MessageAttachment } from '../types.js'; /** A media row — `@adonis-agora/media`'s `MediaRecord`. */ export interface MediaRecordLike { id: string; ownerType: string; ownerId: string; collection: string; name: string; fileName: string; mimeType: string; size: number; disk: string; path: string; order: number; customProperties: Record<string, unknown>; conversions: Record<string, unknown>; createdAt: Date; updatedAt: Date; } /** `@adonis-agora/media`'s `MediaStore`, the part used here. */ export interface MediaStoreLike { save(record: MediaRecordLike): Promise<MediaRecordLike>; find(id: string): Promise<MediaRecordLike | null>; delete(id: string): Promise<void>; nextOrder(ownerType: string, ownerId: string, collection: string): Promise<number>; /** Every media row of one owner (in one collection) — what the staged-attachment inventory reads. */ listByOwner?(ownerType: string, ownerId: string, collection?: string): Promise<MediaRecordLike[]>; } /** A Drive disk as `@adonis-agora/media` hands it out, the part used here. */ export interface MediaDiskLike { put(key: string, contents: Uint8Array, options?: { contentType?: string; }): Promise<void>; getBytes(key: string): Promise<Uint8Array>; getUrl(key: string): Promise<string>; getSignedUrl(key: string, options?: { expiresIn?: string | number; contentType?: string; }): Promise<string>; delete(key: string): Promise<void>; /** Needed for resumable uploads only — whether the assembled object has landed. */ exists?(key: string): Promise<boolean>; /** Needed for resumable uploads only — the assembled object's size. */ getMetaData?(key: string): Promise<{ contentLength: number; }>; } /** * `@adonis-agora/media`'s `ResumableUploadManager` (`media.resumable`), the part used here: open a * tus session the bytes then stream to over the media library's own tus routes, or abort one. */ export interface MediaResumableLike { createUpload(input: { disk: string; key: string; size?: number; contentType?: string; metadata?: Record<string, string>; }): Promise<{ id: string; }>; abort(id: string): Promise<void>; } /** What `beginUpload` takes: the file as the client describes it, before any byte moves. */ export interface BeginMediaUploadInput { actor: Actor; filename: string; contentType: string; /** Exact byte length — tus needs it up front, and completion is checked against it. */ size: number; } /** A tus session opened for one attachment. Stream the bytes to `location`, then complete. */ export interface BeginMediaUploadResult { /** The attachment's id — what the chat turn sends as `{ mediaId }`. */ mediaId: string; /** The tus upload id. */ uploadId: string; /** The tus resource to `PATCH` the bytes to (relative to the app's origin). */ location: string; } /** A refusal of an upload request, carrying the HTTP status the route answers with. */ export declare class MediaUploadRefusedError extends Error { readonly status: 400 | 409 | 413 | 415 | 422 | 501; constructor(status: 400 | 409 | 413 | 415 | 422 | 501, message: string); } /** The agent store, the part used here — which media a live message of this actor carries. */ export interface ReferencedMediaLike { referencedMediaIds?(actorRef: string, mediaIds: readonly string[]): Promise<string[]>; } /** What {@link MediaAttachmentStaging} is built from. */ export interface MediaAttachmentStagingDeps { storage: MediaStorageLike; store: MediaStoreLike; /** Lets media referenced from the actor's own threads resolve (see `canAccess`). */ agentStore?: ReferencedMediaLike; /** * `media.resumable` — resumable (tus) uploads. Absent or `null` (`config/media.ts` has no * `uploads.resumable`) → uploads are multipart only. */ uploads?: MediaResumableLike | null; } /** `@adonis-agora/media`'s `StorageManager`, the part used here. */ export interface MediaStorageLike { readonly defaultDisk: string; disk(name?: string): MediaDiskLike; } /** Options for `attachmentStores.media({ … })`. All optional. */ export interface MediaAttachmentsOptions { /** Media collection the uploads go in. Default `agent-attachments`. */ collection?: string; /** Owner type the rows carry (the owner id is the actor id). Default `agent-actor`. */ ownerType?: string; /** Drive disk. Default the media library's default disk. */ disk?: string; /** Per-file size cap. Default 20 MiB. */ maxBytes?: number; /** Content types accepted. Default images (png/jpeg/gif/webp), PDF, plain text, CSV. */ allowedContentTypes?: readonly string[]; /** * `private` (default): urls are signed and short-lived, re-minted on every read. `public`: the * disk's plain url. */ visibility?: 'private' | 'public'; /** Lifetime of a signed url, in seconds. Default 7 days. */ urlExpiresInSeconds?: number; /** Mint the url yourself (a CDN, a proxy route). Wins over `visibility`. */ resolveUrl?: (record: MediaRecordLike, context: { disk: MediaDiskLike; }) => string | Promise<string>; /** * Who may attach a file. By default (`allowed`): the actor that uploaded it, or one a message in * one of the actor's OWN threads already carries (a fork, a regenerate) — the latter needs an * agent store with `referencedMediaIds`, which both shipped stores have. Widen or narrow it here — * e.g. let a support agent attach a customer's upload. */ canAccess?: (input: { record: MediaRecordLike; actor: Actor; allowed: boolean; }) => boolean | Promise<boolean>; /** * Where `@adonis-agora/media` mounts its tus routes (`uploads.resumable.routes.prefix` in * `config/media.ts`). Default `/media/uploads/tus`. The `location` a resumable upload answers is * `<tusBasePath>/<uploadId>`. */ tusBasePath?: string; /** Clock and id seams, for tests. */ clock?: () => Date; idGenerator?: () => string; } export declare const DEFAULT_AGENT_MEDIA_COLLECTION = "agent-attachments"; export declare const DEFAULT_AGENT_MEDIA_OWNER_TYPE = "agent-actor"; export declare const DEFAULT_AGENT_MEDIA_TUS_BASE_PATH = "/media/uploads/tus"; /** * Chat attachments stored as media rows in their own collection, one owner per actor, on any Drive * disk — S3, GCS, R2, the local filesystem. Uploads land at * `<ownerType>/<actor>/<collection>/<id>/<file>`; a send names them by id and {@link resolve} mints * the url the model fetches: the caller's own `resolveUrl`, a public url, a signed url, or — on a * disk that cannot sign — the bytes inline as a `data:` url (logged once: fine for dev, not for * production). */ export declare class MediaAttachmentStaging implements AttachmentStagingStore { private readonly deps; private readonly options; private readonly collection; private readonly ownerType; private readonly maxBytes; private readonly allowed; private readonly now; private readonly newId; private warnedInline; constructor(deps: MediaAttachmentStagingDeps, options?: MediaAttachmentsOptions); describe(): AttachmentStagingDescription; /** * Open a tus session for one file, owned by `actor`. Everything is validated here, before any * byte moves. The record exists from now on, `pending` until {@link completeUpload} sees the bytes. */ beginUpload(input: BeginMediaUploadInput): Promise<BeginMediaUploadResult>; /** * Confirm the bytes of a {@link beginUpload} landed, and whole. `null` for an unknown id or one * that is not `actor`'s; `409` while the bytes are still missing; `422` (and the record dropped) * when what arrived is not the size that was declared. Idempotent on a ready record. */ completeUpload(input: { actor: Actor; mediaId: string; }): Promise<MessageAttachment | null>; /** The actor drops one of their own attachments — aborting its upload if still in flight. */ discard(input: { actor: Actor; mediaId: string; }): Promise<boolean>; private get diskName(); stage(input: StageAttachmentInput): Promise<MessageAttachment>; /** * The attachment for `mediaId`, or `null` when it is unknown or this actor may not use it. The * actor may use media they own, and media a message in one of THEIR threads already carries (a * fork, a regenerate) — never anything else, unless `canAccess` says so. */ resolve(input: ResolveAttachmentInput): Promise<MessageAttachment | null>; /** * The actor's staged attachments, newest first: their own media rows in this store's collection — * sent or not, and a resumable upload still in flight too (it is theirs, and it is staged). Metadata * only; a url is only ever minted by {@link resolve}. */ list(input: ListStagedAttachmentsInput): Promise<StagedAttachment[]>; private canUse; private defaultAccess; /** Delete an upload and its row. */ remove(mediaId: string): Promise<void>; private validate; private pathFor; private saveRecord; private ownRecord; /** * A ready record as-is; a pending one promoted to ready once its bytes are on the disk at exactly * the declared size. `'missing'` while they are not; `'mismatch'` when they arrived at another size. */ private ensureReady; private isOurs; private toAttachment; } //# sourceMappingURL=media-attachment-staging.d.ts.map