docs(architecture): explain gateway session mapping and per-session queueing
This commit is contained in:
+1
-1
@@ -6,6 +6,7 @@ This documentation is written to be useful to both humans and AI agents. If you
|
|||||||
|
|
||||||
1. Architecture overview (agent-oriented)
|
1. Architecture overview (agent-oriented)
|
||||||
- `docs/architecture/AGENT_DIAGRAM.md`
|
- `docs/architecture/AGENT_DIAGRAM.md`
|
||||||
|
- `docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md`
|
||||||
- `docs/architecture/TYPESCRIPT_MAP.md`
|
- `docs/architecture/TYPESCRIPT_MAP.md`
|
||||||
- `docs/architecture/SYMBOL_INDEX.md`
|
- `docs/architecture/SYMBOL_INDEX.md`
|
||||||
- `docs/architecture/CONTRIBUTOR_MAP.md`
|
- `docs/architecture/CONTRIBUTOR_MAP.md`
|
||||||
@@ -37,4 +38,3 @@ flowchart TD
|
|||||||
NA --> TP[ToolPolicy + ToolRegistry]
|
NA --> TP[ToolPolicy + ToolRegistry]
|
||||||
TP --> TE[ToolExecutor\nhooks + enforcement + audit]
|
TP --> TE[ToolExecutor\nhooks + enforcement + audit]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Gateway Sessions and Queueing (Agent Execution Model)
|
||||||
|
|
||||||
|
This document explains how the gateway maps WebSocket clients onto durable sessions, and how work is serialised per session so agent execution stays coherent under concurrent requests.
|
||||||
|
|
||||||
|
If you only want the protocol surface, see `docs/api/PROTOCOL.md`.
|
||||||
|
|
||||||
|
## Key Ideas
|
||||||
|
|
||||||
|
- A WebSocket client gets a `connectionId`.
|
||||||
|
- Each connection is attached to a `sessionId`.
|
||||||
|
- Agent work is queued per `sessionId` (FIFO), not per connection.
|
||||||
|
- Sessions persist in SQLite via `SessionManager` even if clients disconnect.
|
||||||
|
|
||||||
|
## Component Map
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph GW[Gateway Process]
|
||||||
|
WS[WebSocket connection\n(connectionId)]
|
||||||
|
GS[GatewayServer]
|
||||||
|
LQ[LaneQueue\nper-session FIFO]
|
||||||
|
SB[SessionBridge\nconnectionId -> sessionId -> AgentOrchestrator]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph CORE[Flynn Core]
|
||||||
|
SM[SessionManager\nin-memory cache + SQLite]
|
||||||
|
SS[SessionStore\nSQLite tables]
|
||||||
|
AO[AgentOrchestrator]
|
||||||
|
end
|
||||||
|
|
||||||
|
WS --> GS
|
||||||
|
GS --> LQ
|
||||||
|
GS --> SB
|
||||||
|
|
||||||
|
SB --> AO
|
||||||
|
SB --> SM
|
||||||
|
SM --> SS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session IDs (What Actually Gets Stored)
|
||||||
|
|
||||||
|
The durable session ID stored by `SessionManager` is:
|
||||||
|
|
||||||
|
`<frontend>:<userId>`
|
||||||
|
|
||||||
|
For the gateway:
|
||||||
|
|
||||||
|
- `SessionBridge.connect()` assigns a `connectionId` (UUID).
|
||||||
|
- It defaults the connection's `sessionId` to `ws:<connectionId>`.
|
||||||
|
- It then calls `SessionManager.getSession('ws', sessionId)`.
|
||||||
|
|
||||||
|
That means gateway sessions are stored as:
|
||||||
|
|
||||||
|
- `ws:ws:<connectionId>`
|
||||||
|
|
||||||
|
This is expected: the gateway adds its own namespace, and the session manager namespaces again by frontend.
|
||||||
|
|
||||||
|
Key files:
|
||||||
|
|
||||||
|
- `src/gateway/session-bridge.ts`
|
||||||
|
- `src/session/manager.ts`
|
||||||
|
|
||||||
|
## Per-Session FIFO Queueing (LaneQueue)
|
||||||
|
|
||||||
|
`agent.send` uses a lane ID derived from the session:
|
||||||
|
|
||||||
|
- lane = `SessionBridge.getSessionId(connectionId)` (preferred)
|
||||||
|
- fallback lane = `connectionId` (only if session lookup fails)
|
||||||
|
|
||||||
|
Within a lane:
|
||||||
|
|
||||||
|
- Only one request executes at a time.
|
||||||
|
- Later requests queue (FIFO) and start after the active request finishes.
|
||||||
|
|
||||||
|
Across lanes:
|
||||||
|
|
||||||
|
- Independent sessions run in parallel.
|
||||||
|
|
||||||
|
Key files:
|
||||||
|
|
||||||
|
- `src/gateway/lane-queue.ts`
|
||||||
|
- `src/gateway/handlers/agent.ts`
|
||||||
|
|
||||||
|
## Cancellation Semantics
|
||||||
|
|
||||||
|
`agent.cancel` performs two separate actions:
|
||||||
|
|
||||||
|
1. Cancels any queued (not-yet-started) work in the lane (`LaneQueue.cancel(laneId)`).
|
||||||
|
2. Requests cancellation of the active agent operation (`AgentOrchestrator.cancel()` via `SessionBridge.cancel()`).
|
||||||
|
|
||||||
|
Important:
|
||||||
|
|
||||||
|
- Cancellation is best-effort for the currently running work: it stops at the next safe point in the agent loop.
|
||||||
|
- Queued work is deterministically rejected.
|
||||||
|
|
||||||
|
Key files:
|
||||||
|
|
||||||
|
- `src/gateway/handlers/agent.ts`
|
||||||
|
- `src/backends/native/orchestrator.ts`
|
||||||
|
|
||||||
@@ -9,6 +9,11 @@
|
|||||||
"date": "2026-02-15",
|
"date": "2026-02-15",
|
||||||
"summary": "Docs gap pass: added docs/README.md as a start-here index and expanded docs/api/PROTOCOL.md with a precise Mermaid diagram explaining per-session lane queueing and cancellation semantics for agent.send/agent.cancel."
|
"summary": "Docs gap pass: added docs/README.md as a start-here index and expanded docs/api/PROTOCOL.md with a precise Mermaid diagram explaining per-session lane queueing and cancellation semantics for agent.send/agent.cancel."
|
||||||
},
|
},
|
||||||
|
"docs-gateway-sessions-and-queue": {
|
||||||
|
"status": "completed",
|
||||||
|
"date": "2026-02-15",
|
||||||
|
"summary": "Added docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md to document how gateway connectionIds map to durable sessionIds, how per-session FIFO lane queueing works, and how agent.cancel behaves."
|
||||||
|
},
|
||||||
"openclaw-gap-roadmap": {
|
"openclaw-gap-roadmap": {
|
||||||
"file": "2026-02-15-openclaw-gap-roadmap.md",
|
"file": "2026-02-15-openclaw-gap-roadmap.md",
|
||||||
"status": "planned",
|
"status": "planned",
|
||||||
|
|||||||
Reference in New Issue
Block a user