@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
81 lines • 3.94 kB
JavaScript
import { COMPONENT_NAME, defineCatalog } from './catalog.js';
import { formatIssues } from './schema.js';
import { componentToText, treeToText } from './text.js';
import { GENUI_TREE_COMPONENT, validateTree } from './tree.js';
export function validateUiCapabilities(value) {
if (!record(value) ||
Object.keys(value).some((key) => key !== 'components') ||
!Array.isArray(value.components))
throw new TypeError('Invalid UI capabilities');
const seen = new Set();
const components = value.components.map((component) => {
if (!record(component) ||
Object.keys(component).some((key) => key !== 'name' && key !== 'version') ||
typeof component.name !== 'string' ||
!COMPONENT_NAME.test(component.name) ||
!Number.isSafeInteger(component.version) ||
Number(component.version) <= 0 ||
seen.has(component.name))
throw new TypeError('Invalid UI component capability');
seen.add(component.name);
return { name: component.name, version: Number(component.version) };
});
return { components };
}
function record(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
export function negotiateCatalog(catalog, capabilities) {
if (capabilities === undefined)
return catalog;
const supported = validateUiCapabilities(capabilities).components;
return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), { jsonSchemaValidator: catalog.validator });
}
/** Validate against current trusted definitions before drawing or generating complete text. */
export async function prepareUiEmission(catalog, capabilities, component, props, version) {
const drawable = negotiateCatalog(catalog, capabilities);
if (component === GENUI_TREE_COMPONENT) {
if (version !== undefined && version !== 1)
throw new Error('Invalid UI tree version');
const validated = await validateTree(catalog, props.root);
if (!validated.ok)
throw new Error(`Invalid UI tree: ${formatIssues(validated.issues)}`);
const supported = (node) => drawable.has(node.type) && (node.children ?? []).every(supported);
const versions = new Map();
const collectVersions = (node) => {
versions.set(node.type, catalog.get(node.type)?.version ?? 1);
for (const child of node.children ?? [])
collectVersions(child);
};
collectVersions(validated.value);
return supported(validated.value)
? {
kind: 'ui',
component,
props: { root: validated.value },
version: 1,
fallbackText: treeToText(catalog, validated.value),
componentVersions: Object.fromEntries(versions),
}
: { kind: 'text', text: treeToText(catalog, validated.value) };
}
const definition = catalog.get(component);
if (definition === undefined)
throw new Error(`Unknown UI component "${component}"`);
const currentVersion = definition.version ?? 1;
if (version !== undefined && version !== currentVersion)
throw new Error(`Invalid UI component version for "${component}"`);
const validated = await catalog.validate(component, props);
if (!validated.ok)
throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
return drawable.has(component)
? {
kind: 'ui',
component,
props: validated.value,
version: currentVersion,
fallbackText: componentToText(catalog, component, validated.value),
}
: { kind: 'text', text: componentToText(catalog, component, validated.value) };
}
//# sourceMappingURL=capabilities.js.map