From bcdb897c13ee24a640f2c2dc02f350b683c699f0 Mon Sep 17 00:00:00 2001 From: Jiayuan Date: Wed, 14 Jan 2026 02:54:16 +0800 Subject: [PATCH] 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 --- README.md | 57 ++++++++++++++++++++++---- docs/system-design.md | 95 +++++++++++++++++++++++-------------------- 2 files changed, 102 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 02354f717e..99cb11f295 100644 --- a/README.md +++ b/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 diff --git a/docs/system-design.md b/docs/system-design.md index 6f64c39ae2..328027f371 100644 --- a/docs/system-design.md +++ b/docs/system-design.md @@ -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 = 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; + async create(params: CreateSessionParams): Promise; + async list(options?: ListSessionsOptions): Promise; + async get(sessionId: string): Promise; + async appendUpdate(sessionId: string, update: SessionNotification): Promise; + async updateMeta(sessionId: string, updates: Partial): Promise; + async delete(sessionId: string): Promise; } ``` +**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. | ---