Agent API Reference
This page is written for coding agents, LLM tools, and host apps that want generated diagrams to be reliable. If you are pointing an agent at Elucim, give it this page plus the user request.
Agent instruction card
Section titled “Agent instruction card”Copy this into an agent prompt when you want it to create or edit an Elucim scene:
Create or modify an Elucim scene as a normalized `ElucimDocument`.
Rules:- Use `version: "2.0"`.- Use stable element IDs. Sibling order in `scene.children` and `elements[id].children` is the stacking order; later siblings paint on top.- Put element visuals in `elements[id].props`; use `layout` for reusable spatial metadata.- Use semantic color tokens like `$background`, `$surface`, `$title`, `$subtitle`, `$accent`, `$border`, `$muted` when possible.- Do not use React wrapper animation syntax or legacy element types such as `fadeIn`, `draw`, `sequence`, `stagger`, or `parallel`.- Author motion with `timelines` and `stateMachines`.- Make a default state machine for playback when the scene has timelines.- Validate with `validateForAgent()`.- Repair timeline duration issues with `repairDocumentForAgent()`.- Check that motion actually changes with `sampleAnimationForAgent()`.- Check generated copy and spacing with `checkLayoutForAgent()` plus `repairLayoutForAgent()`, or `elucim check-layout` followed by `elucim repair-layout`.- Check that the scene is visible and readable with `inspectSceneForAgent()`.- Check polish with `evaluateSceneForAgent().polish` or `analyzePolish()`.- Add explicit relationship metadata (`intent.target`, `intent.flowFrom`, `intent.flowTo`, `intent.relationship`, `intent.group`) plus layout hints such as `layout.rank` and `layout.locked` when element placement should communicate a flow or explanation.- Prefer composite helpers for common diagram structures instead of assembling raw rectangles and text by hand.- Apply `safe` nudges automatically; treat `review` nudges as human/editor-review changes.- Return the final JSON/YAML document plus any validation or inspection warnings you could not fix.What to import
Section titled “What to import”import { addRevealTimeline, addTimeline, applyAgentCommand, applyAgentCommands, applyNudge, bringElementForward, bringElementToFront, checkLayoutForAgent, createAgentSafeDocument, createComparisonScenePreset, createDocument, createAutoStaggerTimeline, createLoopingStateMachine, createReducedMotionDocument, createSemanticMotionTimeline, createStateSnapshotMotion, createStateMachine, diffDocuments, evaluateSceneForAgent, getAgentOperationCatalog, getElementOrder, getTimelineBounds, holdFinalFrame, lintMotion, inspectPolishHeuristics, inspectSceneForAgent, planMotionBeats, previewBeatDiffs, repairDocumentForAgent, repairLayoutForAgent, reorderElement, sampleAnimationForAgent, sendElementBackward, sendElementToBack, summarizeDocument, suggestDocumentNudges, suggestLayoutRepairsForAgent, createTextCalloutScenePreset, createThreeCardFlowScenePreset, updateElement, validateForAgent,} from '@elucim/dsl/agent';
import { analyzePolish, createAutoLayoutGroupPreset, createBadgePreset, createBoundaryPreset, createCalloutCardPreset, createCardGridPreset, createComparisonTablePreset, createConnectorPreset, createDecisionNodePreset, createProgressiveRevealGroupPreset, createQueueStackPreset, createStepCardPreset, createTextBlockPreset, createTextBoxPreset, createTimelineRoadmapPreset, suggestSemanticLayoutNudges,} from '@elucim/dsl';Use @elucim/dsl/agent for generation workflows. Use @elucim/dsl/react only when rendering in React:
import { DslRenderer } from '@elucim/dsl/react';
<DslRenderer dsl={doc} />;CLI discovery
Section titled “CLI discovery”Agents running from a shell can use the publishable CLI instead of writing one-off scripts:
npx @elucim/cli ops --jsonnpx @elucim/cli validate diagram.elc --jsonnpx @elucim/cli check-layout diagram.elc --jsonnpx @elucim/cli repair-layout diagram.elc --out repaired.elc --jsonnpx @elucim/cli repair-layout diagram.elc --apply-review layout-repair-element-overlap-a-b-move-element --out repaired.elc --jsonnpx @elucim/cli inspect diagram.elc --jsonnpx @elucim/cli nudges diagram.elc --semantic-layout --jsonnpx @elucim/cli polish diagram.elc --apply-safe --out polished.elc --jsonnpx @elucim/cli layout diagram.elc --out laid-out.elc --jsonnpx @elucim/cli add-textbox diagram.elc --id summary --x 80 --y 120 --width 320 --height 120 --text "Bounded copy" --auto-fit shrink --background panel --out diagram.elc --jsonnpx @elucim/cli add-beat diagram.elc --id intro-flow --preset revealFlow --targets a,b,c --out diagram.elc --jsonnpx @elucim/cli sample-beats diagram.elc --timeline intro-flow --beats 4 --jsonops --json returns the CLI command catalog and the same code-backed agent operation catalog exposed by getAgentOperationCatalog(). repair-layout applies safe repairs by default; use --apply-review <id> for one suggested review-level repair, --apply-all-review when a workflow intentionally accepts all review-level nudges, and --max-passes <n> to control iterative re-check/repair passes. If installed globally, the binary is still elucim.
Recommended agent loop
Section titled “Recommended agent loop”- Create a normalized starter document with
createDocument(), or start text-heavy scenes fromcreateTextCalloutScenePreset(),createThreeCardFlowScenePreset(), orcreateComparisonScenePreset()and wrap them withcreateAgentSafeDocument(). - Add meaningful elements with stable IDs, roles, intent, and tokenized colors.
- Order siblings with
reorderElement()or front/back helpers when overlap matters. - Animate with
addTimeline()oraddRevealTimeline(). - Wire playback with
createStateMachine()orcreateLoopingStateMachine(). - Validate with
validateForAgent(). - Repair conservative timeline mistakes with
repairDocumentForAgent(). - Preflight layout with
checkLayoutForAgent()andrepairLayoutForAgent()so safe text wrapping and textbox resizing fixes are applied automatically; treat remaining review suggestions, especially overlaps, as human/editor-review changes. - Inspect quality with
evaluateSceneForAgent()andinspectSceneForAgent().evaluateSceneForAgent()includes layout/text diagnostics inquality.issues, exposes the fullquality.layoutreport andquality.layoutRepairs, and treats layout errors as quality failures. - Polish with
report.polish,suggestDocumentNudges(), andapplyNudge(). - Explain remaining warnings and return the final document.
import { addElement, addRevealTimeline, checkLayoutForAgent, createAgentSafeDocument, createThreeCardFlowScenePreset, createLoopingStateMachine, evaluateSceneForAgent, inspectSceneForAgent, repairDocumentForAgent, repairLayoutForAgent, suggestLayoutRepairsForAgent, validateForAgent,} from '@elucim/dsl/agent';
const safeScene = createThreeCardFlowScenePreset({ id: 'cache-flow', title: 'Cache hit rate', subtitle: 'Use bounded card textboxes before adding motion.', items: [ { id: 'request', title: 'Request arrives', body: 'The app asks for a response with stable request metadata.' }, { id: 'lookup', title: 'Cache lookup', body: 'A cache key checks whether useful context is already available.' }, { id: 'respond', title: 'Respond', body: 'Hits return quickly; misses continue to retrieval or generation.' }, ],});
let doc = createAgentSafeDocument(safeScene, { metadata: { title: 'Cache hit rate', intent: 'Show why a small cache improves request latency.', },});
doc = addRevealTimeline(doc, { id: 'intro', targets: ['cache-flow-title', 'request', 'lookup', 'respond'], preset: 'staggeredFadeIn', duration: 60,}).document;
doc = createLoopingStateMachine(doc, { id: 'main', timelineId: 'intro' }).document;doc = repairDocumentForAgent(doc).document;
const validation = validateForAgent(doc);const layoutCheck = checkLayoutForAgent(doc);const layoutRepairs = suggestLayoutRepairsForAgent(doc, layoutCheck);doc = repairLayoutForAgent(doc).document;const quality = evaluateSceneForAgent(doc);if (!quality.valid) { console.table(quality.issues.map(issue => ({ severity: issue.severity, code: issue.code, path: issue.path, suggestions: issue.suggestions?.join('; '), })));}const heuristics = inspectPolishHeuristics(doc);const inspection = inspectSceneForAgent(doc, { timelineId: 'intro' });const safeNudges = quality.nudges.filter(nudge => nudge.confidence === 'safe');doc = safeNudges.reduce((current, nudge) => applyNudge(current, nudge).document, doc);const semanticLayoutNudges = await suggestSemanticLayoutNudges(doc);Helper categories
Section titled “Helper categories”| Category | Helpers | Use |
|---|---|---|
| Create/edit | createDocument, addElement, updateElement, applyAgentCommand, applyAgentCommands | Build and patch normalized documents without mutating inputs. |
| Order | getElementOrder, reorderElement, bringElementForward, sendElementBackward, bringElementToFront, sendElementToBack | Change sibling order without adding z-index metadata. Later siblings paint on top. |
| Timelines | addTimeline, addRevealTimeline, getTimelineBounds, sampleAnimationForAgent | Create explicit keyframe clips and prove they change properties. |
| Semantic motion | planMotionBeats, createSemanticMotionTimeline, createAutoStaggerTimeline, createStateSnapshotMotion, lintMotion, previewBeatDiffs, createReducedMotionDocument, holdFinalFrame | Compile intent-level verbs and narration beats into ordinary Elucim timelines/state machines, then verify readability and accessibility. |
| State machines | createStateMachine, createLoopingStateMachine | Wire timelines into runtime playback. |
| Validation | validateForAgent, repairDocumentForAgent | Return structured errors, hints, conservative repairs, summaries, validation, and diffs. |
| Quality | checkLayoutForAgent, suggestLayoutRepairsForAgent, evaluateSceneForAgent, inspectSceneForAgent, analyzePolish, inspectPolishHeuristics | Catch text overflow, tiny auto-fit copy, likely overlaps, and return deterministic repair suggestions before broader quality checks. Also catches missing metadata, missing motion, blank frames, tiny scenes, off-canvas elements, zero-size elements, low contrast, graph readability issues, weak hierarchy, and static timelines. inspectPolishHeuristics exposes the raw evidence behind the score: bounds, intersections, overflow, text, colors, graph details, connector continuations, and semantic relationships. |
| Review | summarizeDocument, diffDocuments, suggestDocumentNudges, applyNudge | Give hosts/humans compact context, JSON-patch-shaped diffs, and deterministic polish options. |
| Semantic layout | suggestSemanticLayoutNudges | Use ELK as a review-only layout solver for explicit agent-authored relationships between ordinary elements. |
| Operation catalog | getAgentOperationCatalog | Return the authoring, validation, inspection, polish, and layout operations a host can pass to an agent/tool planner. |
| Presets | createTextCalloutScenePreset, createThreeCardFlowScenePreset, createComparisonScenePreset, createAgentSafeDocument, createConnectorPreset, createTextBlockPreset, createTextBoxPreset, createStepCardPreset, createCardGridPreset, createDecisionNodePreset, createBoundaryPreset, createBadgePreset, createQueueStackPreset, createTimelineRoadmapPreset, createComparisonTablePreset, createAutoLayoutGroupPreset, createProgressiveRevealGroupPreset | Start from semantic, theme-tokenized explanatory building blocks instead of raw shapes. The agent-safe scene presets use bounded textboxes for generated copy by construction. |
Command surface
Section titled “Command surface”applyAgentCommands() accepts this higher-level command union:
type AgentCommand = | { op: 'addElement'; element: AgentElementSpec } | { op: 'updateElement'; id: string; patch: AgentElementPatch } | { op: 'moveElement'; id: string; layout: Partial<AgentLayout> } | { op: 'deleteElement'; id: string } | { op: 'reorderElement'; id: string; index: number } | { op: 'bringElementForward'; id: string } | { op: 'sendElementBackward'; id: string } | { op: 'bringElementToFront'; id: string } | { op: 'sendElementToBack'; id: string } | { op: 'reparentElement'; id: string; parentId?: string; index?: number } | { op: 'addTimeline'; timeline: AgentTimelineSpec } | { op: 'addRevealTimeline'; timeline: AgentRevealTimelineSpec } | { op: 'createStateMachine'; stateMachine: AgentStateMachineSpec } | { op: 'updateMetadata'; metadata: Partial<AgentMetadata> } | { op: 'applyNudge'; id: string };Commands return { document, changed, summaries, validation }. They are deterministic and pure; keep the returned document.
Scene inspection
Section titled “Scene inspection”inspectSceneForAgent() is the agent’s lightweight “can I see it?” check. It does not use a browser. It samples timeline frames, estimates element bounds, computes visible element count and occupied area, and reports agent-readable issues.
const report = inspectSceneForAgent(doc, { timelineId: 'intro', frames: [0, 30, 60],});
if (report.issues.length > 0) { console.table(report.issues.map(issue => ({ severity: issue.severity, code: issue.code, path: issue.path, message: issue.message, })));}Issue codes include:
| Code | Meaning |
|---|---|
invalid-document | Validation failed. Fix this before rendering. |
no-visible-elements | A sampled frame has elements but nothing visible in the scene. |
tiny-scene | Visible content occupies too little of the viewport. |
crowded-scene | Visible content fills nearly the whole viewport. |
off-canvas-elements | Elements have measurable bounds outside the scene. |
zero-size-elements | Elements have no measurable bounds. |
low-contrast-elements | Literal colors appear low contrast against literal background colors. |
static-animation | Sampled timeline frames do not change properties. |
Diagram polish
Section titled “Diagram polish”Polish is deterministic. Elucim does not ask an LLM to judge aesthetics; it scores inspectable document signals and suggests command-backed nudges that agents, hosts, or the editor can preview.
import { analyzePolish, applyNudge, createCalloutCardPreset, suggestDocumentNudges,} from '@elucim/dsl';
const polish = analyzePolish(doc);const nudges = suggestDocumentNudges(doc);const safe = nudges.filter(nudge => nudge.confidence === 'safe');doc = safe.reduce((current, nudge) => applyNudge(current, nudge).document, doc);evaluateSceneForAgent(doc) also includes quality.layout, quality.layoutRepairs, and quality.polish, so agents can return one quality object with validation, layout/text diagnostics, deterministic layout repair suggestions, nudges, summary, and polish diagnostics. Layout issue codes such as text-overflows-width, raw-text-too-wide, text-layout-width-without-maxwidth, textbox-overflow, textbox-tiny-font, and element-overlap appear in quality.issues and affect the quality score.
Polish categories include:
| Category | What it checks |
|---|---|
layout | Off-canvas elements and measurable overlaps. |
hierarchy | Whether the title/primary text is visually and semantically dominant. |
readability | Very small text. |
contrast | Overuse of literal colors instead of semantic tokens. |
graph | Graph node overlaps and edge crossings. |
structure | Missing document title or intent metadata. |
motion | Missing reveal/intro motion for generated scenes. |
Nudge confidence matters:
- Apply
safenudges automatically when the user asked for a polished result. These are low-risk changes such as strengthening title hierarchy or fixing tiny text. - Surface
reviewnudges for confirmation or editor review. Graph layout nudges can move many nodes even though they preserve the graph structure.
For explanatory structure, prefer semantic presets. They emit ordinary editable elements, groups, and timelines, not opaque custom schema nodes. For agent-generated copy, prefer the scene-level safe presets first because they allocate bounded text regions and avoid raw SVG text for prose.
const safeCallout = createTextCalloutScenePreset({ id: 'takeaway', title: 'Bounded text first', body: 'Agent-generated scenes should start with explicit text regions so copy can wrap, shrink, truncate, and be checked deterministically.', callout: 'Run checkLayoutForAgent() and repairLayoutForAgent() before rendering.',});
const step = createStepCardPreset({ id: 'draft', x: 80, y: 120, title: 'Draft', body: 'Start with semantic structure.', index: 1,});
const review = createStepCardPreset({ id: 'review', x: 360, y: 120, title: 'Review', body: 'Polish and adjust in the editor.', index: 2,});
const connector = createConnectorPreset({ id: 'draft-to-review', from: { id: 'draft', bounds: { x: 80, y: 120, width: 220, height: 140 } }, to: { id: 'review', bounds: { x: 360, y: 120, width: 220, height: 140 } }, label: 'then', lineStyle: 'dashed', endCap: 'arrow',});Use semantic roles and intent (role: "title", role: "callout", intent.importance: "primary" | "secondary" | "supporting") plus theme tokens ($title, $surface, $primary, $muted) so polish can preserve meaning while improving presentation.
The focused CLI exposes the most common composites for file-based workflows:
npx @elucim/cli add-step-card diagram.elc --id draft --x 80 --y 120 --title "Draft" --body "Semantic card" --out diagram.elc --jsonnpx @elucim/cli add-connector diagram.elc --id draft-to-review --from draft --to review --line-style dashed --start-cap dot --end-cap arrow --out diagram.elc --jsonSemantic motion and beat verification
Section titled “Semantic motion and beat verification”Agents should prefer semantic motion verbs over hand-authored keyframes for common explanatory beats. The helpers still emit ordinary timelines and state machines.
const beats = planMotionBeats({ seconds: 12, fps: 30, beats: [ { id: 'intro', role: 'intro', targets: ['title'] }, { id: 'context', role: 'context', targets: ['draft', 'review'] }, { id: 'feedback', role: 'feedback', targets: ['draft-to-review'] }, { id: 'takeaway', role: 'takeaway', targets: ['summary'] }, ],});
const timeline = createSemanticMotionTimeline(doc, { id: 'feedback-loop', preset: 'revealFlow', targets: ['draft', 'review', 'summary'], duration: 90,});
const handoff = createSemanticMotionTimeline(doc, { id: 'draft-to-review-handoff', preset: 'handoff', from: 'draft', to: 'review', connectorId: 'draft-to-review',});
const motionLint = lintMotion({ ...doc, timelines: { [timeline.id]: timeline, [handoff.id]: handoff } });const beatDiffs = previewBeatDiffs({ ...doc, timelines: { [timeline.id]: timeline } }, { timelineId: timeline.id, beats });const reduced = createReducedMotionDocument({ ...doc, timelines: { [timeline.id]: timeline } }, { mode: 'minimal' });Preset names are revealFlow, emphasizeDecision, tracePath, loopOnce, handoff, drainQueue, and compareBeforeAfter. Motion lint reports blank first frames, too-fast transitions, simultaneous overload, hidden labels, flashing, excessive motion, static timelines, and missing reduced-motion fallbacks. Beat diffs summarize what appears, disappears, moves, or changes at each named beat.
Semantic ELK layout
Section titled “Semantic ELK layout”For diagrams that are not graph primitives, agents should author semantic relationships and ask Elucim for an ELK-backed review nudge:
const doc = { version: '2.0', scene: { type: 'player', children: ['request', 'cache-check', 'database', 'cache-note'] }, elements: { request: { id: 'request', type: 'rect', role: 'step', intent: { role: 'step', importance: 'primary', flowTo: ['cache-check'] }, layout: { x: 100, y: 200, width: 180, height: 80, rank: 1 }, props: { x: 100, y: 200, width: 180, height: 80, fill: '$surface' }, }, 'cache-check': { id: 'cache-check', type: 'rect', role: 'decision', intent: { role: 'decision', importance: 'primary', flowFrom: ['request'], flowTo: ['database'] }, layout: { x: 320, y: 200, width: 200, height: 90, rank: 2 }, props: { x: 320, y: 200, width: 200, height: 90, fill: '$surface' }, }, database: { id: 'database', type: 'rect', role: 'step', intent: { role: 'step', importance: 'supporting', flowFrom: ['cache-check'] }, layout: { x: 580, y: 200, width: 180, height: 80, rank: 3 }, props: { x: 580, y: 200, width: 180, height: 80, fill: '$surface' }, }, 'cache-note': { id: 'cache-note', type: 'text', role: 'callout', intent: { role: 'callout', target: 'cache-check', relationship: 'explains' }, layout: { x: 320, y: 340, width: 260, height: 44 }, props: { x: 320, y: 340, content: 'A hit avoids database work', fontSize: 18, fill: '$muted' }, }, },};
const [nudge] = await suggestSemanticLayoutNudges(doc);The semantic layout adapter builds a virtual graph from target, flowFrom, flowTo, layout.rank, and unambiguous line/curve connectors, runs ELK, then returns normal updateElement commands. layout.locked: true prevents an element from being moved by the returned commands.
Repair policy
Section titled “Repair policy”repairDocumentForAgent() is intentionally conservative. It currently extends timeline durations when keyframes exceed the declared duration and returns:
{ document: AgentDocument; changed: boolean; summaries: string[]; validation: AgentValidationResult; diff: JsonPatchOperation[];}Do not pretend it fixes every quality problem. Use inspection.issues and validation.errors to decide what the agent should fix next.
Output contract for agents
Section titled “Output contract for agents”When returning work to a user or host app, prefer this shape:
{ document: ElucimDocument; validation: ReturnType<typeof validateForAgent>; quality: ReturnType<typeof evaluateSceneForAgent>; inspection: ReturnType<typeof inspectSceneForAgent>; notes: string[];}The document is the artifact. Validation, quality, inspection, and notes explain confidence and remaining risk.
Common mistakes to avoid
Section titled “Common mistakes to avoid”- Do not create wrapper elements like
fadeIn,draw,stagger,parallel, orsequence. - Do not put animation props such as
fadeIn,draw, orwriteon elements. - Do not leave timelines without a state machine if the scene should play in a viewer.
- Do not use unstable generated IDs if the user may ask for follow-up edits.
- Do not rely on literal colors everywhere; prefer semantic tokens.
- Do not stop at
validateForAgent(); a valid scene can still be blank, tiny, static, or unreadable.
Related docs
Section titled “Related docs”- Agent documents explains the normalized document model and state-machine semantics in more depth.
- Renderer shows how hosts render the final document.
- Validation explains structured validation errors.
- Editor overview shows how generated documents round-trip through the visual editor.