Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.crewform.tech/llms.txt

Use this file to discover all available pages before exploring further.

Overview

CrewForm supports the AG-UI (Agent-User Interaction) protocol, enabling real-time streaming of agent execution events to any compatible frontend via Server-Sent Events (SSE).
AG-UI complements MCP (tools) and A2A (agents) — together they form the three agentic protocols that CrewForm supports.

How It Works

When a CrewForm agent executes a task, the task runner emits structured AG-UI events through an SSE endpoint. Any frontend — CrewForm’s dashboard, a custom app, or a CopilotKit integration — can subscribe and display execution in real-time.
Agent Executor  →  AG-UI Event Bus  →  SSE Endpoint  →  Your Frontend

Event Types

EventDescription
RUN_STARTEDTask execution begins
TEXT_MESSAGE_STARTLLM response stream begins
TEXT_MESSAGE_CONTENTLLM response token chunk
TEXT_MESSAGE_ENDLLM response stream ends
TOOL_CALL_STARTTool execution begins (includes tool name)
TOOL_CALL_ARGSTool call arguments
TOOL_CALL_ENDTool execution completes (includes result)
RUN_FINISHEDTask completes successfully
RUN_ERRORTask fails with error message
INTERACTION_REQUESTAgent pauses and requests user input
INTERACTION_RESPONSEUser submitted their response
INTERACTION_TIMEOUTInteraction timed out without response

Rich Interactions

AG-UI supports bidirectional communication — agents can pause execution, present choices to the user, and resume based on user input.

Interaction Types

TypeWhen to useUser sees
approvalAgent needs permission before proceedingApprove / Reject buttons
confirm_dataAgent wants the user to verify or edit dataData table with Confirm / Edit / Reject
choiceAgent needs the user to pick from optionsRadio button list with Select

How It Works

Agent executes → INTERACTION_REQUEST → Agent pauses

                        User sees modal, makes decision

                POST /respond → INTERACTION_RESPONSE → Agent resumes

INTERACTION_REQUEST Event

When an agent requests input, the SSE stream emits:
{
  "type": "INTERACTION_REQUEST",
  "timestamp": 1711000010,
  "threadId": "task-uuid",
  "interactionId": "uuid",
  "interactionType": "approval",
  "title": "Deploy to production?",
  "description": "The agent wants to deploy version 2.4.1 to production.",
  "timeoutMs": 300000
}
For confirm_data, includes a data object. For choice, includes a choices array.

Submitting a Response

POST /ag-ui/:agentId/respond
Body:
{
  "threadId": "task-uuid",
  "interactionId": "uuid",
  "approved": true
}
For confirm_data, include data with modified values. For choice, include selectedOptionId.

React Hook Usage

The useAgentStream hook handles interactions automatically:
const { status, textContent, pendingInteraction, respond } = useAgentStream(
  'https://your-task-runner-url',
  agentId,
  taskId,
  apiKey,
  true
)

// When an interaction is pending, show the modal
if (pendingInteraction) {
  return (
    <InteractionModal
      interaction={pendingInteraction}
      onRespond={respond}
    />
  )
}

Timeout Behavior

  • Default timeout: 5 minutes per interaction
  • When the timeout expires, the agent emits INTERACTION_TIMEOUT and the task fails
  • The task status transitions: runningwaiting_for_inputfailed
  • Timeout duration is included in the INTERACTION_REQUEST event

Authentication

AG-UI uses the same Bearer token auth as A2A — provide an API key from your workspace’s api_keys table (provider: ag-ui or a2a).

Important Notes

  • The SSE stream is real-time only — connect before or during task execution
  • Events stream for the duration of task execution and close on completion
  • The threadId maps to a CrewForm task ID
  • Works with any AG-UI-compatible client, including CopilotKit
  • Rich interactions require a connected SSE client to display the modal — if no client is connected, the interaction will time out

Request

POST /ag-ui/:agentId/sse
Headers:
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
Body (RunAgentInput):
{
  "threadId": "task-uuid",
  "runId": "task-uuid"
}

Response

The endpoint returns text/event-stream with AG-UI events:
data: {"type":"RUN_STARTED","timestamp":1711000000,"threadId":"abc","runId":"abc"}

data: {"type":"TEXT_MESSAGE_START","timestamp":1711000001,"messageId":"msg_1","role":"assistant"}

data: {"type":"TEXT_MESSAGE_CONTENT","timestamp":1711000002,"messageId":"msg_1","delta":"Here is "}

data: {"type":"TEXT_MESSAGE_CONTENT","timestamp":1711000003,"messageId":"msg_1","delta":"the result..."}

data: {"type":"TEXT_MESSAGE_END","timestamp":1711000004,"messageId":"msg_1"}

data: {"type":"RUN_FINISHED","timestamp":1711000005,"threadId":"abc","runId":"abc","result":"Here is the result..."}

Quick Test

Health Check

curl http://localhost:3001/ag-ui/health
# → {"status":"ok","protocol":"ag-ui","version":"1.1"}

Stream Events

# In terminal 1: Connect to SSE stream
curl -N -X POST http://localhost:3001/ag-ui/AGENT_ID/sse \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"threadId":"TASK_ID","runId":"TASK_ID"}'

# In terminal 2: Trigger the task via API or dashboard

React Hook

CrewForm includes a useAgentStream React hook for consuming AG-UI events:
import { useAgentStream } from '@/hooks/useAgentStream'

function TaskStreamView({ taskId, agentId, apiKey }) {
  const { status, textContent, toolCalls, error } = useAgentStream(
    'https://your-task-runner-url',
    agentId,
    taskId,
    apiKey,
    true // enabled
  )

  return (
    <div>
      <p>Status: {status}</p>
      <pre>{textContent}</pre>
      {toolCalls.map(tc => (
        <div key={tc.id}>
          🔧 {tc.name}: {tc.status === 'done' ? tc.result : 'running...'}
        </div>
      ))}
    </div>
  )
}

Hook Return Values

FieldTypeDescription
status'idle' | 'connecting' | 'streaming' | 'completed' | 'error'Connection state
textContentstringAccumulated LLM response text
toolCallsAgUiToolCall[]Tool calls with name, args, result, status
pendingInteractionAgUiInteractionRequest | nullCurrent interaction awaiting user response
eventsAgUiEvent[]All raw AG-UI events received
errorstring | nullError message if status is 'error'
respond(response) => Promise<void>Submit a response to a pending interaction