# 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 platform’s 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
