Skip to main content

States

States represent distinct phases of a conversation. Each state contains tasks to complete and defines rules for transitioning to other states.

State Properties​

PropertyTypeRequiredDefaultDescription
idstringYes—Unique identifier for this state
titlestringYes—Display name for the state
typestringNo"loose"Execution mode (strict or loose)
descriptionstringNo""Purpose of this state
tasksarrayNo[]Tasks to complete in this state
transitionsarrayNo[]Rules for moving to other states

Execution Modes​

The type field determines how the agent processes tasks within the state.

STRICT Mode​

Sequential task processing — one task at a time, in order.

{
"id": "medical-intake",
"title": "Medical Intake",
"type": "strict",
"tasks": [
{ "id": "symptoms", "description": "Collect symptoms" },
{ "id": "duration", "description": "Collect duration" },
{ "id": "medications", "description": "Collect medications" }
]
}

Characteristics:

  • Tasks execute in array order (symptoms → duration → medications)
  • Agent completes one task before moving to the next
  • User cannot skip ahead
  • Ensures all prerequisite information is collected

Best for:

  • Medical or legal intake forms
  • Step-by-step wizards
  • Processes requiring specific order
  • Compliance-sensitive workflows

LOOSE Mode​

Flexible task processing — agent chooses naturally based on conversation flow.

{
"id": "general-inquiry",
"title": "General Inquiry",
"type": "loose",
"tasks": [
{ "id": "topic", "description": "Understand topic of interest" },
{ "id": "preferences", "description": "Learn user preferences" },
{ "id": "budget", "description": "Determine budget range" }
]
}

Characteristics:

  • Agent can address tasks in any order
  • Natural conversation flow
  • User can provide information proactively
  • Agent adapts to user's responses

Best for:

  • Customer support conversations
  • Sales discovery calls
  • General information gathering
  • Exploratory conversations

GOAL Mode​

A natural conversation towards an objective. The agent is not shown a task list; it sees what is still missing towards the goal and asks for it in its own words.

{
"id": "explore-routine",
"title": "Daily Routine",
"type": "goal",
"tasks": [],
"goal": {
"objective": "Understand what a normal day looks like for the participant",
"success_description": "We know when they get up, what fills the morning, and what they look forward to",
"deliverables": [
{ "key": "morning_routine", "description": "What the participant does in the morning", "required": true }
]
},
"transitions": [
{ "target_state_id": "wrap-up", "condition_type": "goal_achieved" }
]
}

How a goal step ends (since 1.4.0):

  • The goal is the state's one task. The agent closes it when the success description is met, or gives it up when the participant declines the subject or asks to move on. The outcome is kept for this state only.
  • Before it closes or gives up the goal, a separate check has to agree, quoting the participant's own words. Small talk, a vague "maybe", or a required answer still marked unconfirmed does not reach a goal.
  • Closing is refused while a required deliverable of the state is missing. If the state's routes read deliverables, one matching route is enough; when none matches, any one of their keys is asked for.
  • A closed goal whose routes match nothing moves on to the next state in plan order. The stuck-state safety net still releases a goal step that makes no progress, and records it as skipped.
  • Insights the agent notices beyond the declared deliverables are kept (up to 25 per session) but don't count as progress.
  • A goal state's tasks stay empty. Tasks left over from switching a state from flexible to goal are a known issue (#770).

Best for:

  • Coaching and reflective conversations
  • Interviews where the conversation should feel open
  • Discovery, where the order of topics doesn't matter

Comparison Table​

AspectSTRICTLOOSEGOAL
Task OrderSequentialFlexibleNo task list, gaps towards a goal
User ControlLimitedHighHigh
Conversation FeelStructuredNaturalOpen conversation
Skip TasksNot allowedAgent decidesThe goal can be given up, after a check
Use CaseForms, intakeDiscovery, supportCoaching, interviews

State Transitions​

Transitions define when and how to move from one state to another.

Transition Properties​

PropertyTypeRequiredDefaultDescription
target_state_idstringYes—State to transition to
condition_typestringNo"all_tasks_complete"Type of condition to evaluate
prioritynumberNo1Lower = higher priority
condition_configobjectNo{}Additional condition parameters

Condition Types​

The state machine supports the following condition_type values:

Condition TypePurposeAdded In
all_tasks_completeTransition once every task in the current state has been addressed (completed or skipped)Existing
goal_achievedTransition once a goal state's goal has been closed or given up (the default for type: "goal"; ends through the state's own task since 1.4.0)Existing
deliverable_existsTransition when a deliverable key has been collectedExisting
deliverable_valueTransition when a deliverable equals a specific valueExisting
turn_count_exceededGuardrail transition based on turn countTicket 5
deliverable_value_inTransition when a deliverable matches one of several valuesTicket 5
deliverable_value_numericNumeric comparison (gt, gte, lt, lte, eq, neq, between)Ticket 5
all_ofComposite AND over nested child conditionsTicket 5
any_ofComposite OR over nested child conditionsTicket 5
compoundComposite with explicit operator (and or or)Ticket 5

Existing Conditions​

all_tasks_complete​

Transitions once every task in the current state — required and optional — has been addressed (completed or skipped). A task with deliverables is addressed automatically when its required deliverables are collected (or, for an all-optional task, once every declared deliverable is collected); a deliverable-less task must be completed or skipped explicitly. A newly entered state is never vacuously complete. See Task Completion for the full rules.

{
"target_state_id": "review",
"condition_type": "all_tasks_complete",
"priority": 1
}

deliverable_exists​

Transitions when a deliverable key has any collected value.

{
"target_state_id": "has_referral",
"condition_type": "deliverable_exists",
"priority": 1,
"condition_config": {
"key": "referral_code"
}
}

