docs: add matrix per-chat context design
This commit is contained in:
parent
430c82dba1
commit
9bb93fbbda
1 changed files with 278 additions and 0 deletions
|
|
@ -0,0 +1,278 @@
|
|||
# Matrix Per-Chat Context Design
|
||||
|
||||
## Goal
|
||||
|
||||
Move the Matrix surface from the current shared-agent-context MVP to true per-chat agent contexts using the new platform `chat_id`, while preserving the existing Matrix UX model built around rooms, spaces, and local chat labels such as `C1`, `C2`, and `C3`.
|
||||
|
||||
## Core Decision
|
||||
|
||||
The Matrix surface remains the owner of user-facing chat organization.
|
||||
|
||||
- Matrix rooms, spaces, chat names, and archive state remain surface concerns.
|
||||
- The platform agent becomes the owner of actual conversation context.
|
||||
- The integration layer stores an explicit mapping from each surface chat to one platform context.
|
||||
|
||||
This is the selected "Variant A" architecture:
|
||||
|
||||
`surface_chat -> platform_chat_id`
|
||||
|
||||
## Why This Decision
|
||||
|
||||
The current Matrix adapter already has a stable UX model:
|
||||
|
||||
- a user has a space
|
||||
- each working room has a local chat id like `C1`
|
||||
- commands such as `!new`, `!chats`, `!rename`, and `!archive` operate on that model
|
||||
|
||||
Replacing that entire model with platform-native chat ids would be a broader refactor than needed. The surface and the platform solve different problems:
|
||||
|
||||
- the surface organizes rooms and commands for users
|
||||
- the platform persists and branches real conversation context
|
||||
|
||||
Keeping those responsibilities separate lets us add true per-chat context now without rebuilding the Matrix adapter around a new identity model.
|
||||
|
||||
## Scope
|
||||
|
||||
This design covers:
|
||||
|
||||
- true per-chat context for Matrix rooms
|
||||
- a new `!branch` command
|
||||
- real context-aware semantics for `!new`, `!context`, `!save`, and `!load`
|
||||
- lazy migration of legacy Matrix rooms created before platform `chat_id` support
|
||||
|
||||
This design does not cover:
|
||||
|
||||
- end-to-end Matrix encryption support
|
||||
- Telegram changes
|
||||
- platform UI for browsing contexts
|
||||
- a future unified cross-surface chat browser
|
||||
|
||||
## Data Model
|
||||
|
||||
### Surface chat identity
|
||||
|
||||
The Matrix surface keeps its existing identifiers:
|
||||
|
||||
- Matrix room id, for example `!room:example.org`
|
||||
- local chat id, for example `C2`
|
||||
- room name
|
||||
- archive status
|
||||
- owning space id
|
||||
|
||||
These remain the source of truth for Matrix UX.
|
||||
|
||||
### Platform context identity
|
||||
|
||||
Each working Matrix room gets a `platform_chat_id` stored in its room metadata.
|
||||
|
||||
Example `room_meta` shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"chat_id": "C2",
|
||||
"space_id": "!space:example.org",
|
||||
"name": "Research",
|
||||
"platform_chat_id": "chat_8f2c..."
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- one working Matrix room maps to exactly one current platform context
|
||||
- two Matrix rooms must not share the same `platform_chat_id` unless we intentionally implement that later
|
||||
- branching creates a new `platform_chat_id`, never reuses the old one
|
||||
|
||||
## Runtime Semantics
|
||||
|
||||
### Normal message flow
|
||||
|
||||
1. A Matrix message arrives in a working room.
|
||||
2. The Matrix adapter resolves the room to local `room_meta`.
|
||||
3. The integration layer reads `platform_chat_id` from that metadata.
|
||||
4. `RealPlatformClient.send_message(...)` sends the message to the platform using that `platform_chat_id`.
|
||||
5. The platform appends the exchange to that specific context and returns the reply.
|
||||
6. The Matrix adapter sends the reply back to the room.
|
||||
|
||||
The key change is that the agent no longer treats all Matrix rooms as one shared context.
|
||||
|
||||
### `!new`
|
||||
|
||||
`!new` creates a new user-facing chat and a new empty platform context at the same time.
|
||||
|
||||
Flow:
|
||||
|
||||
1. Create a new Matrix room in the user space.
|
||||
2. Ask the platform to create a new blank context and return its `platform_chat_id`.
|
||||
3. Store that `platform_chat_id` in the new room metadata.
|
||||
4. Invite the user into the room.
|
||||
|
||||
Result:
|
||||
|
||||
- the new room is immediately independent
|
||||
- sending the first message does not share memory with the previous room
|
||||
|
||||
### `!branch`
|
||||
|
||||
`!branch` creates a new room whose starting point is a snapshot of the current room context.
|
||||
|
||||
Flow:
|
||||
|
||||
1. Resolve the current room's `platform_chat_id`.
|
||||
2. Ask the platform to create a new context branched from that source.
|
||||
3. Create a new Matrix room.
|
||||
4. Store the new `platform_chat_id` in the new room metadata.
|
||||
5. Invite the user into the new room.
|
||||
|
||||
Result:
|
||||
|
||||
- the new room starts with the current history and state
|
||||
- later messages diverge independently
|
||||
|
||||
### `!save`
|
||||
|
||||
`!save [name]` saves a snapshot of the current room's platform context under the current user.
|
||||
|
||||
Semantics:
|
||||
|
||||
- saves are owned by the user, not by the room
|
||||
- the saved snapshot originates from the current `platform_chat_id`
|
||||
|
||||
### `!load`
|
||||
|
||||
`!load` shows the user's saved contexts and loads the chosen snapshot into the current room's platform context.
|
||||
|
||||
Semantics:
|
||||
|
||||
- a saved context created in one room can be loaded into any other room owned by the same user
|
||||
- loading does not replace the Matrix room identity
|
||||
- loading affects only the current room's mapped `platform_chat_id`
|
||||
|
||||
### `!context`
|
||||
|
||||
`!context` reports the state of the current room context, not a global user session.
|
||||
|
||||
Minimum expected output:
|
||||
|
||||
- current room name or local chat id
|
||||
- current `platform_chat_id` presence or status
|
||||
- what saved context, if any, was last loaded here
|
||||
- last token usage if the platform still returns it
|
||||
|
||||
## Legacy Room Migration
|
||||
|
||||
Existing Matrix rooms were created before real platform `chat_id` support and therefore may not have `platform_chat_id` in their metadata.
|
||||
|
||||
We need a non-destructive migration.
|
||||
|
||||
### Lazy migration strategy
|
||||
|
||||
For a room without `platform_chat_id`:
|
||||
|
||||
1. On the first operation that requires platform context, detect the missing mapping.
|
||||
2. Create a new blank platform context for that room.
|
||||
3. Persist the new `platform_chat_id` into room metadata.
|
||||
4. Continue the requested operation normally.
|
||||
|
||||
This applies to:
|
||||
|
||||
- first normal message
|
||||
- `!context`
|
||||
- `!save`
|
||||
- `!load`
|
||||
- `!branch`
|
||||
|
||||
This avoids forcing users to recreate their rooms manually.
|
||||
|
||||
## Interface Changes
|
||||
|
||||
### Matrix metadata
|
||||
|
||||
Extend Matrix `room_meta` helpers to read and write `platform_chat_id`.
|
||||
|
||||
### Real platform client
|
||||
|
||||
`RealPlatformClient` must stop treating the current `chat_id` parameter as a purely local label when talking to the platform. The surface-facing call can still receive the local `chat_id`, but platform calls must use the resolved `platform_chat_id`.
|
||||
|
||||
Recommended integration direction:
|
||||
|
||||
- Matrix resolves the room mapping before calling the platform
|
||||
- `RealPlatformClient` receives the platform context id it should use
|
||||
|
||||
This keeps the platform client simple and avoids giving it Matrix-specific storage responsibilities.
|
||||
|
||||
### Agent API wrapper
|
||||
|
||||
The wrapper must support platform calls that are explicitly context-aware:
|
||||
|
||||
- create new context
|
||||
- branch context
|
||||
- send message into a specific context
|
||||
- save current context
|
||||
- load saved context into a specific context
|
||||
|
||||
If upstream naming differs, the adapter layer should normalize those operations into stable local methods.
|
||||
|
||||
## Command Semantics in MVP
|
||||
|
||||
The MVP command set should evolve to this:
|
||||
|
||||
- `!new` creates a new room with a new empty platform context
|
||||
- `!branch` creates a new room with a branched platform context
|
||||
- `!context` reports the current room context
|
||||
- `!save` saves the current room context for the user
|
||||
- `!load` loads one of the user's saved contexts into the current room
|
||||
|
||||
Commands that do not have reliable backend support should remain hidden or explicitly marked unavailable.
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Missing mapping
|
||||
|
||||
If `platform_chat_id` is missing:
|
||||
|
||||
- try lazy migration first
|
||||
- only return an error if migration fails
|
||||
|
||||
### Platform create or branch failure
|
||||
|
||||
If the platform cannot create or branch a context:
|
||||
|
||||
- do not create partially-initialized room metadata
|
||||
- return a user-facing error in the source room
|
||||
- log enough detail to diagnose the backend failure
|
||||
|
||||
### Save and load failure
|
||||
|
||||
The surface must not claim success before the platform confirms success.
|
||||
|
||||
For MVP quality:
|
||||
|
||||
- user-facing text should say "request sent" only when confirmation is not available
|
||||
- once platform confirmation exists, switch to real success or failure messages
|
||||
|
||||
## Testing
|
||||
|
||||
Add or update tests for:
|
||||
|
||||
- a new room gets a new `platform_chat_id`
|
||||
- two rooms created with `!new` do not share context ids
|
||||
- `!branch` creates a new room with a different `platform_chat_id` derived from the current one
|
||||
- sending messages from two rooms uses different platform context ids
|
||||
- saved contexts remain user-visible across rooms
|
||||
- loading the same saved context into two different rooms affects those rooms independently afterward
|
||||
- a legacy room without `platform_chat_id` lazily receives one on first use
|
||||
- failures during create, branch, save, and load do not leave broken metadata behind
|
||||
|
||||
## Migration Path
|
||||
|
||||
This design preserves a clean future direction:
|
||||
|
||||
- Matrix continues to own its UX model
|
||||
- Telegram can adopt the same `surface_chat -> platform_chat_id` mapping later
|
||||
- when the platform matures further, more of the save/load/branch logic can move from prompts or local workarounds into real platform APIs
|
||||
|
||||
The key long-term boundary stays stable:
|
||||
|
||||
- surfaces own presentation and routing
|
||||
- the platform owns context
|
||||
- the integration layer owns the mapping
|
||||
Loading…
Add table
Add a link
Reference in a new issue