Skip to content

How a request flows

Every entry point reduces to the same four seams around one dispatcher, in the same order, handled by the same code.

The reply travels the same path back, through the dispatcher to the channel that asked.

What each seam refuses to know

The seams are Channel, Provider, Executor and Agent. The dispatcher sits between them as the core: directives, the six config layers, authorization, history, the agent loop. It never imports a platform SDK; the tree is checked for that.

SeamInterfaceImplementationsKnows nothing about
ChannelChannelIO + IncomingMessage (src/core/types.ts)Slack (Socket Mode), the CLI's ask, HTTP ingress, MCPagents, models, where tools run
ProviderProvider (src/providers/types.ts)Anthropic; OpenAI-compatible (OpenAI, Groq, Ollama, vLLM)Slack, authorization, where tools run
ExecutorExecutor (src/execution/executor.ts)the bot host; an E2B or Cloudflare sandbox per thread; a residentwhich agent, model or channel asked
AgentAgentDef data (src/agents/registry.ts)general, coding, review, ship, researchthe channel, the executor

Every seam has two or more implementations; the second proves the interface (decision 0001). Only the dispatcher starts a run (decision 0002). Identifiers are platform-namespaced (slack:C…, slack:U…, slack:C…:<ts>) so scopes, grants and memory key on them.

One request, end to end

The same sequence runs from the CLI, or on a local backend.

Every step is measured

The request is one span tree: a root when the process sees the message, a child for every awaited step (thread read, workspace attach, each model turn and tool call, the reply). The status card, the run timeline and the friction report read the same spans (decision 0020; tracing spec).

One run per thread: replying while it works

A thread has one workspace, so it runs one agent at a time. A reply mid-run does not start a second run:

  • Every agent takes the reply as a follow-up, read at its next step; the run page stays one run.
  • Asking for a different agent mid-run is the one refusal. agent:review … during a coding run is told so, and waits or moves to a new thread.
  • A follow-up is never lost. If the run ends first it runs as its own turn; after an operator stop you are told it was not run.

Operator commands (help, runs stop …, config …) are not runs and always answer inline. Why one live run rather than a queue: decision 0011.

Why this shape

A pipeline per integration drifts: fix an authorization bug in one, forget the other three. A dispatcher fix is fixed everywhere, because nowhere else can hold the logic. Operator commands follow the same rule: One definition, every surface.