SanitizingHttpAgent
An HttpAgent that tolerates the AG-UI event streams real agent backends emit — use it to connect the bot to a remote runtime over AGENT_URL.
Overview
SanitizingHttpAgent is an HttpAgent (from @ag-ui/client) that tolerates the event streams real agent backends emit. The stock HttpAgent re-validates every streamed event against a strict schema, and a single rejected event aborts the entire run — some backends (notably LangGraph) legitimately emit events that fail it, e.g. a TOOL_CALL_START with a null parentMessageId, which is exactly the shape that carries interrupts. This class parses the same SSE stream but coerces the known nullable-string fields instead, so interrupts and human-in-the-loop work over HTTP.
This is the agent class to use when the bot connects to a remote AG-UI backend — the bot-process/runtime-process split shown in the Slack quickstart and used by the triage example.
Signature
import { SanitizingHttpAgent } from "@copilotkit/bot-slack";
class SanitizingHttpAgent extends HttpAgent {
constructor(config: HttpAgentConfig); // { url, headers?, … }
}Usage
const bot = createBot({
adapters: [slack({ botToken, appToken })],
agent: (threadId) => {
const agent = new SanitizingHttpAgent({
url: process.env.AGENT_URL!, // e.g. http://localhost:8200/api/copilotkit/agent/assistant/run
headers: process.env.AGENT_AUTH_HEADER
? { Authorization: process.env.AGENT_AUTH_HEADER }
: undefined,
});
agent.threadId = threadId;
return agent;
},
});Behavior
- Trusted-runtime tradeoff — the coercion replaces the stock transform's entire strict re-validation step, not just the offending field. That's acceptable only because the bot talks to a runtime you control; transport and HTTP errors still throw.
- Visible in production — the first coercion logs a one-time breadcrumb so the workaround's use is observable.
Related
- createBot — the
agentfactory option - slack() — the adapter this ships with
- Slack quickstart — the split-process setup this enables