Skip to main content

What they do

Gateway sits between your application code and the agent. It handles:
  • Session management — maps each user conversation to a stable session id. The agent sees consistent memory across turns.
  • Per-session concurrency control — concurrent messages to the same session are queued fairly so the agent never races itself.
  • Agent routing — in multi-agent setups, routes each message to the right agent.
Channel adapts a messaging platform (HTTP, WebSocket, Slack, etc.) into the Gateway’s routing model. It resolves who sent the message, which agent should handle it, and where to deliver the reply. For most use cases you don’t interact with Gateway or Channel directly — agent.channel(...) wires everything up behind the scenes.

Quick start

agent.channel(...) lazily creates an internal gateway, registers the agent, and injects the gateway into the channel. After this call, chat is ready to use.

SendOptions

SendOptions tells the channel who is talking and which conversation this belongs to:

Multimodal / structured messages

Plain-text send(String) is a convenience. For images, audio, or multi-part turns, pass a pre-built Msg (or List<Msg>) — every String overload has matching Msg / List<Msg> variants (including SendOptions and sendStream):

RuntimeContext merge

Channel turns always build a RuntimeContext inside the Gateway. Callers can contribute a caller base via SendOptions / InboundMessage.runtimeContext() / a ChannelRuntimeContextResolver. Merge order:
  1. Start from the caller context (may be empty)
  2. If a ChannelRuntimeContextResolver is configured and returns non-null, that value replaces the caller base
  3. Gateway overlays identity fields — sessionId (gw-…), userId, msgContext, gateKey, outboundAddress — which always win on conflict
Wire a resolver through GatewayBootstrap:
Or call gateway.setRuntimeContextResolver(...) after obtaining the gateway. Do not put business attributes in MsgContext.extra — that map participates in the session key.

Streaming events + SSE

sendStream() returns Flux<AgentEvent> — the same fine-grained event stream as agent.streamEvents(), but routed through the gateway with session management.

Spring Boot SSE controller

Talking to exposed subagents

When the agent spawns a subagent with expose_to_user=true, the gateway exposes that subagent as a user-addressable entry point. A SubagentExposedEvent is emitted into the sendStream() event stream carrying the subagentId.

Discovering exposed subagents

SubagentExposedEvent fields:

Sending messages to subagents

Once you have a subagentId, send messages directly to the subagent — bypassing the parent agent entirely:

SSE with subagent support

A typical SSE controller handles both main-agent and subagent messages:
The client watches for SUBAGENT_EXPOSED events to render new conversation tabs, and passes the subagentId back on subsequent requests.

Multi-agent routing

For scenarios with multiple HarnessAgent instances, use GatewayBootstrap:

Routing by agentId

Use SendOptions.withAgentId() to route a message to a specific agent:

Thread exposure with GatewayBootstrap

To enable expose_to_user on subagents, wire the gateway bridge into each agent’s subagent middleware:
With agent.channel(...), this wiring happens automatically.

Custom Channel

Implement the Channel interface to adapt a new messaging platform:
Register it with GatewayBootstrap:

Built-in channel adapters

AgentScope provides ready-to-use Channel adapters for popular messaging platforms as extension modules: See the Channel Adapters integration overview for details.
  • Subagent — declaring and spawning subagents, background tasks, streaming forwarding
  • Architecture — how parent and child agents cooperate