Skip to main content
Action Runner allows the AI agent to execute pre-built flows by emitting [ACTION:slug:{"params"}] tags in chat responses. The system intercepts these tags, executes the flow steps, and streams status updates back to the user.

How It Works

  1. AI emits action tag — Model outputs [ACTION:slug:{"params"}] in its prose
  2. Interceptor strips tag — Action-runner plugin removes tag from visible chat
  3. Flow execution — System fetches flow from Directus action_flows collection
  4. Status streaming — Each step streams status back to chat interface
  5. Results logged — Execution logged in agent_audits collection

Flow Storage

Flows are stored in the Directus action_flows collection:
FieldTypeDescription
slugstringUnique identifier (used in ACTION tag)
namestringHuman-readable name
descriptiontextWhat the flow does
stepsJSONArray of step definitions
activebooleanEnable/disable flow

Built-in Action Flows

SlugPurpose
scout-analyzeScrape URL + AI analysis
taxonomy-tagAuto-classify content with 6-concept taxonomy
post-createDraft platform-specific social post
message-generateFan engagement message
memory-recallSearch stored data + summarize
media-processQueue media job (watermark, teaser, compress)

Step Types

The Action Runner supports 14+ step types. Each step can reference previous step results using variable interpolation.

Directus Steps

directus_read

Read items from a Directus collection.
{
  "type": "directus_read",
  "result_key": "user_data",
  "config": {
    "collection": "user_personas",
    "filter": {"user_id": {"_eq": "$input.user_id"}},
    "fields": "id,onboarding_state,brand_colors",
    "limit": 1
  }
}
Config options:
  • collection (required) — Collection name
  • filter — Filter object (Directus query syntax)
  • fields — Comma-separated field names
  • limit — Max items to return
  • sort — Sort field (-field for descending)
  • id — Read single item by ID

directus_write

Create a new item in a collection.
{
  "type": "directus_write",
  "result_key": "new_post",
  "config": {
    "collection": "scheduled_posts",
    "item": {
      "creator_profile_id": "$input.profile_id",
      "platform": "onlyfans",
      "caption": "$input.caption",
      "scheduled_time": "$input.scheduled_time",
      "status": "pending"
    }
  }
}

directus_update

Update an existing item.
{
  "type": "directus_update",
  "result_key": "updated",
  "config": {
    "collection": "creator_profiles",
    "id": "$input.profile_id",
    "item": {
      "last_scraped": "$context.timestamp"
    }
  }
}

directus_delete

Delete an item.
{
  "type": "directus_delete",
  "result_key": "deleted",
  "config": {
    "collection": "scheduled_posts",
    "id": "$input.post_id"
  }
}
Full-text search across a collection.
{
  "type": "directus_search",
  "result_key": "search_results",
  "config": {
    "collection": "scraped_media",
    "query": "$input.search_term",
    "fields": "id,caption,engagement_rate",
    "limit": 10
  }
}

directus_trigger

Trigger a Directus Flow.
{
  "type": "directus_trigger",
  "result_key": "flow_result",
  "config": {
    "flow_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "payload": {
      "user_id": "$input.user_id",
      "action": "send_notification"
    }
  }
}

Stagehand Steps (Browser Automation)

stagehand_start

Start a new browser session.
{
  "type": "stagehand_start",
  "result_key": "session",
  "config": {}
}
Returns: { sessionId: "xyz123" }

stagehand_navigate

Navigate to a URL.
{
  "type": "stagehand_navigate",
  "result_key": "nav_result",
  "config": {
    "sessionId": "$session.sessionId",
    "url": "$input.target_url"
  }
}

stagehand_extract

Extract data from current page using AI.
{
  "type": "stagehand_extract",
  "result_key": "page_data",
  "config": {
    "sessionId": "$session.sessionId",
    "instruction": "Extract all post titles and engagement metrics",
    "schema": {
      "posts": {
        "type": "array",
        "items": {
          "title": "string",
          "likes": "number",
          "comments": "number"
        }
      }
    }
  }
}

stagehand_act

Perform an action on the page (click, type, etc.).
{
  "type": "stagehand_act",
  "result_key": "action_result",
  "config": {
    "sessionId": "$session.sessionId",
    "action": "Click the login button"
  }
}

stagehand_close

Close browser session.
{
  "type": "stagehand_close",
  "result_key": "closed",
  "config": {
    "sessionId": "$session.sessionId"
  }
}
Start session with stored platform cookies (bypasses login).
{
  "type": "stagehand_cookie_login",
  "result_key": "auth_session",
  "config": {
    "creator_profile_id": "$input.profile_id",
    "platform": "onlyfans",
    "url": "https://onlyfans.com/my/subscribers"
  }
}
Fetches decrypted cookies from platform_sessions, injects them, and navigates to target URL.

