Files
sim/apps/docs/components/workflow-preview/examples.ts
T
Waleed 7545391cb3 feat(docs): render workflow previews with the shared editor renderer (#5277)
* chore(workflow-renderer): declare @sim/emcn dep + wire the package into docs

Adds the missing @sim/emcn peer/dev dependency to @sim/workflow-renderer (it imports @sim/emcn in every View but resolved only via workspace hoisting). Wires apps/docs to consume @sim/workflow-renderer (dependency, transpilePackages, Tailwind @source) and adds remark-breaks (pulled transitively via the barrel's NoteBlockView export) — mirroring the @sim/emcn integration. Foundation for migrating the docs workflow-preview fork onto the shared Views. Build resolves the package/@source/remark-breaks cleanly.

* feat(docs): render loop/parallel containers with the shared SubflowNodeView

Replaces the forked PreviewContainerNode with a thin DocsContainerNode that maps the static preview data to SubflowNodeView's read-only (isPreview) props — no stores or hooks. Adds the block size to the preview node data so the view can size itself, and corrects the parallel example's start-edge handle id to 'parallel-start-source' (the view derives the handle id from kind). Deletes preview-container-node.tsx. Container colors/icons are now owned by the shared view (loop=blue, parallel=yellow).

* feat(docs): render block nodes with the shared WorkflowBlockView

Replaces the forked PreviewBlockNode with a thin DocsBlockNode that maps the static preview data to WorkflowBlockView's props — store-free, builds the subblock rows (condition/router Context+routes/default + tools + error) via SubBlockRowView, strips branch-id prefixes so the view's regenerated handle ids match, remaps router->router_v2, and keeps the framer-motion dim/stagger wrapper. Promotes resolveIcon into block-icons.tsx, adds the --workflow-edge token to docs global.css, deletes preview-block-node.tsx. The canvas diagrams now render with the real editor's view.

* refactor(workflow-renderer): make editor-only WorkflowBlockView props optional

The child-deploy, schedule, and webhook badge props (and their callbacks) only matter in the editor. Mark them optional and optional-chain the three callbacks so read-only consumers (docs, academy) can omit the whole group instead of passing ~18 explicit off-values. The editor still passes them, so its behavior is byte-identical (verified: apps/sim type-check clean). DocsBlockNode drops the off-props.

* feat(docs): replace how-it-runs static diagrams with live WorkflowPreview

Swaps the four static PNGs on the how-it-runs page for live, app-styled WorkflowPreview diagrams (concurrency, combination, condition+router branching, error path). Adds the four example workflows and renders error-port edges red to match the editor. The English page only; the translated execution/basics pages keep the PNGs.

* refactor(workflow-renderer): the view owns condition/router/error rows

Both the editor container and the docs adapter hand-built the condition/router/error summary rows in an order that had to stay in lockstep with the view's absolute handle-offset math — a three-way coupling with nothing enforcing it. The view now renders those rows itself from the conditionRows/routerRows it already receives (plus a routerContextValue prop for the router's Context row), so row order and handle geometry live together in one place. Both containers pass only data and their non-branch rows.

Editor is byte-identical: getDisplayValue moves to where conditionRows/routerRows are built; the no-subBlock SubBlockRow path is already an exact SubBlockRowView(title, value) passthrough; the error row stays gated on shouldShowDefaultHandles. Verified apps/sim type-check clean. Docs now also renders the error row on condition/router blocks, which the real editor already did (shouldShowDefaultHandles is true for them) — an alignment fix.

* refactor(docs): drop the parallel --wp-* token layer for the app/emcn tokens

The workflow-preview ran a 25-token --wp-* mirror (22 were pure aliases of app tokens docs already defines) plus a .wp-scope wrapper class. Replaces every var(--wp-X) with its canonical app/emcn token (--wp-edge->--workflow-edge, --wp-highlight->--brand-secondary, badges->--badge-*, etc.), adds the one missing token (--divider), and deletes the .wp-scope blocks + class. Visually identical (aliases resolve to the same values); the preview now inherits the same design tokens as the shared views and the rest of the app instead of a hand-rolled parallel set.

* refactor(docs): adopt emcn Badge + dedup resolveIcon in workflow-preview

output-bundle's hand-rolled type badge (BADGE_COLORS + a styled span) becomes the emcn Badge (its green/blue/orange/purple/gray variants use the identical --badge-* tokens). resolveIcon, which had three copies, is now imported once from block-icons by output-bundle and block-inspector.

* refactor(docs): rebuild the preview inspector on emcn chip primitives

The lightbox inspector was a hand-rolled facsimile (raw divs + a CONTROL class string + inline dashed borders). It now composes from the same @sim/emcn primitives the live editor's sub-block controls wrap — ChipSelect/ChipInput/ChipTextarea(viewOnly)/ChipSwitch/ChipTag/FieldDivider/Label — so it reads as the real editor panel, fed example data (read-only, full opacity via readOnly/viewOnly, not greyed). Slider stays minimal (no emcn equivalent) but on app tokens. Props API and embedded/standalone modes unchanged.

* refactor(docs): render the block-reference hero through the shared View

Retires the hand-rolled BlockCard (a parallel reimplementation of WorkflowBlockView) and the BlockDisplaySpec data model. Each block hero is now a single-block PreviewWorkflow (block-display-workflows.ts) rendered through the same toReactFlowElements -> DocsBlockNode -> WorkflowBlockView pipeline as the diagrams, mounted in a minimal fitView ReactFlow (maxZoom 1.3, no canvas chrome). A single block can no longer drift from the canvas.

* fix(docs): define sim's type scale + align the preview inspector to the editor

Docs Tailwind v4 never defined sim's custom font sizes (text-small/caption/md/micro), so emcn components (Label, Badge, the shared views) fell back to inherited sizes — the inspector labels rendered huge. Adds the type scale to the docs @theme. Also aligns the inspector header to the real editor panel (surface-4 bar, size-[18px] rounded-sm icon, text-sm name) and removes the Connections section (and its now-dead prop/wiring).

* fix(docs): inspector shows the full field list + dragged positions persist

Inspector: shows the block type's full field list (from the reference data) with the example's values overlaid, so it reads like the editor panel instead of only the canvas summary rows. Drag: selecting another block no longer relayouts the canvas — node positions the viewer dragged are preserved across highlight/selection changes (only a different workflow relayouts).

* feat(docs): highlight <> references + env vars; hide Ask AI over the lightbox; respace blocks

Inspector text fields render the value with <...> block references and {{...}} environment variables highlighted in brand-secondary (a lean read-only port of the editor's formatDisplayText), in the canonical chip field chrome. The floating Ask AI widget is hidden while a preview lightbox is open. Plus the example-data respacing so the editor-faithful Error row no longer makes stacked blocks overlap.

* fix(docs): make per-type field templates match the real block registry

Audited every block type's field list (the source the inspector + block-reference heroes render) against apps/sim/blocks/blocks/*. Corrected drift to the registry's default-visible fields, titles, and order: agent gains Temperature; router gains Model; wait gains Async; schedule rewritten (default is Daily, not minutes); webhook_trigger expanded to its real default-visible set; human_in_the_loop notification title fixed. Provider-credential and advanced-mode fields stay hidden, matching the editor. Canvas diagrams keep their clean curated rows; the inspector now shows the full, real field list per the chosen clean-canvas/full-inspector split.

* improvement(docs): taller default preview height so respaced diagrams aren't shrunk

Bumps the default WorkflowPreview height 260->300 (the respaced, editor-faithful blocks are taller, so fitView was shrinking diagrams that relied on the default). The tall how-it-runs routing diagram gets 400.

* improvement(docs): zoomable inline preview + taller default + themed controls

The inline preview is now zoomable outside the lightbox: adds react-flow zoom/fit Controls (themed to the dark canvas chrome) and enables pinch-zoom, while keeping scroll-zoom off so the page still scrolls over the diagram. Pan-drag and click-block-to-inspect already worked. Default height 300->340.

* improvement(docs): click canvas to expand; click empty lightbox to deselect

Clicking the inline preview canvas opens the full lightbox; clicking empty space in the lightbox clears the selection, matching the real editor.

* improvement(docs): reveal inline zoom controls on hover only

The always-visible zoom controls felt heavy on the inline preview; they now fade in on hover (matching the expand button) and stay visible in the lightbox.

* improvement(docs): drop zoom controls on the inline preview

Inline preview keeps pinch-zoom, pan, drag, and click-to-expand; zoom buttons stay in the lightbox only.

* improvement(docs): remove zoom controls from the lightbox too

Both previews zoom via scroll/pinch and pan via drag; no on-canvas zoom buttons. Drops the Controls import and its theming CSS.

* improvement(docs): match the real canvas — flat background + editor edge geometry

Closes the last faithfulness gaps the audit found: removes the dot grid (the real editor hides its background — flat bg), aligns PreviewEdge to the editor's smoothstep math (borderRadius 8, offset 30) and 2px stroke (default + error edges), the selection ring to 1.75px, and minZoom to 0.1. Structural parity (blocks/handles/containers/colors/tokens) was already shared. Kept PreviewEdge rather than swapping to WorkflowEdgeView, which would clobber the docs-only highlight/dim/animate for no visual gain.

* improvement(docs): rebrand the docs assistant as 'Ask Sim', styled like the real chat input

Renames the floating assistant from 'Ask AI' to 'Ask Sim' (matching the platform's voice — you talk to Sim) and restyles the composer to mirror the home chat input: a rounded-2xl bordered field with the toolbar inside, and the same 28px circular send/stop button (the home's exact active/disabled colors + white/black arrow). Updates the lightbox hide-selector to the new label.

* improvement(docs): match Ask Sim message styling to the mothership chat

Aligns the user bubble (rounded-[16px] surface-5, text-base/primary, leading-23, max-w-85%) and the assistant markdown (text-base, 600 headings/strong, text-primary dashed-underline links, surface-5 code blocks) to the real mothership chat's user-message + chat-content treatment, instead of the prior generic text-sm rendering. The composer already mirrors the home user-input (rounded-2xl field + 28px circular send button).

* improvement(docs): compact single-row Ask Sim composer

The two-row layout left a tall dead gap (the docs widget has no toolbar buttons to fill the second row). The composer is now a single row — textarea with the circular send button inline — so it sits at the natural input height.

* fix(docs): pass the router Context value to the shared view

DocsBlockNode never set routerContextValue, so the view (which renders the router's Context row from that prop, not from rows) showed a blank Context even when the preview data authored a value like <start.input>. Extract it from the block's Context row and pass it through.

* fix(docs): don't apply a block-type field template that doesn't match the block

inspectorFieldsFor keyed the full field template purely off block.type, but some types are reused across roles (a table action block vs the table trigger, a webhook trigger vs the webhook action), so the wrong template was applied. Only use the template when the block's authored rows are actually a subset of it; otherwise fall back to the block's own rows.

* fix(docs): connect preview edges to subflow container handles

toReactFlowElements hardcoded targetHandle to 'target' and defaulted source handles to 'source', but Loop/Parallel containers (SubflowNodeView) expose a 'loop-end-source'/'parallel-end-source' output handle and a left input handle with no id. Edges into and out of containers therefore failed to connect. Resolve each edge end to the block's real handle based on whether it's a container.

* fix(docs): don't expand the inspector template for blocks with no rows

block.rows.every(...) is vacuously true for an empty rows array, so a block defined only by branches (e.g. a router in ROUTING_WORKFLOW) inherited the type template's invented field defaults. Require non-empty authored rows before applying the template.

* fix(docs): render blank branch/router-context values as '-' like the editor

The editor maps condition/router branch values and the router Context through getDisplayValue, which renders '-' for a blank value. DocsBlockNode mapped them to an empty string, so else branches and unset routes looked blank instead of matching the editor. Mirror getDisplayValue's empty-value handling.

* fix(docs): show '-' for blank inspector branch values, matching the canvas

inspectorFieldsFor passed raw branch.value into the lightbox branch fields, so an unset else route read blank in the inspector while DocsBlockNode (and the editor's getDisplayValue) render '-' on the canvas. Normalize the same way; drop the now-redundant placeholder.
2026-06-29 20:27:33 -07:00

1969 lines
52 KiB
TypeScript

import type { PreviewWorkflow } from '@/components/workflow-preview/workflow-data'
/**
* The running example used across the Workflows overview: a workflow that takes
* an incoming customer message, classifies its category and urgency, and returns
* the result. Colors match the real Start / Agent / Response blocks.
*/
export const CLASSIFY_WORKFLOW: PreviewWorkflow = {
id: 'classify-message',
name: 'Classify customer message',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Customer message' }],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 340, y: 0 },
rows: [
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Messages', value: 'Classify <start.input>' },
],
},
],
edges: [{ id: 'start-agent', source: 'start', target: 'agent' }],
}
/**
* A three-block chain used on the Data flow page: the message is classified, then
* a reply is drafted from that classification. Shows values moving forward along
* the chain (Reply reads Classify's output; Classify reads the Start input).
*/
export const CLASSIFY_REPLY_WORKFLOW: PreviewWorkflow = {
id: 'classify-reply',
name: 'Classify and reply',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Customer message' }],
},
{
id: 'classify',
name: 'Classify',
type: 'agent',
bgColor: '#33C482',
position: { x: 330, y: 0 },
rows: [
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Messages', value: 'Classify <start.input>' },
],
},
{
id: 'reply',
name: 'Reply',
type: 'agent',
bgColor: '#33C482',
position: { x: 660, y: 0 },
rows: [
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Messages', value: 'Draft a reply for <classify.content>' },
],
},
],
edges: [
{ id: 'start-classify', source: 'start', target: 'classify' },
{ id: 'classify-reply', source: 'classify', target: 'reply' },
],
}
/**
* A support workflow used on the "Using a knowledge base" page: a question is
* searched against a knowledge base, and an Agent answers from the matches.
*/
export const SUPPORT_KB_WORKFLOW: PreviewWorkflow = {
id: 'support-kb',
name: 'Answer from docs',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Customer question' }],
},
{
id: 'knowledge',
name: 'Knowledge',
type: 'knowledge',
bgColor: '#00B0B0',
position: { x: 330, y: 0 },
rows: [
{ title: 'Operation', value: 'Search' },
{ title: 'Query', value: '<start.input>' },
],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 660, y: 0 },
rows: [
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Messages', value: 'Answer from <knowledge.results>' },
],
},
],
edges: [
{ id: 'start-knowledge', source: 'start', target: 'knowledge' },
{ id: 'knowledge-agent', source: 'knowledge', target: 'agent' },
],
}
/**
* The lead-enrichment chain from the "Using tables in workflows" page: query
* unprocessed rows, classify each with an Agent, write the result back. The
* first Table block is named "Table 1" to match the `<table1.rows>` references
* in the prose.
*/
export const TABLE_ENRICH_WORKFLOW: PreviewWorkflow = {
id: 'table-enrich',
name: 'Enrich leads',
blocks: [
{
id: 'table1',
name: 'Table 1',
type: 'table',
bgColor: '#10B981',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [
{ title: 'Operation', value: 'Query Rows' },
{ title: 'Filter', value: 'status = unprocessed' },
],
},
{
id: 'classify',
name: 'Classify',
type: 'agent',
bgColor: '#33C482',
position: { x: 330, y: 0 },
rows: [
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Messages', value: 'Classify each lead' },
],
},
{
id: 'table2',
name: 'Table 2',
type: 'table',
bgColor: '#10B981',
position: { x: 660, y: 0 },
rows: [
{ title: 'Operation', value: 'Update Rows' },
{ title: 'Set', value: 'status = qualified' },
],
},
],
edges: [
{ id: 'table1-classify', source: 'table1', target: 'classify' },
{ id: 'classify-table2', source: 'classify', target: 'table2' },
],
}
/**
* The example on the API block page: fetch data over HTTP, then summarize the
* response with an Agent.
*/
export const API_FETCH_WORKFLOW: PreviewWorkflow = {
id: 'api-fetch',
name: 'Fetch and summarize',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'City' }],
},
{
id: 'api',
name: 'API',
type: 'api',
bgColor: '#2F55FF',
position: { x: 330, y: 0 },
rows: [
{ title: 'Method', value: 'GET' },
{ title: 'URL', value: 'api.weather.com/<start.input>' },
],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 660, y: 0 },
rows: [
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Messages', value: 'Summarize <api.data>' },
],
},
],
edges: [
{ id: 'start-api', source: 'start', target: 'api' },
{ id: 'api-agent', source: 'api', target: 'agent' },
],
}
/**
* The example on the Condition block page: route a ticket to a different path
* based on its priority.
*/
export const CONDITION_ROUTE_WORKFLOW: PreviewWorkflow = {
id: 'condition-route',
name: 'Route by priority',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 60 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Ticket' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 330, y: 60 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: "<start.priority> === 'high'" },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'escalate',
name: 'Escalate',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 0 },
rows: [{ title: 'Messages', value: 'Escalate this ticket' }],
},
{
id: 'reply',
name: 'Reply',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 140 },
rows: [{ title: 'Messages', value: 'Draft a standard reply' }],
},
],
edges: [
{ id: 'start-condition', source: 'start', target: 'condition' },
{
id: 'condition-escalate',
source: 'condition',
target: 'escalate',
sourceHandle: 'condition-if',
},
{ id: 'condition-reply', source: 'condition', target: 'reply', sourceHandle: 'condition-else' },
],
}
/** Condition example: gate publishing on a moderation score. */
export const CONDITION_MODERATE_WORKFLOW: PreviewWorkflow = {
id: 'condition-moderate',
name: 'Moderate content',
blocks: [
{
id: 'moderate',
name: 'Moderate',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 60 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Score toxicity of <start.input>' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 330, y: 60 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<moderate.toxicity> > 0.7' },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'block',
name: 'Block',
type: 'function',
bgColor: '#FF402F',
position: { x: 700, y: 0 },
rows: [{ title: 'Code', value: "throw 'blocked'" }],
},
{
id: 'publish',
name: 'Publish',
type: 'api',
bgColor: '#2F55FF',
position: { x: 700, y: 140 },
rows: [{ title: 'Method', value: 'POST' }],
},
],
edges: [
{ id: 'moderate-condition', source: 'moderate', target: 'condition' },
{ id: 'condition-block', source: 'condition', target: 'block', sourceHandle: 'condition-if' },
{
id: 'condition-publish',
source: 'condition',
target: 'publish',
sourceHandle: 'condition-else',
},
],
}
/** Condition example: branch onboarding on the account tier. */
export const CONDITION_ONBOARD_WORKFLOW: PreviewWorkflow = {
id: 'condition-onboard',
name: 'Branch onboarding',
blocks: [
{
id: 'plan',
name: 'Plan',
type: 'function',
bgColor: '#FF402F',
position: { x: 0, y: 60 },
hideTargetHandle: true,
rows: [{ title: 'Code', value: 'return <start.account>' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 330, y: 60 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: "<plan.result.tier> === 'enterprise'" },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'guided',
name: 'Guided setup',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 0 },
rows: [{ title: 'Messages', value: 'Walk through SSO and SCIM' }],
},
{
id: 'quickstart',
name: 'Quick start',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 140 },
rows: [{ title: 'Messages', value: 'Send the 2-minute setup' }],
},
],
edges: [
{ id: 'plan-condition', source: 'plan', target: 'condition' },
{ id: 'condition-guided', source: 'condition', target: 'guided', sourceHandle: 'condition-if' },
{
id: 'condition-quickstart',
source: 'condition',
target: 'quickstart',
sourceHandle: 'condition-else',
},
],
}
/** Function example: reshape an API response into the field a later block needs. */
export const FUNCTION_RESHAPE_WORKFLOW: PreviewWorkflow = {
id: 'function-reshape',
name: 'Reshape a response',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'User ID' }],
},
{
id: 'api',
name: 'API',
type: 'api',
bgColor: '#2F55FF',
position: { x: 320, y: 0 },
rows: [
{ title: 'Method', value: 'GET' },
{ title: 'URL', value: 'api.example.com/users/<start.input>' },
],
},
{
id: 'extract',
name: 'Extract',
type: 'function',
bgColor: '#FF402F',
position: { x: 640, y: 0 },
rows: [
{ title: 'Language', value: 'JavaScript' },
{ title: 'Code', value: 'return <api.data>.profile' },
],
},
],
edges: [
{ id: 'start-api', source: 'start', target: 'api' },
{ id: 'api-extract', source: 'api', target: 'extract' },
],
}
/** Function example: validate and clean input before writing it. */
export const FUNCTION_VALIDATE_WORKFLOW: PreviewWorkflow = {
id: 'function-validate',
name: 'Validate before write',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Form' }],
},
{
id: 'clean',
name: 'Clean',
type: 'function',
bgColor: '#FF402F',
position: { x: 320, y: 0 },
rows: [
{ title: 'Language', value: 'JavaScript' },
{ title: 'Code', value: 'return sanitize(<start.input>)' },
],
},
{
id: 'save',
name: 'Save',
type: 'api',
bgColor: '#2F55FF',
position: { x: 640, y: 0 },
rows: [
{ title: 'Method', value: 'POST' },
{ title: 'Body', value: '<clean.result>' },
],
},
],
edges: [
{ id: 'start-clean', source: 'start', target: 'clean' },
{ id: 'clean-save', source: 'clean', target: 'save' },
],
}
/** Router example: a model triages a ticket to the right team. */
export const ROUTER_TRIAGE_WORKFLOW: PreviewWorkflow = {
id: 'router-triage',
name: 'Triage a ticket',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 95 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Ticket' }],
},
{
id: 'router',
name: 'Router',
type: 'router',
bgColor: '#28C43F',
position: { x: 320, y: 95 },
rows: [
{ title: 'Context', value: '<start.input>' },
{ title: 'Model', value: 'claude-sonnet-4-6' },
],
branches: [
{ id: 'router-sales', label: 'Sales' },
{ id: 'router-support', label: 'Support' },
{ id: 'router-billing', label: 'Billing' },
],
},
{
id: 'sales',
name: 'Sales',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 0 },
rows: [{ title: 'Messages', value: 'Answer the pricing question' }],
},
{
id: 'support',
name: 'Support',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 140 },
rows: [{ title: 'Messages', value: 'Help with the issue' }],
},
{
id: 'billing',
name: 'Billing',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 280 },
rows: [{ title: 'Messages', value: 'Resolve the billing question' }],
},
],
edges: [
{ id: 'start-router', source: 'start', target: 'router' },
{ id: 'router-sales', source: 'router', target: 'sales', sourceHandle: 'router-sales' },
{ id: 'router-support', source: 'router', target: 'support', sourceHandle: 'router-support' },
{ id: 'router-billing', source: 'router', target: 'billing', sourceHandle: 'router-billing' },
],
}
/** Response example: return a structured answer from an API-triggered workflow. */
export const RESPONSE_API_WORKFLOW: PreviewWorkflow = {
id: 'response-api',
name: 'Answer over the API',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Question' }],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 320, y: 0 },
rows: [{ title: 'Messages', value: 'Answer <start.input>' }],
},
{
id: 'response',
name: 'Response',
type: 'response',
bgColor: '#2F55FF',
position: { x: 640, y: 0 },
rows: [
{ title: 'Data', value: '{ "answer": <agent.content> }' },
{ title: 'Status', value: '200' },
],
},
],
edges: [
{ id: 'start-agent', source: 'start', target: 'agent' },
{ id: 'agent-response', source: 'agent', target: 'response' },
],
}
/** Router example: classify incoming feedback into the right child workflow. */
export const ROUTER_CLASSIFY_WORKFLOW: PreviewWorkflow = {
id: 'router-classify',
name: 'Classify feedback',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 50 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Feedback' }],
},
{
id: 'router',
name: 'Router',
type: 'router',
bgColor: '#28C43F',
position: { x: 320, y: 50 },
rows: [{ title: 'Context', value: '<start.input>' }],
branches: [
{ id: 'router-product', label: 'Product' },
{ id: 'router-bug', label: 'Bug report' },
],
},
{
id: 'product',
name: 'Product',
type: 'workflow',
bgColor: '#6366F1',
position: { x: 700, y: 0 },
rows: [{ title: 'Workflow', value: 'product-intake' }],
},
{
id: 'bug',
name: 'Bug report',
type: 'workflow',
bgColor: '#6366F1',
position: { x: 700, y: 140 },
rows: [{ title: 'Workflow', value: 'bug-triage' }],
},
],
edges: [
{ id: 'start-router', source: 'start', target: 'router' },
{ id: 'router-product', source: 'router', target: 'product', sourceHandle: 'router-product' },
{ id: 'router-bug', source: 'router', target: 'bug', sourceHandle: 'router-bug' },
],
}
/** Router example: qualify a lead into sales or self-serve. */
export const ROUTER_LEAD_WORKFLOW: PreviewWorkflow = {
id: 'router-lead',
name: 'Qualify a lead',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 50 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Lead' }],
},
{
id: 'router',
name: 'Router',
type: 'router',
bgColor: '#28C43F',
position: { x: 320, y: 50 },
rows: [{ title: 'Context', value: '<start.input>' }],
branches: [
{ id: 'router-enterprise', label: 'Enterprise' },
{ id: 'router-selfserve', label: 'Self-serve' },
],
},
{
id: 'enterprise',
name: 'Enterprise',
type: 'agent',
bgColor: '#33C482',
position: { x: 700, y: 0 },
rows: [{ title: 'Messages', value: 'Book a sales call' }],
},
{
id: 'selfserve',
name: 'Self-serve',
type: 'workflow',
bgColor: '#6366F1',
position: { x: 700, y: 140 },
rows: [{ title: 'Workflow', value: 'onboarding' }],
},
],
edges: [
{ id: 'start-router', source: 'start', target: 'router' },
{
id: 'router-enterprise',
source: 'router',
target: 'enterprise',
sourceHandle: 'router-enterprise',
},
{
id: 'router-selfserve',
source: 'router',
target: 'selfserve',
sourceHandle: 'router-selfserve',
},
],
}
/** Response example: acknowledge a webhook after processing it. */
export const RESPONSE_WEBHOOK_WORKFLOW: PreviewWorkflow = {
id: 'response-webhook',
name: 'Acknowledge a webhook',
blocks: [
{
id: 'webhook',
name: 'Webhook',
type: 'webhook',
bgColor: '#10B981',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Format', value: 'JSON' }],
},
{
id: 'process',
name: 'Process',
type: 'function',
bgColor: '#FF402F',
position: { x: 320, y: 0 },
rows: [{ title: 'Code', value: 'save(<webhook.body>)' }],
},
{
id: 'ack',
name: 'Response',
type: 'response',
bgColor: '#2F55FF',
position: { x: 640, y: 0 },
rows: [
{ title: 'Data', value: '{ "received": true }' },
{ title: 'Status', value: '200' },
],
},
],
edges: [
{ id: 'webhook-process', source: 'webhook', target: 'process' },
{ id: 'process-ack', source: 'process', target: 'ack' },
],
}
/** Response example: return a different status on each branch. */
export const RESPONSE_ERROR_WORKFLOW: PreviewWorkflow = {
id: 'response-error',
name: 'Status per branch',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 60 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Request' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 320, y: 60 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<start.valid>' },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'ok',
name: 'Response',
type: 'response',
bgColor: '#2F55FF',
position: { x: 700, y: 0 },
rows: [{ title: 'Status', value: '200' }],
},
{
id: 'bad',
name: 'Response',
type: 'response',
bgColor: '#2F55FF',
position: { x: 700, y: 140 },
rows: [{ title: 'Status', value: '400' }],
},
],
edges: [
{ id: 'start-condition', source: 'start', target: 'condition' },
{ id: 'condition-ok', source: 'condition', target: 'ok', sourceHandle: 'condition-if' },
{ id: 'condition-bad', source: 'condition', target: 'bad', sourceHandle: 'condition-else' },
],
}
/** Variables example: count retries across attempts. */
export const VARIABLES_RETRY_WORKFLOW: PreviewWorkflow = {
id: 'variables-retry',
name: 'Count retries',
blocks: [
{
id: 'api',
name: 'API',
type: 'api',
bgColor: '#2F55FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Method', value: 'GET' }],
},
{
id: 'vars',
name: 'Variables',
type: 'variables',
bgColor: '#8B5CF6',
position: { x: 320, y: 0 },
rows: [{ title: 'Set', value: 'retryCount + 1' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 640, y: 0 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<variable.retryCount> < 3' },
{ id: 'condition-else', label: 'else' },
],
},
],
edges: [
{ id: 'api-vars', source: 'api', target: 'vars' },
{ id: 'vars-condition', source: 'vars', target: 'condition' },
],
}
/** Variables example: hold fetched config for the rest of the run. */
export const VARIABLES_CONFIG_WORKFLOW: PreviewWorkflow = {
id: 'variables-config',
name: 'Hold config',
blocks: [
{
id: 'api',
name: 'API',
type: 'api',
bgColor: '#2F55FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'URL', value: 'api.example.com/profile' }],
},
{
id: 'vars',
name: 'Variables',
type: 'variables',
bgColor: '#8B5CF6',
position: { x: 320, y: 0 },
rows: [{ title: 'Set', value: 'userId, userTier' }],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 640, y: 0 },
rows: [{ title: 'Messages', value: 'Personalize for <variable.userTier>' }],
},
],
edges: [
{ id: 'api-vars', source: 'api', target: 'vars' },
{ id: 'vars-agent', source: 'vars', target: 'agent' },
],
}
/** Wait example: space out two API calls to respect a rate limit. */
export const WAIT_RATELIMIT_WORKFLOW: PreviewWorkflow = {
id: 'wait-ratelimit',
name: 'Space out calls',
blocks: [
{
id: 'first',
name: 'API',
type: 'api',
bgColor: '#2F55FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Method', value: 'GET' }],
},
{
id: 'wait',
name: 'Wait',
type: 'wait',
bgColor: '#F59E0B',
position: { x: 320, y: 0 },
rows: [
{ title: 'Amount', value: '2' },
{ title: 'Unit', value: 'Seconds' },
],
},
{
id: 'second',
name: 'API',
type: 'api',
bgColor: '#2F55FF',
position: { x: 640, y: 0 },
rows: [{ title: 'Method', value: 'GET' }],
},
],
edges: [
{ id: 'first-wait', source: 'first', target: 'wait' },
{ id: 'wait-second', source: 'wait', target: 'second' },
],
}
/** Wait example: send a follow-up after a delay. */
export const WAIT_FOLLOWUP_WORKFLOW: PreviewWorkflow = {
id: 'wait-followup',
name: 'Delayed follow-up',
blocks: [
{
id: 'send',
name: 'Send',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Send the welcome email' }],
},
{
id: 'wait',
name: 'Wait',
type: 'wait',
bgColor: '#F59E0B',
position: { x: 320, y: 0 },
rows: [
{ title: 'Amount', value: '2' },
{ title: 'Unit', value: 'Days' },
],
},
{
id: 'followup',
name: 'Follow up',
type: 'agent',
bgColor: '#33C482',
position: { x: 640, y: 0 },
rows: [{ title: 'Messages', value: 'Send a check-in' }],
},
],
edges: [
{ id: 'send-wait', source: 'send', target: 'wait' },
{ id: 'wait-followup', source: 'wait', target: 'followup' },
],
}
/** Evaluator example: gate a draft on a quality score before it ships. */
export const EVALUATOR_GATE_WORKFLOW: PreviewWorkflow = {
id: 'evaluator-gate',
name: 'Gate on a score',
blocks: [
{
id: 'draft',
name: 'Draft',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Write the announcement' }],
},
{
id: 'evaluator',
name: 'Evaluator',
type: 'evaluator',
bgColor: '#4D5FFF',
position: { x: 320, y: 0 },
rows: [
{ title: 'Metrics', value: 'Accuracy, Clarity' },
{ title: 'Model', value: 'claude-sonnet-4-6' },
],
},
{
id: 'gate',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 640, y: 0 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<evaluator.accuracy> >= 4' },
{ id: 'condition-else', label: 'else' },
],
},
],
edges: [
{ id: 'draft-evaluator', source: 'draft', target: 'evaluator' },
{ id: 'evaluator-gate', source: 'evaluator', target: 'gate' },
],
}
/** Credential example: select one Google account and reuse it across blocks. */
export const CREDENTIAL_SHARE_WORKFLOW: PreviewWorkflow = {
id: 'credential-share',
name: 'Share a credential',
blocks: [
{
id: 'credential',
name: 'Credential',
type: 'credential',
bgColor: '#6366F1',
position: { x: 0, y: 100 },
hideTargetHandle: true,
rows: [
{ title: 'Operation', value: 'Select Credential' },
{ title: 'Account', value: 'Google' },
],
},
{
id: 'gmail',
name: 'Gmail',
type: 'gmail',
bgColor: '#FFFFFF',
position: { x: 380, y: 0 },
rows: [{ title: 'Account', value: '<credential.credentialId>' }],
},
{
id: 'drive',
name: 'Drive',
type: 'google_drive',
bgColor: '#FFFFFF',
position: { x: 380, y: 140 },
rows: [{ title: 'Account', value: '<credential.credentialId>' }],
},
{
id: 'calendar',
name: 'Calendar',
type: 'google_calendar',
bgColor: '#FFFFFF',
position: { x: 380, y: 280 },
rows: [{ title: 'Account', value: '<credential.credentialId>' }],
},
],
edges: [
{ id: 'credential-gmail', source: 'credential', target: 'gmail' },
{ id: 'credential-drive', source: 'credential', target: 'drive' },
{ id: 'credential-calendar', source: 'credential', target: 'calendar' },
],
}
/** Credential example: pick a production or staging account at runtime. */
export const CREDENTIAL_ROUTE_WORKFLOW: PreviewWorkflow = {
id: 'credential-route',
name: 'Route to an account',
blocks: [
{
id: 'pick',
name: 'Pick account',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 60 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Production or staging?' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 330, y: 60 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: "<variable.env> === 'prod'" },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'prod',
name: 'Credential',
type: 'credential',
bgColor: '#6366F1',
position: { x: 700, y: 0 },
rows: [{ title: 'Account', value: 'Prod Slack' }],
},
{
id: 'staging',
name: 'Credential',
type: 'credential',
bgColor: '#6366F1',
position: { x: 700, y: 140 },
rows: [{ title: 'Account', value: 'Staging Slack' }],
},
],
edges: [
{ id: 'pick-condition', source: 'pick', target: 'condition' },
{ id: 'condition-prod', source: 'condition', target: 'prod', sourceHandle: 'condition-if' },
{
id: 'condition-staging',
source: 'condition',
target: 'staging',
sourceHandle: 'condition-else',
},
],
}
const GUARDRAILS_START = {
id: 'start',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
} as const
const GUARDRAILS_GATE = {
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 640, y: 0 },
} as const
/** Guardrails example: validate JSON before parsing it. */
export const GUARDRAILS_JSON_WORKFLOW: PreviewWorkflow = {
id: 'guardrails-json',
name: 'Validate JSON',
blocks: [
{ ...GUARDRAILS_START, rows: [{ title: 'Messages', value: 'Return JSON' }] },
{
id: 'guardrails',
name: 'Guardrails',
type: 'guardrails',
bgColor: '#3D642D',
position: { x: 320, y: 0 },
rows: [{ title: 'Validation', value: 'Valid JSON' }],
},
{
...GUARDRAILS_GATE,
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<guardrails.passed>' },
{ id: 'condition-else', label: 'else' },
],
},
],
edges: [
{ id: 'start-guardrails', source: 'start', target: 'guardrails' },
{ id: 'guardrails-condition', source: 'guardrails', target: 'condition' },
],
}
/** Guardrails example: check an answer is grounded in the knowledge base. */
export const GUARDRAILS_HALLUCINATION_WORKFLOW: PreviewWorkflow = {
id: 'guardrails-hallucination',
name: 'Check grounding',
blocks: [
{ ...GUARDRAILS_START, rows: [{ title: 'Messages', value: 'Answer from the docs' }] },
{
id: 'guardrails',
name: 'Guardrails',
type: 'guardrails',
bgColor: '#3D642D',
position: { x: 320, y: 0 },
rows: [
{ title: 'Validation', value: 'Hallucination' },
{ title: 'Knowledge Base', value: 'docs' },
],
},
{
...GUARDRAILS_GATE,
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<guardrails.score> >= 3' },
{ id: 'condition-else', label: 'else' },
],
},
],
edges: [
{ id: 'start-guardrails', source: 'start', target: 'guardrails' },
{ id: 'guardrails-condition', source: 'guardrails', target: 'condition' },
],
}
/** Guardrails example: block user input that contains PII. */
export const GUARDRAILS_PII_WORKFLOW: PreviewWorkflow = {
id: 'guardrails-pii',
name: 'Block PII',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'User message' }],
},
{
id: 'guardrails',
name: 'Guardrails',
type: 'guardrails',
bgColor: '#3D642D',
position: { x: 320, y: 0 },
rows: [
{ title: 'Validation', value: 'PII Detection' },
{ title: 'Action', value: 'Block' },
],
},
{
...GUARDRAILS_GATE,
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: '<guardrails.passed>' },
{ id: 'condition-else', label: 'else' },
],
},
],
edges: [
{ id: 'start-guardrails', source: 'start', target: 'guardrails' },
{ id: 'guardrails-condition', source: 'guardrails', target: 'condition' },
],
}
/** HITL example: a human approves AI content before it ships. */
export const HITL_APPROVAL_WORKFLOW: PreviewWorkflow = {
id: 'hitl-approval',
name: 'Approve before publish',
blocks: [
{
id: 'draft',
name: 'Draft',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Write the post' }],
},
{
id: 'approve',
name: 'Human in the Loop',
type: 'human_in_the_loop',
bgColor: '#10B981',
position: { x: 300, y: 0 },
rows: [
{ title: 'Display', value: '<draft.content>' },
{ title: 'Resume', value: 'approved' },
],
},
{
id: 'publish',
name: 'Publish',
type: 'api',
bgColor: '#2F55FF',
position: { x: 640, y: 0 },
rows: [{ title: 'Method', value: 'POST' }],
},
],
edges: [
{ id: 'draft-approve', source: 'draft', target: 'approve' },
{ id: 'approve-publish', source: 'approve', target: 'publish' },
],
}
/** HITL example: chain two approvals for a high-stakes change. */
export const HITL_MULTISTAGE_WORKFLOW: PreviewWorkflow = {
id: 'hitl-multistage',
name: 'Two-stage approval',
blocks: [
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Draft the change' }],
},
{
id: 'manager',
name: 'Manager',
type: 'human_in_the_loop',
bgColor: '#10B981',
position: { x: 280, y: 0 },
rows: [{ title: 'Resume', value: 'approved' }],
},
{
id: 'director',
name: 'Director',
type: 'human_in_the_loop',
bgColor: '#10B981',
position: { x: 580, y: 0 },
rows: [{ title: 'Resume', value: 'approved' }],
},
{
id: 'execute',
name: 'Execute',
type: 'function',
bgColor: '#FF402F',
position: { x: 880, y: 0 },
rows: [{ title: 'Code', value: 'apply()' }],
},
],
edges: [
{ id: 'agent-manager', source: 'agent', target: 'manager' },
{ id: 'manager-director', source: 'manager', target: 'director' },
{ id: 'director-execute', source: 'director', target: 'execute' },
],
}
/** HITL example: a human verifies extracted data before processing. */
export const HITL_VALIDATE_WORKFLOW: PreviewWorkflow = {
id: 'hitl-validate',
name: 'Verify before processing',
blocks: [
{
id: 'extract',
name: 'Extract',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Extract the fields' }],
},
{
id: 'validate',
name: 'Human in the Loop',
type: 'human_in_the_loop',
bgColor: '#10B981',
position: { x: 300, y: 0 },
rows: [{ title: 'Display', value: '<extract.content>' }],
},
{
id: 'process',
name: 'Process',
type: 'function',
bgColor: '#FF402F',
position: { x: 640, y: 0 },
rows: [{ title: 'Code', value: 'process()' }],
},
],
edges: [
{ id: 'extract-validate', source: 'extract', target: 'validate' },
{ id: 'validate-process', source: 'validate', target: 'process' },
],
}
/** Webhook example: post a formatted result to an external endpoint. */
export const WEBHOOK_NOTIFY_WORKFLOW: PreviewWorkflow = {
id: 'webhook-notify',
name: 'Notify a service',
blocks: [
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Messages', value: 'Summarize the run' }],
},
{
id: 'format',
name: 'Format',
type: 'function',
bgColor: '#FF402F',
position: { x: 320, y: 0 },
rows: [{ title: 'Code', value: 'toSlackBlocks(<agent.content>)' }],
},
{
id: 'webhook',
name: 'Webhook',
type: 'webhook',
bgColor: '#10B981',
position: { x: 640, y: 0 },
rows: [{ title: 'URL', value: 'hooks.slack.com/…' }],
},
],
edges: [
{ id: 'agent-format', source: 'agent', target: 'format' },
{ id: 'format-webhook', source: 'format', target: 'webhook' },
],
}
/** Webhook example: fire an external process when a check passes. */
export const WEBHOOK_TRIGGER_WORKFLOW: PreviewWorkflow = {
id: 'webhook-trigger',
name: 'Trigger on a check',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Event' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 320, y: 0 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: "<start.status> === 'done'" },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'webhook',
name: 'Webhook',
type: 'webhook',
bgColor: '#10B981',
position: { x: 640, y: 0 },
rows: [{ title: 'URL', value: 'api.partner.com/hook' }],
},
],
edges: [
{ id: 'start-condition', source: 'start', target: 'condition' },
{ id: 'condition-webhook', source: 'condition', target: 'webhook' },
],
}
/** Workflow-block example: call a child workflow and use its result. */
export const WORKFLOW_CALL_WORKFLOW: PreviewWorkflow = {
id: 'workflow-call',
name: 'Call a child workflow',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Lead' }],
},
{
id: 'child',
name: 'Workflow',
type: 'workflow',
bgColor: '#6366F1',
position: { x: 320, y: 0 },
rows: [
{ title: 'Workflow', value: 'enrich-lead' },
{ title: 'Input', value: '<start.input>' },
],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 640, y: 0 },
rows: [{ title: 'Messages', value: 'Summarize <workflow.result>' }],
},
],
edges: [
{ id: 'start-child', source: 'start', target: 'child' },
{ id: 'child-agent', source: 'child', target: 'agent' },
],
}
/** Loop example: run a block once per item, then use the collected results. */
export const LOOP_WORKFLOW: PreviewWorkflow = {
id: 'loop-foreach',
name: 'Score each review',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 95 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Reviews' }],
},
{
id: 'loop',
name: 'Loop',
type: 'loop',
bgColor: '#FAFAF9',
position: { x: 340, y: 30 },
size: { width: 430, height: 170 },
rows: [],
},
{
id: 'score',
name: 'Score',
type: 'agent',
bgColor: '#33C482',
position: { x: 150, y: 62 },
parentId: 'loop',
rows: [{ title: 'Messages', value: 'Rate <loop.currentItem>' }],
},
{
id: 'summary',
name: 'Summary',
type: 'agent',
bgColor: '#33C482',
position: { x: 860, y: 95 },
rows: [{ title: 'Messages', value: 'Summarize <loop.results>' }],
},
],
edges: [
{ id: 'start-loop', source: 'start', target: 'loop' },
{ id: 'loop-score', source: 'loop', target: 'score', sourceHandle: 'loop-start-source' },
{ id: 'loop-summary', source: 'loop', target: 'summary' },
],
}
/** Parallel example: process every item concurrently, then aggregate the results. */
export const PARALLEL_WORKFLOW: PreviewWorkflow = {
id: 'parallel-collection',
name: 'Process tasks in parallel',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 95 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Tasks' }],
},
{
id: 'parallel',
name: 'Parallel',
type: 'parallel',
bgColor: '#1D1C1A',
position: { x: 340, y: 30 },
size: { width: 430, height: 170 },
rows: [],
},
{
id: 'call',
name: 'Call',
type: 'api',
bgColor: '#2F55FF',
position: { x: 150, y: 62 },
parentId: 'parallel',
rows: [{ title: 'URL', value: '/tasks/<parallel.currentItem>' }],
},
{
id: 'aggregate',
name: 'Aggregate',
type: 'function',
bgColor: '#FF402F',
position: { x: 860, y: 95 },
rows: [{ title: 'Code', value: 'merge(<parallel.results>)' }],
},
],
edges: [
{ id: 'start-parallel', source: 'start', target: 'parallel' },
{
id: 'parallel-call',
source: 'parallel',
target: 'call',
sourceHandle: 'parallel-start-source',
},
{ id: 'parallel-aggregate', source: 'parallel', target: 'aggregate' },
],
}
/** Building-agents overview: a minimal agent — the Agent block as the reasoning core. */
export const BUILD_AGENT_WORKFLOW: PreviewWorkflow = {
id: 'build-agent',
name: 'A minimal agent',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Lead' }],
},
{
id: 'agent',
name: 'Score lead',
type: 'agent',
bgColor: '#33C482',
position: { x: 320, y: 0 },
rows: [
{ title: 'Messages', value: 'Score <start.input>' },
{ title: 'Model', value: 'claude-sonnet-4-6' },
],
tools: [
{ type: 'knowledge', name: 'Knowledge', bgColor: '#00B0B0' },
{ type: 'hubspot', name: 'HubSpot', bgColor: '#FF7A59' },
],
},
{
id: 'response',
name: 'Response',
type: 'response',
bgColor: '#2F55FF',
position: { x: 680, y: 0 },
rows: [{ title: 'Data', value: '{ "score": <agent.score> }' }],
},
],
edges: [
{ id: 'start-agent', source: 'start', target: 'agent' },
{ id: 'agent-response', source: 'agent', target: 'response' },
],
}
/** Files guide: read a file, summarize it, save the summary as a new file. */
export const FILE_SUMMARY_WORKFLOW: PreviewWorkflow = {
id: 'file-summary',
name: 'Summarize a file',
blocks: [
{
id: 'file',
name: 'File',
type: 'file',
bgColor: '#40916C',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [
{ title: 'Operation', value: 'Read' },
{ title: 'File', value: 'report.pdf' },
],
},
{
id: 'agent',
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
position: { x: 320, y: 0 },
rows: [
{ title: 'Messages', value: 'Summarize this document' },
{ title: 'Files', value: '<file.files[0]>' },
],
},
{
id: 'write',
name: 'File',
type: 'file',
bgColor: '#40916C',
position: { x: 660, y: 0 },
rows: [
{ title: 'Operation', value: 'Write' },
{ title: 'File Name', value: 'summary.md' },
],
},
],
edges: [
{ id: 'file-agent', source: 'file', target: 'agent' },
{ id: 'agent-write', source: 'agent', target: 'write' },
],
}
/** Tables guide: query rows, classify them, write the results back. */
export const TABLE_ROUNDTRIP_WORKFLOW: PreviewWorkflow = {
id: 'table-roundtrip',
name: 'Classify and write back',
blocks: [
{
id: 'query',
name: 'Table',
type: 'table',
bgColor: '#10B981',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [
{ title: 'Operation', value: 'Query Rows' },
{ title: 'Filter', value: "status = 'unprocessed'" },
],
},
{
id: 'classify',
name: 'Classify',
type: 'agent',
bgColor: '#33C482',
position: { x: 340, y: 0 },
rows: [{ title: 'Messages', value: 'Classify <table.rows>' }],
},
{
id: 'update',
name: 'Table',
type: 'table',
bgColor: '#10B981',
position: { x: 680, y: 0 },
rows: [
{ title: 'Operation', value: 'Update Rows' },
{ title: 'Set', value: "category, status = 'qualified'" },
],
},
],
edges: [
{ id: 'query-classify', source: 'query', target: 'classify' },
{ id: 'classify-update', source: 'classify', target: 'update' },
],
}
/**
* The running example of the choosing guide: a lead scorer combining a
* workflow-as-tool (enrich), a deterministic Function, an Agent with tools,
* and a deterministic Google Sheets append.
*/
export const LEAD_SCORER_WORKFLOW: PreviewWorkflow = {
id: 'lead-scorer',
name: 'Lead scorer',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 0 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Lead' }],
},
{
id: 'enrich',
name: 'Enrich',
type: 'workflow',
bgColor: '#6366F1',
position: { x: 290, y: 0 },
rows: [{ title: 'Workflow', value: 'enrich-lead' }],
},
{
id: 'reshape',
name: 'Reshape',
type: 'function',
bgColor: '#FF402F',
position: { x: 580, y: 0 },
rows: [{ title: 'Code', value: 'return fields(<enrich.result>)' }],
},
{
id: 'score',
name: 'Score lead',
type: 'agent',
bgColor: '#33C482',
position: { x: 870, y: 0 },
rows: [
{ title: 'Messages', value: 'Score <reshape.result>' },
{ title: 'Model', value: 'claude-sonnet-4-6' },
],
tools: [
{ type: 'exa', name: 'Search', bgColor: '#1F40ED' },
{ type: 'gmail', name: 'Send Email', bgColor: '#E0E0E0' },
{ type: 'hubspot', name: 'CRM', bgColor: '#FF7A59' },
{ type: 'workflow', name: 'Deep Enrich', bgColor: '#6366F1' },
],
},
{
id: 'log',
name: 'Log',
type: 'google_sheets',
bgColor: '#FFFFFF',
position: { x: 1200, y: 0 },
rows: [{ title: 'Operation', value: 'Append' }],
},
],
edges: [
{ id: 'start-enrich', source: 'start', target: 'enrich' },
{ id: 'enrich-reshape', source: 'enrich', target: 'reshape' },
{ id: 'reshape-score', source: 'reshape', target: 'score' },
{ id: 'score-log', source: 'score', target: 'log' },
],
}
/**
* The "Blocks run as soon as they can" diagram on the how-it-runs page: two
* agents that each depend only on Start, so they run concurrently.
*/
export const CONCURRENCY_WORKFLOW: PreviewWorkflow = {
id: 'concurrency',
name: 'Run in parallel',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 80 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Ticket' }],
},
{
id: 'support',
name: 'Customer Support',
type: 'agent',
bgColor: '#33C482',
position: { x: 360, y: 0 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
{
id: 'research',
name: 'Deep Researcher',
type: 'agent',
bgColor: '#33C482',
position: { x: 360, y: 150 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
],
edges: [
{ id: 'start-support', source: 'start', target: 'support' },
{ id: 'start-research', source: 'start', target: 'research' },
],
}
/**
* The "A block waits for all its inputs" diagram: a Function that runs only
* after both agents finish, reading both outputs.
*/
export const COMBINATION_WORKFLOW: PreviewWorkflow = {
id: 'combination',
name: 'Wait for all inputs',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 80 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Ticket' }],
},
{
id: 'support',
name: 'Customer Support',
type: 'agent',
bgColor: '#33C482',
position: { x: 360, y: 0 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
{
id: 'research',
name: 'Deep Researcher',
type: 'agent',
bgColor: '#33C482',
position: { x: 360, y: 150 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
{
id: 'combine',
name: 'Combine',
type: 'function',
bgColor: '#FF402F',
position: { x: 720, y: 75 },
rows: [
{ title: 'Language', value: 'JavaScript' },
{ title: 'Code', value: 'merge(<support.content>, <research.content>)' },
],
},
],
edges: [
{ id: 'start-support', source: 'start', target: 'support' },
{ id: 'start-research', source: 'start', target: 'research' },
{ id: 'support-combine', source: 'support', target: 'combine' },
{ id: 'research-combine', source: 'research', target: 'combine' },
],
}
/**
* The "Branches follow one path" diagram: a Condition splits on an explicit
* rule, and on one branch a Router lets a model choose among paths.
*/
export const ROUTING_WORKFLOW: PreviewWorkflow = {
id: 'routing',
name: 'Branch by condition and router',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 140 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Message' }],
},
{
id: 'condition',
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
position: { x: 340, y: 140 },
rows: [],
branches: [
{ id: 'condition-if', label: 'If', value: "<start.type> === 'lead'" },
{ id: 'condition-else', label: 'else' },
],
},
{
id: 'router',
name: 'Router',
type: 'router',
bgColor: '#28C43F',
position: { x: 740, y: 0 },
rows: [],
branches: [
{ id: 'router-sales', label: 'Sales' },
{ id: 'router-support', label: 'Support' },
],
},
{
id: 'reply',
name: 'Reply',
type: 'agent',
bgColor: '#33C482',
position: { x: 740, y: 280 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
{
id: 'sales',
name: 'Sales',
type: 'agent',
bgColor: '#33C482',
position: { x: 1120, y: -60 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
{
id: 'support',
name: 'Support',
type: 'agent',
bgColor: '#33C482',
position: { x: 1120, y: 90 },
rows: [{ title: 'Model', value: 'claude-sonnet-4-6' }],
},
],
edges: [
{ id: 'start-condition', source: 'start', target: 'condition' },
{ id: 'condition-router', source: 'condition', target: 'router', sourceHandle: 'condition-if' },
{ id: 'condition-reply', source: 'condition', target: 'reply', sourceHandle: 'condition-else' },
{ id: 'router-sales', source: 'router', target: 'sales', sourceHandle: 'router-sales' },
{ id: 'router-support', source: 'router', target: 'support', sourceHandle: 'router-support' },
],
}
/**
* The "When a block fails" diagram: a Function fails and the run leaves through
* its red error port to a handler, while the normal-path block never runs.
*/
export const ERROR_PATH_WORKFLOW: PreviewWorkflow = {
id: 'error-path',
name: 'Handle a failure',
blocks: [
{
id: 'start',
name: 'Start',
type: 'start_trigger',
bgColor: '#2FB3FF',
position: { x: 0, y: 80 },
hideTargetHandle: true,
rows: [{ title: 'Input', value: 'Order' }],
},
{
id: 'throwError',
name: 'throwError',
type: 'function',
bgColor: '#FF402F',
position: { x: 340, y: 80 },
rows: [
{ title: 'Language', value: 'JavaScript' },
{ title: 'Code', value: 'throw new Error("failed")' },
],
},
{
id: 'handleSuccess',
name: 'handleSuccess',
type: 'function',
bgColor: '#FF402F',
position: { x: 720, y: 0 },
rows: [{ title: 'Code', value: 'return ok' }],
},
{
id: 'handleError',
name: 'handleError',
type: 'function',
bgColor: '#FF402F',
position: { x: 720, y: 170 },
rows: [{ title: 'Code', value: 'return recovered' }],
},
],
edges: [
{ id: 'start-throw', source: 'start', target: 'throwError' },
{ id: 'throw-success', source: 'throwError', target: 'handleSuccess' },
{ id: 'throw-error', source: 'throwError', target: 'handleError', sourceHandle: 'error' },
],
}