/** * Deployment capability advertisement — what `GET /deployments/:id/config` says * this deployment can do. * * Hand-written to mirror TORUK-CORE's `DeploymentRuntimeCapabilities` * (`deployments/runtime/deployment-runtime.types.ts`). No codegen: the contract * is small, and a hand-written mirror forces a deliberate review whenever CORE * grows a flag. * * ## The absent-vs-false rule * * `enabled` is always present on a capability CORE knows about, including when * it is `false`. An **absent key** means something different: "this server * predates that flag". Both must be treated as disabled by a client, and * `resolveDeploymentCapabilities` collapses them to exactly that — but it never * guesses "unknown, so try". A feature the server has not advertised does not * get switched on speculatively. * * That rule is what lets a new SDK run against an old CORE: with no * `capabilities` block at all, every flag resolves to `false` and the widget * behaves precisely as it did in `0.6.0`, where none of these features were * reachable anyway. The one exception is `streaming` — see * `resolveDeploymentCapabilities`. */ /** Base shape of every capability entry. */ export type RuntimeCapability = { enabled: boolean; }; export type StreamingCapability = RuntimeCapability & { /** * Wire protocols this plane serves. Legacy `{event,data}` SSE is currently the * only external protocol; typed as a widening `string[]` so a future protocol * does not break the type at compile time. */ protocols: string[]; }; export type SessionsCapability = RuntimeCapability & { /** Page-size ceiling the session list DTO enforces. Page, do not truncate. */ maxSessionsPerPage: number; /** Page-size ceiling the session messages DTO enforces. */ maxMessagesPerPage: number; }; export type SpeechToTextCapability = RuntimeCapability & { /** * How to submit audio. `prediction-upload` means: send an `uploads[]` entry * with `type: 'audio'` on `POST :id/predictions`, and the server replaces * `question` with the transcript. There is no standalone transcribe route. */ via: string; }; export type TextToSpeechCapability = RuntimeCapability & { /** `buffered` — one JSON response carrying base64 audio. No chunked protocol. */ mode: string; /** Hard limit. Text above this is rejected with 400, not silently truncated. */ maxInputChars: number; }; export type UploadKindCapability = RuntimeCapability & { /** `null` when the server advertises no ceiling for this kind. */ maxUploadSizeMb: number | null; /** Accepted MIME types or extensions; empty when disabled or unconstrained. */ fileTypes: string[]; }; export type UploadsCapability = { image: UploadKindCapability; ragFile: UploadKindCapability; fullFile: UploadKindCapability; }; export type DeploymentCapabilities = { streaming: StreamingCapability; sessions: SessionsCapability; speechToText: SpeechToTextCapability; textToSpeech: TextToSpeechCapability; uploads: UploadsCapability; feedback: RuntimeCapability; leads: RuntimeCapability; followUpPrompts: RuntimeCapability; /** * Whether this deployment can serve artifacts on the external plane — * `toruk.artifacts.list/get/download`, addressed through a session the * visitor owns. * * CORE reports true only when the deployment has a session plane to scope * reads through AND server-side artifact generation is switched on, so a * false here means artifacts either cannot be authorized or will never be * produced. Either way there is nothing to fetch, and a client should not * render an artifact surface. * * This never grants access to CORE's INTERNAL artifact API * (`/api/v1/artifacts`), which stays owner- and organization-scoped and is * closed to deployment callers regardless of this flag. */ artifacts: RuntimeCapability; /** * Whether the widget should offer a New Chat control. Purely a presentation * switch the deployment owner sets in CORE; nothing on the wire depends on it. * * Like `streaming`, this always worked before the flag existed, so an absent * key resolves to `true` — see `resolveDeploymentCapabilities`. */ newChat: RuntimeCapability; /** * Whether this deployment serves Dynamic UI (the AG-UI wire and its inline * components). CORE pins the legacy wire when this is off, whatever the client * asks for. Absent resolves to `true` for the same reason as `newChat`. */ dynamicUi: RuntimeCapability; }; /** * Envelope version, reported by CORE as `contract.version`. * * Bumped only when the envelope's *shape* changes in a way a client must branch * on. Adding a capability key is not a bump — absent already means disabled. */ export type DeploymentContract = { version: number; }; /** The nine original config fields, plus the capability envelope when present. */ export type DeploymentConfig = { deploymentId?: string; aiEmployeeName?: string | null; aiEmployeeRole?: string | null; theme?: Record; welcomeMessage?: string | null; starterPrompts?: unknown; isStreaming?: boolean; uploadsAllowed?: boolean; authRequired?: boolean; /** `'none'`, `'apiKey'` or `'user'` — the credential this deployment expects from a caller. */ authMode?: string; contract?: DeploymentContract; capabilities?: Partial; [key: string]: unknown; }; /** * Every capability off. This is what an old CORE — one with no `capabilities` * block — resolves to, and it reproduces `0.6.0` behaviour exactly. */ export declare function disabledCapabilities(): DeploymentCapabilities; /** * Read a `/config` body into a fully-populated capability object. * * Never returns a partial: every key is present so callers read * `capabilities.textToSpeech.enabled` without optional chaining and without * having to know which CORE version answered. * * **`streaming` is the one legacy fallback.** Every other flag defaults to * `false`, because before the capability envelope those features were * unreachable and defaulting them on would switch on untested paths against an * old server. Streaming is different: it already worked, gated on the top-level * `isStreaming` field that has always shipped. Defaulting it to `false` would * *remove* working behaviour, so it falls back to `isStreaming`, and then to * `true` — which is what the widget hard-coded before this function existed. * * Unknown future keys are ignored rather than rejected. */ export declare function resolveDeploymentCapabilities(config: unknown): DeploymentCapabilities; /** * Did this server send a capability envelope at all? * * Distinct from "are any capabilities enabled". A caller needs this to choose a * *source*: with an envelope present the capability block is authoritative, and * without one the caller must keep reading the legacy `chatbotConfig`-shaped * keys. `resolveDeploymentCapabilities` cannot express the difference, because * it deliberately normalizes both cases to a fully-populated object. */ export declare function hasCapabilityEnvelope(config: unknown): boolean; /** Envelope version, or `0` when the server predates `contract`. */ export declare function resolveContractVersion(config: unknown): number;