{
  "type": "chat",
  "label": "Chatbot",
  "category": "Communication",
  "description": "An embeddable, interactive AI chat widget with a configurable model, system prompt, design, behavior, and automated actions. Each action creates an output handle that can trigger downstream nodes. Runs on the WordPress site (local-only); publishes a shortcode.",
  "capabilities": ["interactive chat", "automated actions", "web/file search tools", "embeddable shortcode", "custom design", "always-agentic (bounded agentic tool loop with governed tools; degrades to a single reply when no capabilities are configured)"],
  "modes": ["local"],
  "credits": 3,
  "config": [
    { "name": "model", "type": "string", "required": false, "description": "Chat model id." },
    { "name": "aiSource", "type": "enum", "required": false, "default": "credits", "options": ["credits", "byok"], "description": "Per-node AI source (billing/route) for THIS chatbot's AI calls. 'credits' = keyless metered proxy on the connected account (no provider keys needed); 'byok' = the site's own provider keys from Settings (free, unlimited). This explicit choice OVERRIDES the hidden global `prefer_credits_when_connected` preference for ALL of the node's AI transports (buffered reply, streaming reply, agent tool loop, and the handoff intent classifier) with NO silent fallback in either direction: 'credits' on a disconnected/unfunded site surfaces a clear gate/402 (never BYOK); 'byok' with no key configured surfaces a clear 'add a key in Settings' notice (never credits). ABSENT (legacy nodes) = behaviour is byte-identical to before: the route follows the implicit global preference. New nodes default to 'credits' when the site is connected at creation, else 'byok' — always user-changeable in the Model tab." },
    { "name": "systemPrompt", "type": "string", "required": false, "supportsVariables": true, "description": "Initial system instructions." },
    { "name": "modelParams", "type": "object", "required": false, "description": "temperature, top_p, top_k, penalties, max_tokens." },
    { "name": "openaiTools", "type": "object", "required": false, "description": "OpenAI-only web search and file search config." },
    { "name": "actions", "type": "array", "required": false, "description": "Automated actions ({ name, description, fields[] }); each creates a connectable output handle." },
    { "name": "design", "type": "object", "required": false, "description": "Theme, position, dimensions, colors, fonts, quick responses, custom CSS." },
    { "name": "behavior", "type": "object", "required": false, "description": "Initial message, placeholder, history, streaming, citations, rate limits." },
    { "name": "agent", "type": "object", "required": false, "description": "Agentic-engine config for the chat node. There is NO agent-mode toggle — every chat node is an agent. { maxSteps, tools{<toolName>:{enabled,requiresConfirmation,rateLimit}}, handoff{enabled,provider,trigger,keywords,handoffMessage,resolvedMessage,chatwoot{...},zendesk{...},intercom{...},native{assignedRole}} }. The node runs a bounded agentic tool loop over a governed tool registry (built-ins, actions, KB search, MCP, native search, and — when handoff is on — handoff_to_human) with per-tool guardrails and an audit trail ONLY when it has a genuine capability configured (one or more `actions`, or agent-decided handoff with trigger 'agent'/'both'); with none of these it degrades to exactly one model call — a single reply, byte-identical to classic chat (same output/streaming/memory/KB injection/cost). The single prompt lives in `systemPrompt` (Instructions / Persona). Legacy `agent.persona` is migrated into `systemPrompt` and cleared; a runtime safety-net still prepends any un-migrated persona. `agent.enabled` is retired and ignored (old nodes with it absent/false still work). Human handoff (Native | Chatwoot | Zendesk Sunshine | Intercom) can also start on an explicit visitor keyword; while a human is in control the conversation mode is human and the bot only relays. Native handoffs surface in the Operator Inbox, where an operator reads the transcript, replies (relayed live), and resolves back to the bot." }
  ],
  "inputs": ["main"],
  "outputs": ["action"],
  "example": { "nodeName": "Support Bot", "model": "gpt-4o-mini", "systemPrompt": "You are a helpful support assistant.", "actions": [ { "name": "Newsletter Subscription", "description": "Collect an email to subscribe", "fields": [ { "name": "email", "type": "email", "required": true } ] } ] },
  "keywords": ["chat", "chatbot", "assistant", "conversation", "widget", "ai"],
  "contract": {
    "produces": {
      "kind": "structured",
      "cardinality": "named",
      "namedFields": true,
      "fieldsFrom": "actions",
      "reference": "[[{field}] from {id}:{actionId}]",
      "note": "Each action creates an output handle whose id is the action id. Collected action fields are referenced as [[fieldName] from {id}:{actionId}]. Every chat->downstream edge MUST set sourceHandle to the action id."
    }
  }
}
