UNPKG

@adonis-agora/agent

Version:

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

125 lines • 5.73 kB
import { validatePropsSync } from './schema.js'; /** The id of the child at `index` of the node `parentId` (the root is `root`). */ export function treeNodeId(parentId, index) { return `${parentId}.${index}`; } function isRecord(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); } /** * The part of a streaming `ui__render` input worth drawing: every node whose `type` has fully * arrived and names a component of `catalog` (the negotiated one: what this client draws), with its * props so far — unvalidated, and flagged `incomplete` while still open. A node whose component * streams `complete` is a `held` placeholder until it closes. * * `{ root: null }` while nothing is drawable yet. `null` when the input can no longer become a tree * this client draws (an unknown or undrawable component, children where none are taken, past the * size limits): the preview stops there, and the final push decides what is shown. */ export function partialTree(catalog, input, options = {}) { const maxDepth = options.limits?.maxDepth ?? 12; const maxNodes = options.limits?.maxNodes ?? 200; const fallback = options.streaming ?? 'complete'; let nodes = 0; let broken = false; const visit = (value, id, depth) => { if (broken) return undefined; if (!isRecord(value)) { // An element that is not an object will never validate. if (value !== undefined) broken = true; return undefined; } const open = input.isOpen(value); const type = value.type; // `"type": "Car` may yet be `"CardGrid"`: wait for the closing quote. if (typeof type !== 'string' || (open && input.pendingMember(value) === 'type')) { if (!open) broken = true; return undefined; } const definition = catalog.get(type); nodes += 1; if (definition === undefined || definition.internal === true || nodes > maxNodes || depth > maxDepth) { broken = true; return undefined; } const mode = definition.streaming ?? fallback; if (mode === 'complete') { // Drawn only whole — and only once its props would pass (a display gate; the final tree is // validated on its own, as every tree is). One exception: a layout around a component that // streams `partial` of its own accord (a sandbox) is drawn as soon as ITS props are whole, so // what it holds can stream — its children each follow their own mode. const opens = open && definition.children === true && propsSettled(value) && streamsWithin(value, depth); const checked = open && !opens ? undefined : validatePropsSync(definition.props, value.props ?? {}, catalog.validator); if (checked === undefined || !checked.ok) { return { id, type, props: {}, incomplete: true, held: true }; } const children = childrenOf(value, id, depth, definition.children === true); return { id, type, props: checked.value, ...(children !== undefined && children.length > 0 ? { children } : {}), ...(open ? { incomplete: true } : {}), }; } const raw = isRecord(value.props) ? value.props : {}; const props = definition.partialProps !== undefined && (open || input.isOpen(raw)) ? definition.partialProps(raw, input) : raw; const children = childrenOf(value, id, depth, definition.children === true); return { id, type, props, ...(children !== undefined && children.length > 0 ? { children } : {}), ...(open ? { incomplete: true } : {}), }; }; // A node's own props will not change any more: written and closed, or never written while its // children already are (a model writes `props` before `children`). const propsSettled = (value) => isRecord(value.props) ? !input.isOpen(value.props) : value.props === undefined && Array.isArray(value.children); // Somewhere under `value` (as written so far) is a component that streams `partial` itself. const streamsWithin = (value, depth) => { if (depth >= maxDepth || !Array.isArray(value.children)) return false; return value.children.some((child) => { if (!isRecord(child) || typeof child.type !== 'string') return false; if (input.isOpen(child) && input.pendingMember(child) === 'type') return false; return catalog.get(child.type)?.streaming === 'partial' || streamsWithin(child, depth + 1); }); }; const childrenOf = (value, id, depth, takesChildren) => { const raw = value.children; // A stringified `children` (a model quirk the final validation repairs) cannot be drawn yet. if (!Array.isArray(raw)) return undefined; if (raw.length > 0 && !takesChildren) { broken = true; return undefined; } const children = []; for (const [index, child] of raw.entries()) { const next = visit(child, treeNodeId(id, index), depth + 1); if (next !== undefined) children.push(next); } return children; }; const root = visit(input.value, 'root', 1); if (broken) return null; return { root: root ?? null }; } //# sourceMappingURL=progressive.js.map