Config JSONB — the agent & workflow config data dictionary¶
Status: Current | Last reviewed: 2026-09-21
The field-by-field reference for the two config JSONB blobs that hold every
runtime definition in the platform: the agent pipeline on
agent_versions.config and the workflow graph on workflow_versions.config
(= workflow_configs.config). Read this when you need to know what a specific
config key does and who reads it; read schema-guide.md
first for the surrounding columns and the identity-vs-version split.
Both blobs are generated into canonical JSON Schema by generate-config-schemas.ts, which is the source of truth this doc tracks. Every field below exists in those schemas; when in doubt, grep agent-config.schema.json or workflow-config.schema.json.
GPT Live is discriminated by provider: "openai" and service: "GPT Live API".
Its config.delegation_backend reuses the service-stage schema with
type: "llm", so the nested backend configuration is validated against its
own provider/service definition. See GPT Live configuration.
config.tool_progress_messages_enabled is an optional boolean, default false:
it controls authored per-tool progress speech in both Live delegation modes.
How the schemas are composed¶
The generator builds each schema from three sources of truth so they cannot drift from the code:
| Source | Feeds | How |
|---|---|---|
| Hand-authored envelope (in the generator) | Top-level keys + nested branches (Settings, Transfer, OutputLane, …) |
Kept in lockstep with pipeline-types.ts (AgentVersionConfig) and workflow-types.ts (WorkflowConfig) |
| 50 service-definition JSONs (services/) | Per-stage config |
Each becomes a branch of an allOf/if-then discriminated on (provider, service) |
| 23 workflow node manifests (manifests/) | Per-node config |
Each becomes a branch of an allOf/if-then discriminated on type |
Both schemas declare a $id under https://trueserve.ai/schemas/
(agent-config.schema.json, workflow-config.schema.json) and use JSON Schema
draft-07. Every leaf property carries a non-empty description — the schema
doubles as the data dictionary, and the generator's description gate fails
without one.
Validation is non-blocking. At publish time schema-validate.ts runs the config through ajv and surfaces mismatches as warnings only, layered on top of the imperative validators (validate-agent-config, workflow-validation.ts) — it never gates a save, so live configs that predate a schema change are not blocked. The CI workflow config-schema-drift.yml fails if the committed schemas differ from a fresh generation or any property lacks a description.
Note: the discriminated
configbranches are open (additionalProperties: true). An unrecognised(provider, service)ortypematches noif, so the openconfigstill accepts it — known stages/nodes validate precisely without rejecting newly-added ones.
Two validation tiers: structural vs buildability¶
"Is this config valid?" has two distinct answers, enforced by two tiers that share one source of truth and cannot disagree with the runtime:
| Tier | Question | Source of truth | Where it runs |
|---|---|---|---|
| Structural | Are the field shapes/types/enums right? | The generated agent-config.schema.json |
ai-manager (ajv, in the editor + at publish) and ai-orchestrator (jsonschema, in preflight) — the same file |
| Buildability | Will the agent actually build + start a session? | The real pipeline builder | ai-orchestrator preflight (preflight.py) |
Structural validity does not imply the agent will run — credentials,
provider/service mapping, and per-wrapper config rules live outside the schema.
The buildability preflight closes that gap: ai-manager's agent editor POSTs
to POST /api/agents/[agentId]/preflight → the orchestrator's HTTP /preflight
→ preflight_agent,
which runs the agent's stages through the same building blocks the live build
uses — get_service_mapping, ServiceRegistry.validate_service_config, and
get_api_key (credential resolution) — but stops before wrapper.create(),
so it resolves and validates without opening any provider/MCP sockets
("resolve, don't connect"). Because it reuses the real builder, a preflight
ok can't drift from what a real test does.
It returns { ok, errors[], warnings[] } with each issue tagged by gate:
structural (shape / missing provider+type), service_mapping
(unknown (provider, type, service)), service_config (wrapper
validate_config rejected it), credential (the stage's integration_id /
env var resolves to no key), or tool_ref. The editor's test buttons
(voice call + chat) stay disabled while ok is false, showing the first
error as the reason — so an agent that can't start can't be tested. The gate it
does not make: ok: true means the session will start, not that the
conversation is semantically good (a key present but rejected by the provider
only surfaces on a live connect). See
universal-pipeline-builder.md.
"Reuses the real builder" means the builder for that stage¶
get_service_mapping (service_constants.py) is the global
(provider, type, service) → wrapper-key table, and it is the right one for
main-pipeline stages. It is not the only one:
TranslationPipelineBuilder resolves the stages inside
config.translation.flows.{incoming,outgoing}.pipeline through its own
TRANSLATION_SERVICE_LOOKUP, which both adds translation-only services
(Phrase Splitter → PhraseSplitterProcessor, LLM Translation →
LLMTranslationProcessor) and redirects a shared one (ElevenLabs
WebSocket API → FastElevenLabsTTSService, a lower
chunk_length_schedule for faster translated audio). The two tables can't be
merged for exactly that reason: the same triple must resolve differently
depending on which builder owns the stage.
Preflight therefore picks its resolver off the flow marker
_iter_stages already yields — get_translation_service_mapping for a flow
stage, get_service_mapping otherwise. Using the global table for every stage
made preflight report service_mapping errors for the two trueserve stages of
a translation agent that builds and runs fine, so ok was permanently
false and the editor's voice + chat test buttons were disabled for every
cascaded (STT → split → translate → TTS) translation agent. Single-stage
S2S translation agents were unaffected because their triples
(openai/s2s_translation/Realtime Translate, google/s2s_translation/S2ST)
happen to exist in both tables — which is why the failure looked
agent-specific rather than structural. Note the translation resolver
never raises: an
unknown triple falls through to the stage's own service name, and the registry
gate is what rejects it — so a bad flow stage still blocks, just at
service_config rather than service_mapping, mirroring where the live build
fails. Regression coverage:
test_preflight_translation_mapping.py.
Agent config — agent_versions.config¶
Source of truth: AgentVersionConfig in
pipeline-types.ts. Top-level keys:
| Field | Type | Meaning | Read by |
|---|---|---|---|
$schema |
string | Optional URL of the schema, stamped on newly-saved configs. | — |
agent_type |
enum | Legacy runtime discriminator (voice_ai, voice_augmentation, agent_assistant, translation_agent, text_chat). Derived from agents.subtype; the orchestrator still reads it. |
ai-orchestrator |
pipeline |
PipelineEntry[] |
The core of the agent: ordered stages, parallel groups, swimlane groups. | ai-orchestrator (build), ai-manager (canvas) |
settings |
Settings |
Global pipeline settings (see below). | ai-orchestrator |
output_lanes |
{ [id]: OutputLane } |
Named transcript/analysis output lanes, each with parallel branches. | ai-orchestrator |
translation |
UnifiedTranslation |
Bidirectional translation flows. Translation agents only. | ai-orchestrator |
copilot |
CopilotConfig |
The prescriptive Agent Assist branch. Copilot agents only — and on those it holds the whole behaviour, with pipeline down to one transcription stage (see below). |
ai-orchestrator, ai-manager |
resilience |
Resilience |
Fallback-pipeline + health-check behavior. | ai-orchestrator |
lifecycle |
Lifecycle |
Workflow ids to run on conversation start/end (canonical home — see below). | ai-orchestrator |
avatar |
Avatar |
Photorealistic avatar rendering config. | surface-app, ai-orchestrator |
transfer |
Transfer |
Warm-transfer / consult config. | ai-orchestrator |
local_action_tool_ids |
string[] | Copilot-only: host_action tool UUIDs rendered as clickable suggestion buttons in the host. |
surface-app/host |
name, description, extension |
string | Deprecated mirrors of agents.* / stage config. Stripped from agent configs by 004; nothing reads them. |
— |
Warning: panel-scoped top-level agent-config sections (
transfer,resilience,local_action_tool_ids,copilot) ride an opaquemiscbranch through the collab Yjs allow-list. A new top-level agent key is dropped on every autosave unless added toMISC_ROOT_FIELDSin both copies of pipeline-shape.ts. See universal-pipeline-builder.md.
ServiceStage (a pipeline entry)¶
One processing node in the pipeline. pipeline entries are a ServiceStage, a
legacy parallel group (array of branches, each an array of stages), or a
SwimlaneGroup. Stages run in array order; nested arrays run concurrently.
| Field | Type | Meaning | Read by |
|---|---|---|---|
provider |
string | Provider slug (openai, google, elevenlabs, deepgram, aws, …). Required. |
ai-orchestrator |
type |
string | Stage kind: stt, llm, tts, s2s, s2s_translation, translation, stt_suggestion, assistant_agent, processor, utility. Required. |
ai-orchestrator |
service |
string | Specific service variant (matches a service-def service field, e.g. Streaming Completions). |
ai-orchestrator |
id |
string | Stable slug derived from stage_name; the injection-target name and canvas node id. |
ai-orchestrator, ai-manager |
stage_name |
string | Human label shown on the canvas node. | ai-manager |
description |
string | Author note rendered in the node body (documentation only). | ai-manager |
integration_id |
string | Credential integration (integrations.id); falls back to the tenant provider default when omitted. |
ai-orchestrator |
tools |
string[] | Tool refs the stage's LLM may call: a tool_config UUID, builtin:<id>, or inline:<id>. |
ai-orchestrator |
position |
{x,y} |
Canvas coordinates (editor layout only; no runtime effect). | ai-manager |
config |
object | Service-specific config, discriminated on (provider, service) — see below. |
ai-orchestrator |
The config shape is selected by an allOf/if-then branch (one per of the
50 service definitions). Two cross-cutting config keys are added to every
stage branch by the generator:
config.* key |
Meaning | Read by |
|---|---|---|
builtin_tool_configs |
Rich per-node config for selected built-in tools, keyed by unprefixed built-in id — message_injection (default_priority, trigger_response, wait_for_tts_end) and ivr_navigation (goal_mode, goal, goal_template). |
ai-orchestrator |
capability_configs |
Per-capability config for registry capabilities (capability:<slug> refs in config.tools), keyed by slug. The value shape is the capability manifest's config_schema (tenant-defined), so it is validated dynamically at publish (warnings only) rather than in the static schema. DRAFT-only in practice: publish expands the refs and this block travels along untouched. See capability packs. |
ai-manager (publish expander) |
ces_integration |
Google CES-only surface: mcp_url_catalogs, warm_transfer_profile, tool_dispatch[], child_agents[] baked into the generated CES MCP URL. |
ai-orchestrator, agent-host |
On PUBLISHED versions only, the root may carry _capability_provenance
(keyed by slug: definition_id, version, manifest_snapshot) — stamped by
capability-expander.ts
when publishAgentVersion expands capability: refs into inline tools /
logic / prompt text / variables. Drafts never carry it, and collab never
reads published rows, so it deliberately stays out of MISC_ROOT_FIELDS.
Note: a handful of stage-config params are polymorphic —
input_audio_transcription(bool / object / null),turn_detection(enum / object / null), andtools(string refs or inline function-definition objects). The schema emits the accurateoneOf;model/voice/languageare open strings (the service-defoptionslist is the curated UI set, not a validation constraint).
SwimlaneGroup¶
The modern parallel construct (a pipeline entry with type: "swimlane_group").
Branches run concurrently with shared turn-taking and a join policy.
| Field | Type | Meaning |
|---|---|---|
swimlanes[] |
array | Parallel branches: each { stages: ServiceStage[], _swimlane: {id,label,color,turn_priority,gating} }. _swimlane is editor metadata; gating:"off" bypasses the turn gate for analysis-only lanes. |
join_policy.audio |
enum | mix (PCM-sum), first_speaker, or drop. Default mix. |
join_policy.transcripts |
enum | merge or keep_separate. Default merge. |
join_policy.cancel_propagation |
enum | any (OR) or all (AND). Default any. |
join_policy.turn_strategy |
enum | none, explicit_protocol, or barge_in. Default none. |
cross_feeds[] |
array | Inter-swimlane text feeds: {source_swimlane, target_swimlane, frame_types[], template?, debounce_ms?, finalized_only?} — pipe one lane's frames into another lane's LLM context. |
OutputLane / LaneBranch¶
output_lanes is keyed by lane id; each value is an OutputLane that taps the
main pipeline and runs parallel processing branches.
| Field | Type | Meaning |
|---|---|---|
OutputLane.enabled |
bool | Master switch for the lane. |
OutputLane.branches[] |
LaneBranch[] |
Parallel processing branches. |
OutputLane.source_node_index |
int | Main-pipeline node index this lane taps (per-branch can override). |
OutputLane.frame_types[] |
string[] | Frame types flowing into the lane (e.g. transcription, metadata). |
OutputLane.output_type |
enum | output (shown in Transcripts Out) or analysis (processed, not surfaced). |
LaneBranch.processors[] |
ServiceStage[] |
Sequential stages for the branch. |
LaneBranch.label |
string | Branch label (e.g. Sentiment, Compliance). |
LaneBranch.injection |
object | Local injection {target, prompt}: route messages to this branch and prompt its LLM. |
LaneBranch.transcript_filter |
object | {transcript_types[], speaker_roles[]} filtering which transcripts the branch processes. |
_legacy_essentials (on both) is a backward-compat toggle that auto-appends
essential frame types when absent/true; new configs set it false.
UnifiedTranslation / TranslationFlow¶
| Field | Type | Meaning |
|---|---|---|
flows.incoming |
TranslationFlow |
Caller→agent direction. |
flows.outgoing |
TranslationFlow |
Agent→caller direction. |
TranslationFlow.pipeline[] |
ServiceStage[] |
Stages for the direction (STT→translation→TTS, or a single S2ST stage). The only required field. |
TranslationFlow.echo_original_audio |
bool | Echo the untranslated audio at whisper volume. |
TranslationFlow.echo_volume |
number | Echo volume percent (5–50, default 25). |
Warning: the language pair's canonical home is now the translation / S2ST stage's own
config(config->>target_language,echo_target_language, and the stage's voice selection), not the flow. The flow-levelsource_language,target_language, andvoice_configare deprecated legacy fields, used only by cascaded STT→translate→TTS flows and otherwise collapsed into the stages. Do not author the pair at flow level for S2ST / realtime translation stages.
Settings (config.settings)¶
Source of truth: UniversalPipelineSettings in pipeline-types.ts.
| Field | Type | Meaning | Read by |
|---|---|---|---|
audio |
object | Transport audio I/O: format (pcm16/pcmu/pcma), sample_rate, channels. pcm16 for WebRTC; G.711 for telephony. |
ai-orchestrator |
compliance |
object | {enabled, redact_pii} — PII redaction of transcripts and stored messages. |
ai-orchestrator |
lifecycle |
Lifecycle |
Mirror of top-level config.lifecycle. No longer read — 004 drops this nested key. |
— |
text_turns |
object | Turn-taking for multi-branch text_chat agents: mode (concurrent_serialized/sequential_shared), order (completion/config), allow_silence. Consumed only when text_chat with 2+ branches. |
ai-orchestrator |
context_summarization |
bool | object | Auto context summarization (off by default). true uses defaults; an object overrides max_context_tokens (8000), target_context_tokens (6000), max_unsummarized_messages (20), min_messages_after_summary (4). Cascaded voice + text only; S2S manages context server-side. |
ai-orchestrator |
swimlane |
object | Simultaneous-speech mixing config for 2+ parallel S2S/realtime branches; drives TurnAwareMixer + SwimlaneAudioTagger. |
ai-orchestrator |
rate_limits |
array | Named rate-limit profiles referenced by workflow llm_agent nodes. Agent configs rarely set this. |
async-agent |
Resilience, Avatar, Transfer¶
$def |
Key fields | Read by |
|---|---|---|
Resilience |
enabled, fallback_pipeline_id (agent id), health_check_strategy (none/on_demand/proactive), health_check_interval_seconds. |
ai-orchestrator |
Avatar |
enabled, provider (azure_avatar/bey), Azure: integration_id/character/style/voice/background_color/background_image; BeyondPresence: bey_avatar_id/bey_integration_id. |
surface-app, ai-orchestrator |
Transfer |
enabled, provider (genesys_byoc/webex_byova/scv_native/genesys_cloud), consult_agents[], hold_mode (music/listen_only/talk_and_listen), post_merge_mode (drop_both_agents/keep_agent_a/keep_both), consult_timeout_sec, recording, role:"consult". |
ai-orchestrator |
Lifecycle |
on_start, on_complete — workflow_configs.id (or null) to run on conversation open/end. Canonical at top level; settings.lifecycle is dead. |
ai-orchestrator |
Injection and Ivr are defined in the schema's $defs but are not
top-level fields on AgentVersionConfig — that config lives per-stage under
config.builtin_tool_configs. They are absent from MISC_ROOT_FIELDS
accordingly, and pipeline-shape.test.ts
asserts a top-level injection/ces/warm_transfer/ivr key does not survive
the Y.Doc round-trip.
Agent Logic (variables, per-stage logic / tool_logic)¶
$def |
Key fields | Read by |
|---|---|---|
AgentVariable (top-level variables[]) |
name (snake_case, session-variable namespace), type, source (mutable/linked), default, linked_to, description (required). In MISC_ROOT_FIELDS (both copies). |
ai-orchestrator (Phase 3+), editor |
StageLogic (pipeline[N].config.logic) |
rules[] (trigger/condition/actions), response_guards[] (check/on_violation/enforcement/strict). |
ai-orchestrator (Phase 3+) |
ToolLogic (pipeline[N].config.tool_logic.<toolName>) |
available_when (fail closed), unavailable_message, bind (schema-stripped params), capture. Sibling of tools, never a mutation of it. |
ai-orchestrator (Phase 3+) |
Full semantics, grammar, and validation rules: agent-logic.md.
The copilot branch (Agent Assist)¶
Source of truth: CopilotConfig in pipeline-types.ts. Present on
subtype=copilots agents instead of hand-wired suggestion stages, and
unique among the top-level branches in that it holds the whole behaviour of
the agent: config.pipeline carries exactly one real stage (transcription) and
everything else is planned from here. See
copilots.md for the runtime.
| Field | Type | Meaning | Read by |
|---|---|---|---|
transcription.enhancement |
object | Optional transcript rewrite before any type reads it: enabled, llm_provider, llm_model, integration_id, context_window, enhancement_mode (domain_correction default | full_enhancement), system_prompt. full_enhancement also rewrites grammar, making the transcript a paraphrase. |
ai-orchestrator |
transcription.glossary.terms[] |
array | {term, hint} domain vocabulary fed to enhancement and (opt-in) translation. |
ai-orchestrator |
variables |
Record<string,string> |
Session-start bindings ($var:NAME) usable in every copilot prompt. |
ai-orchestrator |
gate |
object | The shared actionability gate: enabled, llm_provider, llm_model, integration_id, min_utterances, debounce_seconds, context_window, runs_on, prompt_override. |
ai-orchestrator |
types[] |
CopilotTypeConfig[] |
One configured behaviour each — see below. | ai-orchestrator, ai-manager |
summarization.on_transfer |
object | {enabled, llm_provider, llm_model, integration_id, system_prompt, card} — publishes live, pinning a SummaryCard while the call is still up. |
ai-orchestrator |
summarization.post_call |
object | Same shape plus optional response_schema (custom object fields), card_bindings (card property → response.<path>), and context_window (turns; default all), extracted during teardown; persisted as a copilot_summary conversation event and replayed on the historical SSE stream. |
ai-orchestrator, conversation-api |
types[] entries are a discriminated-ish union on kind
(suggested_answers | coaching | custom | legacy translation | form_filling). Shared
fields: id, kind, label, runs_on (agent|customer|both),
enabled, card, llm_provider, llm_model, integration_id,
bypass_gate, context_window, auto_summarize_context, min_utterances,
debounce_seconds, temperature, layout_region. Per-kind:
coaching → system_prompt, tool_ids[], inline_tools[], tool_logic (normal per-tool bindings/conditions/captures); custom → same plus response_schema and card_bindings;
suggested_answers → prompt_override, required knowledge_tool_ids[], inline_tools[], tool_argument_mapping, and query_refinement (llm_provider, llm_model, integration_id, system_prompt, response_field_descriptions); translation →
target_language, glossary_ref, render; form_filling → forms[] +
allow_dynamic.
Every prompt-bearing branch accepts response_field_descriptions: JSON Pointer → description text, applied only to existing fields. Query refinement always returns {query: string}, used in Knowledge input mappings. Context windows are in turns. LatestMessage, LatestCustomerMessage, LatestAgentMessage, ConversationHistory, and answer-only KnowledgeSources are reserved runtime variables, not session-start bindings.
Four properties of this branch are contractual, not conventions:
types[]being a list is the discriminator.is_copilot_pipeline_configkeys on it, and an empty list still matches — a copilot with nothing enabled is a real agent that transcribes and suggests nothing. The legacy hand-wired shape carries the sameagent_type: 'agent_assistant'with nocopilotkey, so it keeps building throughUniversalPipelineBuilder.cardis<catalog-uri>#<ComponentName>. Built-in card workers use itsprops_schemaas their response contract. Custom Copilot and post-call summaries can define an independentresponse_schemaand map its values withcard_bindings; mapped props must validate against the card schema. An unresolvable ref means the worker is skipped, not downgraded.translationis the exception (inline kind, built-in schema, never consults a card).idis minted once at add time and never re-derived. The A2UIsurface_idiscopilot-<id>, and a surface replaces in place only while its id is stable, so re-deriving from a renamed label stacks a new card per rename.copilotmust stay inMISC_ROOT_FIELDSin both copies ofpipeline-shape.ts(see the warning above). It is a panel-scoped branch, so it rides the opaquemiscY.Map — and an unlisted key is dropped on every autosave with the change visible in the editor.
Workflow config — workflow_versions.config / workflow_configs.config¶
Source of truth: WorkflowConfig in
workflow-types.ts. Top-level keys:
| Field | Type | Meaning | Read by |
|---|---|---|---|
$schema |
string | Optional schema URL, stamped on newly-saved configs. | — |
name |
string | Display name. Authoritative copy is the table row (workflow_configs.name); this JSONB mirror is optional — ~1/4 of live configs omit it. |
ai-manager |
description |
string | Human-readable workflow description. | ai-manager |
execution_mode |
enum | sync (caller blocks; forbids wait/human_review/conversation_actions), async (returns execution_id), or batch (implies async; events buffered + flushed). Unset = sync. |
async-agent |
settings |
WorkflowSettings |
Runtime limits, retry policy, rate limits, batch-mode config. | async-agent |
phases |
WorkflowPhase[] |
Canvas grouping containers (no runtime effect). | ai-manager |
nodes |
WorkflowNode[] |
The workflow nodes. Required. | async-agent |
edges |
WorkflowEdge[] |
Directed edges with optional conditional routing. Required. | async-agent |
triggers |
WorkflowTrigger[] |
Versioned trigger definitions; projected to trigger_rules on publish. |
ai-manager (publish) |
input_parameters |
WorkflowParam[] |
Declared inputs (test panel + trigger field-mapping). | async-agent |
output_parameters |
WorkflowResponseVar[] |
Declared outputs (alias of response_variables). |
async-agent |
response_variables |
WorkflowResponseVar[] |
Outputs exposed to conversation lifecycle hooks ({{lifecycle.<name>}}). |
async-agent, ai-orchestrator |
inline_tools |
array | Workflow-local tool defs (Python/API/knowledge), referenced as inline:<id>. Knowledge entries carry source_slug but never the connection (server_url/transport/token) — the MCP URL is derived at runtime from the tenant's Knowledge settings. |
async-agent, agent-host |
communication_templates |
object | Reusable SMS/email templates keyed by name; referenced by notification nodes via template_ref. |
async-agent |
tool_definitions |
array | Legacy workflow-exposed tool definitions. | async-agent |
Warning: as with agent configs, the collab Yjs shape is an allow-list. A new top-level
WorkflowConfigkey is dropped on every autosave unless added in both directions to workflow-shape.ts. This silently atetriggersandexecution_modeonce.
WorkflowNode shared fields¶
| Field | Type | Meaning |
|---|---|---|
id |
string | Unique node id within the workflow. Required. |
type |
enum | Node type (see table below); selects the runtime handler and the config shape. Required. |
label |
string | Human-friendly name (never a lookup key — id stays fixed). |
goal |
string | Optional description of the node's purpose. |
phase |
string | Optional phase id (canvas grouping). |
parent |
string | Parent foreach/subworkflow node id (nested body nodes). |
provider |
string | LLM provider hint. |
integration_id |
string | Credential integration id used by the node. |
position / size |
object | Canvas layout only. |
config |
object | Per-node-type config, discriminated on type — see below. |
WorkflowEdge is {from, to, condition?, kind?, label?}: use START/END
pseudo-nodes for entry/exit; condition is the router output id that selects
the edge; kind:"body_ref" marks a layout-only foreach/parallel body edge
skipped at runtime.
Node types¶
The config shape is discriminated on type. The 23 types carried in the
generated schema (one per manifest):
type |
Purpose |
|---|---|
llm_agent |
Run an LLM with an instruction, optional tools, optional structured output. |
router |
Branch into named outputs by per-output expression (first truthy wins). |
tool |
Execute a registered tool directly without an LLM. |
code |
Run sandboxed Python (async def execute(ctx)). Retired in the editor; loads as a Tool node selecting an inline Python tool. |
http_request |
Call any HTTP endpoint with retries, timeout, integration-backed auth. |
assignment |
Write workflow variables (set/append/prepend/concat/increment). |
conversation_actions |
Create/reuse a conversation, optionally start a live session, inject an instruction, mint a share code. |
notification |
Send a message via an integrated channel (SMS, email, push). |
human_review |
Suspend until a human approves or rejects. |
wait |
Suspend until a duration elapses or an event arrives. |
foreach |
Run a nested body once per item in a list/dataset (durable, bounded concurrency). |
parallel |
Deprecated. Use foreach with mode:"parallel". |
subworkflow |
Call another workflow as a subroutine (inline or ref; depth-capped at 2). |
ocr_extract |
Extract text/tables/forms/structured data from a document via OCR, LLM vision, or both. |
transcribe |
Transcribe an audio file via a managed STT provider. |
pii_redaction |
Detect and redact PII from a string before downstream nodes. |
reasoning_step |
Execute a single reasoning step with typed output and retry. |
reasoning_chain |
Sequence multiple reasoning steps with shared state and a cost budget. |
plan_extract |
Extract a structured plan from conversation messages. |
score_generation |
Compute a numeric score from workflow fields (local weighted average or managed AutoML). |
salesforce |
First-class deterministic Salesforce step (create/update/query/publish), no LLM. |
scheduled |
Legacy delay node. Prefer wait with a duration condition. |
passthrough |
No-op fallback for unknown node types. |
Note: TS
NODE_TYPESalso definesnote(a canvas-only annotation, never traversed at runtime). It has no manifest, so it is not in the generated schema'stypeenum and itsconfig(text,color) is not discriminated.
Router node — canonical shape¶
The router config is { outputs: [{id, label, expression?}] }. Outputs are
evaluated in order; route_value = the first match's id, which equals the
outgoing edge's condition, so the target node is inferred entirely from the
edge you drew on the canvas. The legacy condition_field / routes shape was
migrated to outputs[] by
004 (and the Python
back-compat branch in router.py
removed) — it is no longer supported at runtime.
ocr_extract node — canonical shape¶
Canonical fields: ocr_integration_id, document_source
(url/base64/s3_uri/gcs_uri/azure_blob_url/minio_key),
document_reference, processing_type (ocr/llm/both), aws_features (and
aws_queries, gcp_processor_id/gcp_location, azure_model_id), plus the LLM
side (llm_integration_id, llm_model, llm_prompt, llm_output_schema,
llm_input_mode). The legacy input{} / integration_id / features /
output_schema shape was migrated by
004 (matching the
Python normaliser removed in
ocr_extract.py).
WorkflowSettings, triggers, params¶
$def |
Key fields |
|---|---|
WorkflowSettings |
max_runtime_minutes, max_total_llm_calls, timeout_action, retry_policy{max_retries,backoff_base_seconds}, llm_execution, rate_limits[], batch_mode{enabled,flush_cron,interval_seconds,max_window_seconds,max_batch_size,group_by,llm_defaults}. |
WorkflowTrigger |
kind (api/agent_tool/conversation_start/conversation_end/schedule/salesforce/gcp_pubsub/aws_eventbridge), enabled, label, condition. conversation_*/schedule/salesforce/gcp_pubsub/aws_eventbridge project trigger_rules rows on publish; api/agent_tool do not. |
WorkflowParam |
name, type, description, required, is_input, default, enum, items. Referenced as {{input.<name>}}. |
WorkflowResponseVar |
name, source (dot-path or JSONata), source_mode (path/jsonata), description, type. |
How to regenerate¶
cd shared/service-definitions
npx tsx generate-config-schemas.ts # write the 2 JSON schemas + generated-schemas.ts
npx tsx generate-config-schemas.ts --check # exit 1 if committed files drift (the CI gate)
npx tsx generate-config-schemas.ts --report # list properties missing a description, don't fail
A plain run writes agent-config.schema.json, workflow-config.schema.json, and the bundler-safe TS module generated-schemas.ts that the ajv validator imports. Re-run after editing a service definition, a node manifest, or the envelope in the generator — otherwise config-schema-drift.yml fails CI.
See also¶
- Schema guide — the columns around these blobs.
- Config realign backlog — tracked dual-home / legacy-field convergence work.
- pipeline-types.ts · workflow-types.ts — the TS source of truth.
- Universal pipeline builder — how the orchestrator consumes the agent config.
Copilot form instances and applicability¶
CopilotType.catalogs and CopilotSummarizationTarget.catalogs are selected catalog URI arrays. Form Filling uses CopilotType.cards as its available card-reference palette. CopilotForm.card chooses one reference (implicit when the palette has one card), and card_bindings maps display fields to selected-card properties. repeat_mode is once (default) or repeatable; completion_mode is agent_confirmed (default) or required_fields. Each configured form gets its own worker, with instance state held by the active pipeline. The generated schema describes all these fields.
The runtime adds the applicable/reason/nullable-result response envelope; it is not authored into response_schema. response_field_descriptions keys under /envelope/properties/ override applicability descriptions. Other keys continue to address the result schema. SDK dismissal policy is presentation state and is not stored in the agent's Copilot configuration. See Copilots.
Inline tool testing¶
Inline Python/API/Knowledge tool definitions can carry output_schema and a version-1 testing object. The same data-only mock schema is used for library-tool revisions. Simulation submission pins these fields together with the selected agent version. See Tool testing.
Saved conversation tests¶
agent_test_cases.persona contains the simulated caller's name, goal, emotional
state, language and optional opening line, background and facts. Submission copies
the caller into agent_sim_batches.spec.personas and snapshots selected case
revisions, expected outcomes, turn limits and linked goldens under spec.test_cases.
spec.test_case_ids preserves the selection for reruns. These are evaluation
records; the agent's runtime configuration is pinned separately in target.
See Agent evaluation API.