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.
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-textsend(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 aRuntimeContext inside the Gateway. Callers can contribute a caller base via SendOptions / InboundMessage.runtimeContext() / a ChannelRuntimeContextResolver. Merge order:
- Start from the caller context (may be empty)
- If a
ChannelRuntimeContextResolveris configured and returns non-null, that value replaces the caller base - Gateway overlays identity fields —
sessionId(gw-…),userId,msgContext,gateKey,outboundAddress— which always win on conflict
GatewayBootstrap:
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 withexpose_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 asubagentId, 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:SUBAGENT_EXPOSED events to render new conversation tabs, and passes the subagentId back on subsequent requests.
Multi-agent routing
For scenarios with multipleHarnessAgent instances, use GatewayBootstrap:
Routing by agentId
UseSendOptions.withAgentId() to route a message to a specific agent:
Thread exposure with GatewayBootstrap
To enableexpose_to_user on subagents, wire the gateway bridge into each agent’s subagent middleware:
agent.channel(...), this wiring happens automatically.
Custom Channel
Implement theChannel interface to adapt a new messaging platform:
GatewayBootstrap:
Built-in channel adapters
AgentScope provides ready-to-use Channel adapters for popular messaging platforms as extension modules:- DingTalk — Stream protocol (persistent WebSocket)
- Feishu / Lark — Event subscription callback
- GitHub — Issue / PR comment webhook
- GitLab — Note hook
- WeCom — Encrypted callback
Related pages
- Subagent — declaring and spawning subagents, background tasks, streaming forwarding
- Architecture — how parent and child agents cooperate