stagehand_extract_cached

Extract with 10-minute cache (reduces browser calls).
{
  "type": "stagehand_extract_cached",
  "result_key": "cached_data",
  "config": {
    "sessionId": "$session.sessionId",
    "url": "https://example.com",
    "instruction": "Extract pricing table",
    "ttl_ms": 600000
  }
}

Ollama Steps (AI Generation)

ollama_generate

Generate completion (single-turn, no history).
{
  "type": "ollama_generate",
  "result_key": "caption",
  "config": {
    "model": "dolphin-mistral:7b",
    "prompt": "Write a flirty caption for this photo: $input.photo_description",
    "system": "You are an OnlyFans content strategist. Be playful and engaging.",
    "options": {
      "temperature": 0.9,
      "top_p": 0.95
    }
  }
}

ollama_chat

Multi-turn chat with message history.
{
  "type": "ollama_chat",
  "result_key": "response",
  "config": {
    "model": "qwen-2.5:latest",
    "messages": [
      {"role": "user", "content": "$input.user_message"}
    ],
    "system": "You are a helpful assistant."
  }
}

Taxonomy Steps

taxonomy_terms_read

Read taxonomy terms with caching.
{
  "type": "taxonomy_terms_read",
  "result_key": "terms",
  "config": {
    "super_concept": "aesthetic",
    "platform": "onlyfans",
    "limit": 20
  }
}

taxonomy_invalidate

Bust taxonomy cache after writes.
{
  "type": "taxonomy_invalidate",
  "result_key": "invalidated",
  "config": {}
}

Queue Steps

bullmq_enqueue

Enqueue a BullMQ job.
{
  "type": "bullmq_enqueue",
  "result_key": "job",
  "config": {
    "queue": "media-jobs",
    "job": {
      "id": "$input.job_id",
      "type": "apply_watermark",
      "media_id": "$input.media_id",
      "watermark_text": "© 2026"
    }
  }
}

Generic HTTP

http_request

Make arbitrary HTTP request.
{
  "type": "http_request",
  "result_key": "api_response",
  "config": {
    "url": "https://api.example.com/webhook",
    "method": "POST",
    "headers": {
      "Authorization": "Bearer $input.token"
    },
    "body": {
      "event": "post_created",
      "data": "$new_post"
    }
  }
}

Variable Interpolation

Steps can reference data from:
  • $input.field — Parameters from ACTION tag
  • $steps[N].field — Result from step N (0-indexed)
  • $result_key.field — Result from step with given result_key
  • $context.field — Context data (user info, workspace, timestamp)
Example flow:
{
  "slug": "post-creator",
  "name": "Create Scheduled Post",
  "steps": [
    {
      "type": "ollama_generate",
      "result_key": "caption",
      "config": {
        "model": "dolphin-mistral:7b",
        "prompt": "Write caption for: $input.description"
      }
    },
    {
      "type": "directus_write",
      "result_key": "post",
      "config": {
        "collection": "scheduled_posts",
        "item": {
          "caption": "$caption.response",
          "platform": "$input.platform",
          "scheduled_time": "$input.time"
        }
      }
    }
  ]
}

Creating a New Action Flow

  1. Design the flow — Map out steps and data flow
  2. Create in Directus — Add to action_flows collection
  3. Set slug — Unique identifier (e.g., analytics-report)
  4. Define steps — JSON array of step definitions
  5. Test execution — Trigger from AI chat: [ACTION:analytics-report:{"period":"7d"}]
  6. Check audit log — Review in agent_audits collection

Execution Timeouts

  • Per-step timeout: 120 seconds
  • Steps exceeding timeout are aborted
  • Flow execution stops on first failed step
  • Partial results available in error response

Error Handling

Failed steps return error details:
{
  "success": false,
  "slug": "post-creator",
  "error": "Step 2 (directus_write) failed: Collection not found",
  "results": {
    "caption": { "response": "Check out this new content!" }
  },
  "failedStep": 1
}
All executions logged to agent_audits with full error details.

Implementation Files

  • server/utils/actionRunner/index.js — Flow executor (server/utils/actionRunner/index.js:1)
  • server/utils/actionRunner/stepExecutors.js — Step type implementations (server/utils/actionRunner/stepExecutors.js:1)

Next Steps

Project Structure

Understand the codebase layout

Extending MCP

Add new MCP tools

Build docs developers (and LLMs) love