Skip to main content
This document covers custom_interface, a display-only tool that renders prior step outputs through a custom Handlebars HTML template in the task feed. The template is not pre-authored: on the first run the tool sees the actual data, generates a template with an LLM from the user’s UI description, and freezes it into the tool’s configuration; subsequent runs reuse the frozen template with no generation call.

When to use it (vs app_tool vs custom_connector)

  • custom_interface — display-only. Renders data from prior steps as a read-mostly HTML view (cards, tables, side-by-side comparisons) with citation highlighting and inline value editing. It performs no external calls and drives no workflow logic.
  • app_tool — interactive. A configured-tool kind backed by a generated application whose runtime calls go through /api/app-runtime (gated by the binding’s allowlist). Use when the user needs a working mini-app, not a rendered view.
  • custom_connector — integration. Connects an agent to an external HTTP API (auth, endpoints, schemas). Its optional UI template renders a connector result; it exists to fetch/push data, not to display prior step outputs.

Authentication and enablement

No integration or credentials — the tool only reads data already present in the task. It is a configured tool: each instance is created from the agent’s tool settings with a display name and a UI description (e.g. “side-by-side PO vs Invoice comparison”), stored as bindings (ui_description, title, display_name). Off by default — enable it in the agent’s Tools panel.

Inputs

Required:
  • variables — the prior step outputs to display. Each item is {name, ref} where ref is a ${VAR_N} reference and name is the logical root the template uses (e.g. invoice). Pass the whole variable, not a sub-path.
Injected from bindings (not shown as a per-run input):
  • ui_template — the frozen HTML template (empty on first run).
  • ui_description, title, display_name — configuration metadata.

Output

Structured content:
  • render_mode is custom_ui when a template is available, json when generation failed (the frontend then falls back to a structured data view).
  • Citable values ({value, word_id_groups}) render as clickable citation spans linking back to the source document; double-clicking a field allows inline correction (only the corrected data is persisted — the template and render mode are server-owned and cannot be changed through an edit).

Template lifecycle

  1. First run: no frozen template → the tool builds a compact shape skeleton of the actual data and calls the LLM (Pro model, falling back to Default) to generate a Handlebars template, then freezes it into configured_tools.bindings.ui_template. The freeze is fill-empty-only (never overwrites an existing template, e.g. a user’s saved edit) and is mirrored onto the tool’s draft and active-version counterpart rows so the version diff stays clean.
  2. Subsequent runs: the frozen template is injected via bindings. Because extraction field names can vary run to run, a fast field-mapping LLM call aliases this run’s data keys to the template’s paths (add-only, fail-open).
  3. Editing: the config editor can regenerate, hand-edit, or refine the template with feedback (POST /api/configured-tools/:id/custom-interface/refine) and save it (PUT /api/configured-tools/:id/custom-interface/template — an explicit save always overwrites).
Supported template syntax: {{var}} (dot paths), {{#each}}, {{#if}}, {{#unless}}, {{#with}}, all with {{else}}. @index, @root, ../ parent paths, and array indexing are not supported and are stripped at render time. Rendered HTML is sanitized (DOMPurify) before injection — scripts, event handlers, and javascript: URLs never execute.

Limits

  • Template generation happens at most once per configured tool (first run wins; failures fall back to the JSON view and retry on the next run).
  • The generation prompt describes data shape within a bounded line budget; extremely deep/wide payloads have deep fields omitted from the skeleton (logged as a warning).
  • Rendering is client-side; very large data payloads are subject to the platform’s tool-output caps.

Side effects

  • First successful generation writes ui_template into the tool’s configured_tools.bindings (and its draft/active-version counterparts). No external systems are touched.

Expected errors

  • variables is required — pass at least one {name, ref} — no input provided.
  • Template generation failure (LLM error/empty output) — logged warning; the step still completes with render_mode: "json".
  • Freeze/persist failures are best-effort — logged warning, never fail the step; the template is regenerated on the next run.