Skip to content

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.

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.
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} />;

Agents running from a shell can use the publishable CLI instead of writing one-off scripts:

Terminal window
npx @elucim/cli ops --json
npx @elucim/cli validate diagram.elc --json
npx @elucim/cli check-layout diagram.elc --json
npx @elucim/cli repair-layout diagram.elc --out repaired.elc --json
npx @elucim/cli repair-layout diagram.elc --apply-review layout-repair-element-overlap-a-b-move-element --out repaired.elc --json
npx @elucim/cli inspect diagram.elc --json
npx @elucim/cli nudges diagram.elc --semantic-layout --json
npx @elucim/cli polish diagram.elc --apply-safe --out polished.elc --json
npx @elucim/cli layout diagram.elc --out laid-out.elc --json
npx @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 --json
npx @elucim/cli add-beat diagram.elc --id intro-flow --preset revealFlow --targets a,b,c --out diagram.elc --json
npx @elucim/cli sample-beats diagram.elc --timeline intro-flow --beats 4 --json

ops --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.

  1. Create a normalized starter document with createDocument(), or start text-heavy scenes from createTextCalloutScenePreset(), createThreeCardFlowScenePreset(), or createComparisonScenePreset() and wrap them with createAgentSafeDocument().
  2. Add meaningful elements with stable IDs, roles, intent, and tokenized colors.
  3. Order siblings with reorderElement() or front/back helpers when overlap matters.
  4. Animate with addTimeline() or addRevealTimeline().
  5. Wire playback with createStateMachine() or createLoopingStateMachine().
  6. Validate with validateForAgent().
  7. Repair conservative timeline mistakes with repairDocumentForAgent().
  8. Preflight layout with checkLayoutForAgent() and repairLayoutForAgent() so safe text wrapping and textbox resizing fixes are applied automatically; treat remaining review suggestions, especially overlaps, as human/editor-review changes.
  9. Inspect quality with evaluateSceneForAgent() and inspectSceneForAgent(). evaluateSceneForAgent() includes layout/text diagnostics in quality.issues, exposes the full quality.layout report and quality.layoutRepairs, and treats layout errors as quality failures.
  10. Polish with report.polish, suggestDocumentNudges(), and applyNudge().
  11. 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);
CategoryHelpersUse
Create/editcreateDocument, addElement, updateElement, applyAgentCommand, applyAgentCommandsBuild and patch normalized documents without mutating inputs.
OrdergetElementOrder, reorderElement, bringElementForward, sendElementBackward, bringElementToFront, sendElementToBackChange sibling order without adding z-index metadata. Later siblings paint on top.
TimelinesaddTimeline, addRevealTimeline, getTimelineBounds, sampleAnimationForAgentCreate explicit keyframe clips and prove they change properties.
Semantic motionplanMotionBeats, createSemanticMotionTimeline, createAutoStaggerTimeline, createStateSnapshotMotion, lintMotion, previewBeatDiffs, createReducedMotionDocument, holdFinalFrameCompile intent-level verbs and narration beats into ordinary Elucim timelines/state machines, then verify readability and accessibility.
State machinescreateStateMachine, createLoopingStateMachineWire timelines into runtime playback.
ValidationvalidateForAgent, repairDocumentForAgentReturn structured errors, hints, conservative repairs, summaries, validation, and diffs.
QualitycheckLayoutForAgent, suggestLayoutRepairsForAgent, evaluateSceneForAgent, inspectSceneForAgent, analyzePolish, inspectPolishHeuristicsCatch 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.
ReviewsummarizeDocument, diffDocuments, suggestDocumentNudges, applyNudgeGive hosts/humans compact context, JSON-patch-shaped diffs, and deterministic polish options.
Semantic layoutsuggestSemanticLayoutNudgesUse ELK as a review-only layout solver for explicit agent-authored relationships between ordinary elements.
Operation cataloggetAgentOperationCatalogReturn the authoring, validation, inspection, polish, and layout operations a host can pass to an agent/tool planner.
PresetscreateTextCalloutScenePreset, createThreeCardFlowScenePreset, createComparisonScenePreset, createAgentSafeDocument, createConnectorPreset, createTextBlockPreset, createTextBoxPreset, createStepCardPreset, createCardGridPreset, createDecisionNodePreset, createBoundaryPreset, createBadgePreset, createQueueStackPreset, createTimelineRoadmapPreset, createComparisonTablePreset, createAutoLayoutGroupPreset, createProgressiveRevealGroupPresetStart from semantic, theme-tokenized explanatory building blocks instead of raw shapes. The agent-safe scene presets use bounded textboxes for generated copy by construction.

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.

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:

CodeMeaning
invalid-documentValidation failed. Fix this before rendering.
no-visible-elementsA sampled frame has elements but nothing visible in the scene.
tiny-sceneVisible content occupies too little of the viewport.
crowded-sceneVisible content fills nearly the whole viewport.
off-canvas-elementsElements have measurable bounds outside the scene.
zero-size-elementsElements have no measurable bounds.
low-contrast-elementsLiteral colors appear low contrast against literal background colors.
static-animationSampled timeline frames do not change properties.

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:

CategoryWhat it checks
layoutOff-canvas elements and measurable overlaps.
hierarchyWhether the title/primary text is visually and semantically dominant.
readabilityVery small text.
contrastOveruse of literal colors instead of semantic tokens.
graphGraph node overlaps and edge crossings.
structureMissing document title or intent metadata.
motionMissing reveal/intro motion for generated scenes.

Nudge confidence matters:

  • Apply safe nudges automatically when the user asked for a polished result. These are low-risk changes such as strengthening title hierarchy or fixing tiny text.
  • Surface review nudges 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:

Terminal window
npx @elucim/cli add-step-card diagram.elc --id draft --x 80 --y 120 --title "Draft" --body "Semantic card" --out diagram.elc --json
npx @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 --json

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.

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.

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.

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.

  • Do not create wrapper elements like fadeIn, draw, stagger, parallel, or sequence.
  • Do not put animation props such as fadeIn, draw, or write on 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.
  • 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.