mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-25 20:15:37 +02:00
docs: update README and system design with session management
- Update architecture diagram with SessionStore - Add session management section to README - Document IPC API for sessions - Update system design with implemented SessionStore - Resolve open questions (Q1, Q2, Q4, Q5) - Add Q6 for future agent state restoration discussion - Fix agent repository links Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
57
README.md
57
README.md
@@ -8,9 +8,9 @@ Multica uses the [Agent Client Protocol (ACP)](https://github.com/anthropics/age
|
||||
|
||||
| Agent | Command | Notes |
|
||||
|-------|---------|-------|
|
||||
| [OpenCode](https://github.com/anthropics/opencode) | `opencode acp` | |
|
||||
| [OpenCode](https://github.com/anomalyco/opencode) | `opencode acp` | |
|
||||
| [Codex CLI (ACP)](https://github.com/zed-industries/codex-acp) | `codex-acp` | Community ACP wrapper |
|
||||
| [Gemini CLI](https://github.com/anthropics/gemini-cli) | `gemini acp` | |
|
||||
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini acp` | |
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -82,11 +82,54 @@ pnpm build:linux
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Multica
|
||||
└── Conductor (orchestrates agent communication)
|
||||
└── ClientSideConnection (ACP SDK)
|
||||
└── AgentProcess (subprocess management)
|
||||
└── opencode/codex-acp/gemini (stdio)
|
||||
Multica (Electron)
|
||||
├── Renderer Process (React)
|
||||
│ └── UI Components (Chat, Settings, etc.)
|
||||
│
|
||||
├── Main Process
|
||||
│ ├── Conductor (orchestrates agent communication)
|
||||
│ │ ├── SessionStore (session persistence)
|
||||
│ │ └── ClientSideConnection (ACP SDK)
|
||||
│ │ └── AgentProcess (subprocess management)
|
||||
│ │ └── opencode/codex-acp/gemini (stdio)
|
||||
│ │
|
||||
│ └── IPC Handlers (session, agent, config)
|
||||
│
|
||||
└── Preload (contextBridge)
|
||||
└── electronAPI (exposed to renderer)
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
Multica maintains its own session layer on top of ACP:
|
||||
|
||||
```
|
||||
~/.multica/sessions/
|
||||
├── index.json # Session list (fast load)
|
||||
└── data/
|
||||
└── {session-id}.json # Full session data + updates
|
||||
```
|
||||
|
||||
**Key design decisions:**
|
||||
- **Client-side storage**: Multica stores raw `session/update` data for UI display
|
||||
- **Agent-agnostic**: Each agent manages its own internal state separately
|
||||
- **Resume behavior**: Creates new ACP session, displays stored history in UI
|
||||
|
||||
### IPC API
|
||||
|
||||
```typescript
|
||||
// Session management
|
||||
electronAPI.createSession(cwd)
|
||||
electronAPI.listSessions(options?)
|
||||
electronAPI.getSession(id)
|
||||
electronAPI.resumeSession(id)
|
||||
electronAPI.deleteSession(id)
|
||||
|
||||
// Agent control
|
||||
electronAPI.startAgent(agentId)
|
||||
electronAPI.stopAgent()
|
||||
electronAPI.sendPrompt(sessionId, content)
|
||||
electronAPI.cancelRequest(sessionId)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -352,58 +352,66 @@ class Conductor {
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.2.2 Session Manager
|
||||
#### 5.2.2 Session Store (Implemented)
|
||||
|
||||
**Responsibility**: Track and manage multiple conversation sessions.
|
||||
**Responsibility**: Persist and manage session data with conversation history.
|
||||
|
||||
**Storage Structure**:
|
||||
```
|
||||
~/.multica/sessions/
|
||||
├── index.json # Session list index (fast load)
|
||||
└── data/
|
||||
└── {session-id}.json # Complete session data + updates
|
||||
```
|
||||
|
||||
**Key Design Decisions**:
|
||||
- **Client-side storage**: Multica stores raw ACP `session/update` data
|
||||
- **Agent-agnostic**: Each agent (opencode, codex, gemini) manages its own internal state
|
||||
- **Resume behavior**: Creates new ACP session, displays stored history in UI only
|
||||
|
||||
```typescript
|
||||
// src/main/conductor/SessionManager.ts
|
||||
// src/main/session/SessionStore.ts
|
||||
|
||||
interface SessionState {
|
||||
id: string;
|
||||
interface MulticaSession {
|
||||
id: string; // Multica-generated UUID
|
||||
agentSessionId: string; // Agent-returned session ID
|
||||
agentId: string; // Agent used (opencode/codex/gemini)
|
||||
workingDirectory: string;
|
||||
createdAt: Date;
|
||||
lastActiveAt: Date;
|
||||
createdAt: string; // ISO 8601
|
||||
updatedAt: string;
|
||||
status: 'active' | 'completed' | 'error';
|
||||
title?: string;
|
||||
messageCount: number;
|
||||
}
|
||||
|
||||
class SessionManager {
|
||||
private sessions: Map<string, SessionState> = new Map();
|
||||
interface StoredSessionUpdate {
|
||||
timestamp: string;
|
||||
update: SessionNotification; // Raw ACP data
|
||||
}
|
||||
|
||||
createSession(id: string, workingDirectory: string): SessionState {
|
||||
const state: SessionState = {
|
||||
id,
|
||||
workingDirectory,
|
||||
createdAt: new Date(),
|
||||
lastActiveAt: new Date(),
|
||||
messageCount: 0,
|
||||
};
|
||||
this.sessions.set(id, state);
|
||||
return state;
|
||||
}
|
||||
interface SessionData {
|
||||
session: MulticaSession;
|
||||
updates: StoredSessionUpdate[];
|
||||
}
|
||||
|
||||
updateActivity(sessionId: string): void {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (session) {
|
||||
session.lastActiveAt = new Date();
|
||||
session.messageCount++;
|
||||
}
|
||||
}
|
||||
|
||||
getSession(sessionId: string): SessionState | undefined {
|
||||
return this.sessions.get(sessionId);
|
||||
}
|
||||
|
||||
getAllSessions(): SessionState[] {
|
||||
return Array.from(this.sessions.values());
|
||||
}
|
||||
|
||||
removeSession(sessionId: string): void {
|
||||
this.sessions.delete(sessionId);
|
||||
}
|
||||
class SessionStore {
|
||||
async initialize(): Promise<void>;
|
||||
async create(params: CreateSessionParams): Promise<MulticaSession>;
|
||||
async list(options?: ListSessionsOptions): Promise<MulticaSession[]>;
|
||||
async get(sessionId: string): Promise<SessionData | null>;
|
||||
async appendUpdate(sessionId: string, update: SessionNotification): Promise<void>;
|
||||
async updateMeta(sessionId: string, updates: Partial<MulticaSession>): Promise<MulticaSession>;
|
||||
async delete(sessionId: string): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**Session Resume Flow**:
|
||||
1. Load session data from SessionStore
|
||||
2. Start agent if not running
|
||||
3. Create new ACP session (agent has no memory of previous conversation)
|
||||
4. Update `agentSessionId` mapping
|
||||
5. UI displays stored conversation history
|
||||
|
||||
#### 5.2.3 Config Manager
|
||||
|
||||
**Responsibility**: Persist and retrieve application configuration.
|
||||
@@ -1022,11 +1030,12 @@ const mainWindow = new BrowserWindow({
|
||||
|
||||
| ID | Question | Status | Decision |
|
||||
|----|----------|--------|----------|
|
||||
| Q1 | Official ACP TypeScript SDK package name? | Open | Need to verify |
|
||||
| Q2 | Should we support multiple concurrent sessions in V1? | Open | Leaning towards single session |
|
||||
| Q1 | Official ACP TypeScript SDK package name? | ✅ Resolved | `@agentclientprotocol/sdk` |
|
||||
| Q2 | Should we support multiple concurrent sessions in V1? | ✅ Resolved | Yes, SessionStore supports multiple sessions |
|
||||
| Q3 | How to handle agent crashes gracefully? | Open | Auto-restart with notification |
|
||||
| Q4 | Conversation history storage format? | Open | SQLite vs JSON files |
|
||||
| Q5 | Should working directory be per-session or global? | Open | Per-session recommended |
|
||||
| Q4 | Conversation history storage format? | ✅ Resolved | JSON files (index.json + per-session data files) |
|
||||
| Q5 | Should working directory be per-session or global? | ✅ Resolved | Per-session |
|
||||
| Q6 | Should Multica restore agent internal state on resume? | Open | Currently: No. Creates new ACP session, UI shows history only. Future: Consider `session/load` if agents support it. |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user