docs: add matrix direct-agent prototype design

This commit is contained in:
Mikhail Putilovskij 2026-04-08 00:22:20 +03:00
parent b08a5e3d96
commit 1fdb5bf303

View file

@ -0,0 +1,243 @@
# Matrix Direct-Agent Prototype Design
## Goal
Ship a working Matrix prototype that talks to the real Lambda agent instead of `MockPlatformClient`, while preserving the current Matrix adapter logic and keeping the code expandable toward future platform versions.
## Scope
This design is for a Matrix-only prototype delivered from this repository on a dedicated branch. It is not a `main` branch rollout and it is not a separate prototype repo.
The design assumes a minimal companion fork of `platform/agent` may be used, but changes there must stay as small as possible.
## Constraints
- Preserve the current Matrix transport logic as much as possible.
- Keep `core/` unaware of platform immaturity.
- Avoid broad changes to platform repos.
- Prefer one narrow patch to `platform/agent` over changes to both `platform/agent` and `platform/agent_api`.
- Keep the backend boundary reusable for future Telegram or other surfaces.
- Do not pretend unsupported platform capabilities are real.
## Live Platform Findings
Based on the live repo analysis performed on April 7, 2026:
- `platform/master` is not yet a usable consumer-facing backend for surfaces.
- `platform/agent` exposes a working WebSocket endpoint for prompt/response exchange.
- `platform/agent_api` documents and implements text-oriented WebSocket messaging, but the bot does not need to depend on that package directly.
- `platform/agent` currently hardcodes a single shared backend memory thread via `thread_id = "default"`, which would cause all chats to share context.
## Architecture
The prototype remains in this repo and introduces a new real backend path behind the existing SDK boundary.
### New files
- `sdk/real.py`
- Exports `RealPlatformClient`
- Implements the existing `PlatformClient` contract from `sdk/interface.py`
- Composes the lower-level prototype pieces
- `sdk/agent_session.py`
- Owns direct WebSocket communication with the real agent
- Manages connection lifecycle, request/response handling, and thread identity
- `sdk/prototype_state.py`
- Owns local prototype-only state
- Stores user mapping, local settings, and lightweight metadata needed until a real control plane exists
### Responsibility split
- Matrix adapter remains transport-specific only.
- `core/` continues to depend only on `PlatformClient`.
- `RealPlatformClient` acts as the anti-corruption layer between the current bot contract and the platforms incomplete shape.
- Local control-plane behavior remains explicit and replaceable later.
## Message and Identity Model
Each Matrix chat gets a stable backend session identity.
### Surface identity
- Surface: `matrix`
- Surface user id: Matrix MXID, for example `@alice:example.org`
- Surface chat id: logical chat id from `ChatManager`, for example `C1`
- Surface ref: Matrix room id
### Backend thread identity
Use a deterministic thread key:
`matrix:{matrix_user_id}:{chat_id}`
Example:
`matrix:@alice:example.org:C1`
### Mapping rules
- One Matrix logical chat maps to one backend memory thread.
- `!new` creates a fresh logical chat and therefore a fresh backend thread.
- `!rename` only changes display metadata and does not change backend identity.
- `!archive` stops active use of the thread in the surface, but does not need to delete backend memory in v1.
## Runtime Flow
### Normal message flow
1. Matrix event arrives in an existing room.
2. Existing Matrix routing resolves room to logical `chat_id`.
3. `core/handlers/message.py` calls `platform.send_message(...)`.
4. `RealPlatformClient` derives the backend thread key from `(platform, user_id, chat_id)`.
5. `AgentSessionClient` sends the prompt to the agent WebSocket using that thread key.
6. The reply is converted into the existing `MessageResponse` or `MessageChunk` contract.
7. Matrix sends the final text back to the room.
### Settings flow
For v1, settings remain local:
- `get_settings()` reads from local prototype state
- `update_settings()` writes to local prototype state
This is intentional. The prototype must not claim settings are backed by the real platform when no such platform API exists yet.
## Feature Matrix
### Real in v1
- `!start`
- Plain text messaging with the real agent
- Matrix chat lifecycle already implemented in this repo:
- `!new`
- `!chats`
- `!rename`
- `!archive`
- Per-chat conversation memory, provided the agent accepts dynamic thread identity
### Local in v1
- `!settings`
- `!skills`
- `!soul`
- `!safety`
- `!status`
- user registration and local user mapping
### Deferred
- Attachments and file upload to the agent
- Voice input to the agent
- Image input to the agent
- Long-running task callbacks and webhook-style async completion
- Real control-plane integration through `platform/master`
## Minimal Upstream Change
To avoid shared memory across all conversations, make one narrow change in the forked `platform/agent` repo:
- stop hardcoding `thread_id = "default"`
- derive thread identity from WebSocket connection context
### Preferred mechanism
Read `thread_id` from WebSocket query parameters rather than changing the message payload format.
Example:
`ws://host:port/agent_ws/?thread_id=matrix:@alice:example.org:C1`
This is preferred because:
- it limits the platform patch to one repo
- it avoids changing both server and SDK protocol shape
- it keeps the client message body text-only
- it makes session identity explicit and easy to reason about
## Why Not Use `platform/agent_api` Directly
The bot should not depend on their client package for the prototype.
Reasons:
- the bot already has its own internal integration boundary in `sdk/interface.py`
- a tiny local WebSocket client is enough for this protocol
- avoiding a dependency on `platform/agent_api` keeps rebasing simpler
- if upstream stabilizes later, the bot can adopt their SDK without affecting Matrix handlers
## Repo Strategy
### This repo
Owns:
- Matrix surface logic
- SDK compatibility layer
- local prototype state
- backend selection and wiring
### Forked `platform/agent`
Owns only:
- minimal thread identity patch required for per-chat memory
### Explicitly not doing
- no separate prototype repo
- no changes to `platform/master` for v1
- no unnecessary changes to `platform/agent_api`
## Migration Path
This design is intentionally expandable.
When the platform develops further:
- `sdk/prototype_state.py` can be replaced or reduced by a real `MasterClient`
- `sdk/agent_session.py` can remain the direct session transport if still relevant
- `RealPlatformClient` can continue to present the stable bot-facing interface
- Telegram or another surface can reuse the same backend components without rethinking the integration model
## Risks
### Risk: hidden platform assumptions leak upward
Mitigation:
- keep all direct-agent logic below `RealPlatformClient`
- avoid changing `core/` contracts for prototype convenience
### Risk: settings semantics drift from future platform reality
Mitigation:
- make local settings behavior explicit in code and docs
- keep settings isolated in `sdk/prototype_state.py`
### Risk: upstream `agent` fork diverges
Mitigation:
- keep the patch minimal and narrowly scoped to thread identity
### Risk: thread identity source is unstable
Mitigation:
- derive thread key from existing stable bot-side identities: platform, surface user id, logical chat id
## Testing Strategy
- Unit tests for `sdk/agent_session.py` request/response behavior
- Unit tests for `sdk/prototype_state.py` local settings and user mapping
- Unit tests for `sdk/real.py` contract compliance with `PlatformClient`
- Matrix integration tests confirming:
- existing commands still work
- different logical chats map to different backend thread keys
- rename does not change thread identity
- archive stops reuse from the surface perspective
## Success Criteria
- Matrix can talk to the real agent without rewriting the Matrix adapter architecture
- Chats do not share backend memory accidentally
- Unsupported platform capabilities remain local or deferred rather than being faked as “real”
- The backend boundary remains suitable for later Telegram or other surfaces