The format

Validation and repair

Every line is checked against your app's components as it streams, and the API asks the same model to rewrite only the lines that break a rule.

View as Markdown

A model gets things wrong: an attribute that doesn't exist, a value outside its list, a component it made up. The core catches each one on its line, as the answer streams, and the API repairs only those lines.

Lenient where the intent is clear

  • Values match without case, and a few synonyms count: error means danger, column means bar. These are reported as notes, not errors.
  • A bad value is fixed at its edge: the default if there is one, or an optional attribute is dropped. A bad table row drops only that row.
  • An unknown component is dropped and its content kept as text, so nothing the model said is lost.
  • Names models use on their own (<Table>, <Column>, <Buttons>) are read as what they mean.

Strict where it isn't

Each problem is a diagnostic on its line, with a class:

Class What it means
hallucinated-component a component that isn't in your catalog
signature-mismatch an attribute the component doesn't have, or of the wrong kind
required-field a required attribute left out
enum-mismatch a value outside an attribute's list, with no default to fall back on
malformed-syntax a tag that doesn't close, or closes the wrong thing
unreadable-tag a line that opens like a component but isn't a tag the core can read
unsafe-url an address that could run code, like javascript:

A cut-off answer is reported as truncated, never passed off as whole.

Repair

When an answer has errors, the API sends the model the broken lines with what's wrong with each and asks for those lines again, with nothing else rewritten. Broken nesting, like a tab closed with its parent's tag, is mended with no model at all.

  • It's on by default. "repair": false in the options turns it off.
  • A blank answer gets one fresh try.
  • A repair runs on the same model and key as the answer, and counts as part of the same call.
  • It stops after 30 seconds and keeps what it has.

What you see

Through /v1/ui (what connectAgent reads), a repaired answer replaces the one that streamed, and a morphscreen.repair event says what changed. You don't need to do anything.

Through /v1/chat/completions, the last chunk's morphscreen field carries the core's verdict, and when a repair was needed, the repaired text:

JSON
{
  "morphscreen": {
    "valid": true,
    "truncated": false,
    "components": 12,
    "diagnostics": [],
    "repair": { "repaired": true, "errors_before": 2, "rounds": 1, "nesting": false, "ms": 1840, "lines": [7, [12, 14]], "text": "...", "valid": true }
  }
}

Draw repair.text in place of what streamed. Without streaming, the answer you get is already the repaired one.

On the dashboard

Every call's line shows whether its answer was valid, which components had errors, and what repair did: the dashboard.