import { COMPILER_META_KEYS, type CompilerMetadata } from '@llui/dom'; import type { SignalComponentHandle } from '@llui/dom'; import type { AgentEffect } from './effects.js'; import type { AgentConfirmState } from './agentConfirm.js'; import type { AgentDocs, AgentContext, AgentToken, MessageAnnotations } from '../protocol.js'; import { type CodecRegistry } from '../codecs.js'; /** * The shape the compiler emits as its msg-schema metadata (keyed by * `COMPILER_META_KEYS.msgSchema`). Mirrors `MsgField` * from `@llui/vite-plugin/src/msg-schema.ts`. Three coexisting forms: * * 1. Bare primitive: `'string' | 'number' | 'boolean' | 'unknown'` * and bare enum: `{enum: [...]}` (values may be string, number, * or boolean — the compiler preserves the literal kind so JSON * round-trips don't lose type info). * 2. Bare nested types: `{kind: 'object', shape}` for inline / * followed-via-typeIndex shapes; `{kind: 'array', element}` for * `T[]` / `readonly T[]` / `Array`; `{kind: 'discriminated- * union', discriminant, variants}` for tagged unions of objects * (e.g. `Format = {kind:'exact'} | {kind:'range', min, max}`). * The synthesizer recurses to build copy-paste-ready nested * examples; the validator walks the same tree. * 3. Rich descriptor: wraps any of the above with `{optional?, * priority?, hint?}` carrying TS optionality and `@should` hints. */ export type MsgSchemaBareType = string | { enum: ReadonlyArray; } | { kind: 'object'; shape: Record; } | { kind: 'array'; element: MsgSchemaBareType; } | { kind: 'discriminated-union'; discriminant: string; variants: Record>; }; export type MsgSchemaField = MsgSchemaBareType | { type: MsgSchemaBareType; optional?: boolean; priority?: 'should'; hint?: string; /** * Boolean JS expression authored with `@validates("expr")` JSDoc. * Has `v` bound to the field value at runtime; the validator * compiles it lazily with `new Function('v', 'return (' + src + * ')')` and caches the function across calls. Use for invariants * the type system can't express — numeric ranges, format * predicates, length bounds. */ validates?: string; }; export type MsgSchemaShape = { discriminant: string; variants: Record>; }; /** * The compiled def as this client reads it. The metadata half is keyed by the * compiler↔runtime ABI (`COMPILER_META_KEYS`, owned by `@llui/dom`): this client * and the compiler that wrote the def routinely land in different bundle chunks, * so the key is taken from the shared table and never spelled as a literal. * `CompilerMetadata` types the annotations loosely (`Record`); * we narrow the one field this client interprets structurally. */ type ComponentMetadata = Omit & { readonly [COMPILER_META_KEYS.msgAnnotations]?: Record; name: string; agentAffordances?: (state: unknown) => Array<{ type: string; [k: string]: unknown; }>; agentDocs?: AgentDocs; agentContext?: (state: unknown) => AgentContext; }; export type CreateAgentClientOpts = { handle: SignalComponentHandle; def: ComponentMetadata; appVersion?: string; rootElement: Element | null; slices: { getConnect: (s: State) => unknown; getConfirm: (s: State) => AgentConfirmState; wrapConnectMsg: (m: unknown) => Msg; wrapConfirmMsg: (m: unknown) => Msg; /** * Optional: wrap an agentLog msg so the client-side activity feed * mirrors what Claude is doing. If omitted, outbound log-append * frames still go to the server, but the local agent.log slice * stays empty (the UI won't show activity). */ wrapLogMsg?: (m: unknown) => Msg; /** * Optional: wrap an agentAttention msg so the visual-attention * slice can clear its spotlight on the auto-clear timer. Hosts * that wire `agentAttention` should set this; hosts that don't * leave it unset and the spotlight (which they aren't rendering) * never matters. The factory uses it for the reverse direction * too: `onLogEntry` re-dispatches the same `Append { entry }` * payload into the attention slice when wired, so a single * incoming `log-append` frame fans out to both slices without * the host needing to write the routing. */ wrapAttentionMsg?: (m: unknown) => Msg; }; /** * Codec registry for non-JSON-safe values (Date, Blob, Map, …) * crossing the LAP boundary. Defaults to `makeDefaultCodecs()` * which ships `iso-date` and `epoch-millis`. Provide a custom * registry to register additional codecs (e.g. `base64-blob` for * file uploads). See `@llui/agent/codecs` for the convention. */ codecs?: CodecRegistry; /** * Redaction hook applied to app state **at the source**, before any * snapshot leaves the browser for the agent/LLM. Runs on every * wire-bound read — `get_state`/`observe`/`query_state`, the * per-change `state-update` broadcast, and confirm-resolution * snapshots — so a secret omitted here never transits the WS, the * server, or the model. Return a redacted COPY (do not mutate the * input); the reducer/app keep the real state. Omit fields, mask * values, or return `{}` to withhold state entirely. This is the * only place that can use the app's own knowledge of which fields * are sensitive — prefer it over any downstream/server-side filter. */ redactState?: (state: State) => State; /** * Payload-validation policy for agent `send_message` dispatches. * `'strict'` rejects payload fields not in the compiled schema and * warns on `'unknown'`-typed fields the agent supplied a value for; * `'lenient'` (default) accepts extras silently. Wired through to the * per-dispatch validator so strict mode is usable in production, not * only in tests. */ dispatchPolicy?: 'strict' | 'lenient'; /** * Base path for agent HTTP endpoints. Default: `'/agent'` (matches * the canonical paths in `@llui/vite-plugin`'s dev middleware and * `@llui/agent/server`). The mint URL, resume URLs, and revoke URL * derive from this so consumers don't have to keep them in sync. * * Override when: * - **Cross-origin agent server**: pass the full base, e.g. * `'https://api.example.com/agent'` or `'http://localhost:8787/agent'`. * - **`@cloudflare/vite-plugin` in dev**: pass `'/cdn-cgi/agent'` * because cloudflare-vite shadows non-`/cdn-cgi/*` routes. */ agentBasePath?: string; /** * Storage adapter for the active session blob. When provided the * framework owns the persist/restore loop end-to-end: writes on * `MintSucceeded`, reads on `start()` (auto-dispatching * `RestoreSession` when a non-expired blob is found), clears on * `Disconnect` / `Revoke` / explicit clear effects. * * Default: `defaultSessionStorage()` — uses `window.sessionStorage` * under the key `'llui-agent:session'`. Tab-scoped (survives * refresh, dies on tab close), which matches how a single-tab * agent connection should behave. * * Pass `null` to opt out entirely; the framework then emits the * `AgentSessionPersist` / `AgentSessionClear` effects unchanged * and the host owns storage. Useful for SSR builds where * `sessionStorage` is undefined and the host wants to no-op the * storage layer. * * Pass a custom adapter for tests, IndexedDB-backed apps, or * environments where `sessionStorage` is unavailable but the * persistence semantics are still wanted (e.g. Web Workers). */ sessionStorage?: AgentSessionStorage | null; }; /** * Tab-lifetime persistence for the active agent session. Reads / * writes a single blob; the framework synchronizes it with the * connect lifecycle so refresh-survival is automatic. Implementations * must be synchronous on the read path so `start()` can decide * whether to dispatch `RestoreSession` before any UI mounts — * otherwise the `idle`-only guard in the reducer might miss the * restore when a `Mint` click races the async lookup. */ export type AgentSessionStorage = { read(): PersistedAgentSession | null; write(session: PersistedAgentSession): void; clear(): void; }; export type PersistedAgentSession = { token: AgentToken; tid: string; lapUrl: string; wsUrl: string; /** Hard-expiry, MILLISECONDS-since-epoch (LAP v2 — see MintResponse). */ expiresAt: number; }; /** * The default `AgentSessionStorage` — wraps `window.sessionStorage` * under a single key and treats parse / type-mismatch failures as * "no session". Returns `null` from the factory when `window` is * undefined (SSR/tests), so calling code never has to feature-detect * the browser environment itself. */ export declare function defaultSessionStorage(storageKey?: string): AgentSessionStorage | null; export type AgentClient = { effectHandler: (effect: AgentEffect) => Promise; start(): void; stop(): void; }; export declare function createAgentClient(opts: CreateAgentClientOpts): AgentClient; export {}; //# sourceMappingURL=factory.d.ts.map