deliverable_value​

Transitions when a deliverable equals a specific value.

{
"target_state_id": "premium_support",
"condition_type": "deliverable_value",
"priority": 1,
"condition_config": {
"key": "membership_type",
"value": "premium"
}
}

New Conditions from Ticket 5​

turn_count_exceeded​

Transitions when turn counters cross a threshold. Use this for fallback routing when users get stuck.

{
"target_state_id": "clarify_or_handoff",
"condition_type": "turn_count_exceeded",
"priority": 5,
"condition_config": {
"turns": 3,
"scope": "without_progress"
}
}
  • scope: "without_progress" tracks consecutive turns without completing work
  • scope: "total" tracks total turns in the current state

deliverable_value_in​

Transitions when a deliverable value is in an allowed set.

{
"target_state_id": "high_priority_queue",
"condition_type": "deliverable_value_in",
"priority": 2,
"condition_config": {
"key": "issue_category",
"values": ["billing_error", "payment_failed", "account_locked"]
}
}

deliverable_value_numeric​

Transitions when a numeric deliverable satisfies a comparison rule.

Example 1: threshold check

{
"target_state_id": "urgent_path",
"condition_type": "deliverable_value_numeric",
"priority": 1,
"condition_config": {
"key": "risk_score",
"operator": "gte",
"value": 8
}
}

Example 2: range check

{
"target_state_id": "moderate_path",
"condition_type": "deliverable_value_numeric",
"priority": 2,
"condition_config": {
"key": "risk_score",
"operator": "between",
"min": 4,
"max": 7,
"inclusive": true
}
}

all_of​

Transitions only when all child conditions are true.

{
"target_state_id": "eligible_fast_track",
"condition_type": "all_of",
"priority": 1,
"condition_config": {
"conditions": [
{
"condition_type": "deliverable_exists",
"condition_config": { "key": "consent" }
},
{
"condition_type": "deliverable_value",
"condition_config": { "key": "age_verified", "value": true }
}
]
}
}

any_of​

Transitions when at least one child condition is true.

{
"target_state_id": "expedited_checkout",
"condition_type": "any_of",
"priority": 1,
"condition_config": {
"conditions": [
{
"condition_type": "deliverable_exists",
"condition_config": { "key": "express_checkout" }
},
{
"condition_type": "deliverable_exists",
"condition_config": { "key": "saved_payment_method" }
}
]
}
}

compound​

Composite condition with explicit logical operator.

{
"target_state_id": "ready_to_close",
"condition_type": "compound",
"priority": 1,
"condition_config": {
"operator": "and",
"conditions": [
{
"condition_type": "deliverable_exists",
"condition_config": { "key": "summary_confirmed" }
},
{
"condition_type": "deliverable_value",
"condition_config": { "key": "approval_status", "value": "approved" }
}
]
}
}

UI and JSON Support​

The visual Plan Builder currently exposes three condition types in the edge editor:

  • all_tasks_complete
  • deliverable_exists
  • deliverable_value

Advanced conditions from Ticket 5 are supported in plan JSON and runtime evaluation. They can be authored directly in JSON today.

Transition Priority​

When multiple transition conditions are satisfied, the transition with the lowest priority value wins.

{
"transitions": [
{
"target_state_id": "emergency",
"condition_type": "deliverable_value",
"priority": 1,
"condition_config": {
"key": "urgency",
"value": "emergency"
}
},
{
"target_state_id": "standard-flow",
"condition_type": "all_tasks_complete",
"priority": 10
}
]
}

In this example:

  • If urgency equals "emergency", go to emergency state (priority 1)
  • Otherwise, when all tasks complete, go to standard-flow (priority 10)

Complete State Example​

{
"id": "customer-intake",
"title": "Customer Intake",
"type": "strict",
"description": "Collect essential customer information before routing",
"tasks": [
{
"id": "identify",
"description": "Identify Customer",
"instruction": "Ask for the customer's name and account number",
"required": true,
"deliverables": [
{
"key": "customer_name",
"type": "string",
"required": true
},
{
"key": "account_number",
"type": "string",
"required": true
}
]
},
{
"id": "classify",
"description": "Classify Issue",
"instruction": "Determine the type of issue: billing, technical, or general",
"required": true,
"deliverables": [
{
"key": "issue_type",
"type": "enum",
"enum_values": ["billing", "technical", "general"],
"required": true
}
]
}
],
"transitions": [
{
"target_state_id": "billing-support",
"condition_type": "deliverable_value",
"priority": 1,
"condition_config": {
"key": "issue_type",
"value": "billing"
}
},
{
"target_state_id": "technical-support",
"condition_type": "deliverable_value",
"priority": 1,
"condition_config": {
"key": "issue_type",
"value": "technical"
}
},
{
"target_state_id": "general-support",
"condition_type": "all_tasks_complete",
"priority": 10
}
]
}

State Flow Diagram​

A typical conversation flows through multiple states:

┌─────────────┐
│ Greeting │ (loose) - Welcome user, collect name
└──────┬──────┘
│ all_tasks_complete
▼
┌─────────────┐
│ Intake │ (strict) - Collect required information
└──────┬──────┘
│ deliverable_value (based on issue_type)
▼
┌─────────────────────────────────────┐
│ Billing │ Technical │ General │
└────┬─────┴─────┬─────┴──────┬──────┘
│ │ │
└───────────┼────────────┘
│ all_tasks_complete
▼
┌─────────────┐
│ Farewell │ (strict) - Close conversation
└─────────────┘

Next Steps​

  • Tasks — Learn about task configuration
  • Deliverables — Configure data collection
  • Examples — See complete plan examples