@@ -10,14 +10,14 @@ Obsidian plugin for AI agent interaction (Claude Code, Gemini CLI, custom agents
1010```
1111src/
1212├── domain/ # Pure domain models + ports (interfaces)
13- │ ├── models/ # agent-config, agent-error, chat-message, chat-session
13+ │ ├── models/ # agent-config, agent-error, chat-message, chat-session, session-update
1414│ └── ports/ # IAgentClient, ISettingsAccess, IVaultAccess
1515├── adapters/ # Interface implementations
1616│ ├── acp/ # ACP protocol (acp.adapter.ts, acp-type-converter.ts)
1717│ └── obsidian/ # Platform adapters (vault, settings, mention-service)
1818├── hooks/ # React custom hooks (state + logic)
1919│ ├── useAgentSession.ts # Session lifecycle, agent switching
20- │ ├── useChat.ts # Message sending, callbacks
20+ │ ├── useChat.ts # Message sending, session update handling
2121│ ├── usePermission.ts # Permission handling
2222│ ├── useMentions.ts # @[[note]] suggestions
2323│ ├── useSlashCommands.ts # /command suggestions
4040### ChatView (` components/chat/ChatView.tsx ` )
4141- ** Hook Composition** : Combines all hooks (useAgentSession, useChat, usePermission, etc.)
4242- ** Adapter Instantiation** : Creates AcpAdapter, VaultAdapter, MentionService via useMemo
43+ - ** Callback Registration** : Registers ` onSessionUpdate ` for unified event handling
4344- ** Rendering** : Delegates to ChatHeader, ChatMessages, ChatInput
4445
4546### Hooks (` hooks/ ` )
4849- ` createSession() ` : Load config, inject API keys, initialize + newSession
4950- ` switchAgent() ` : Change active agent, restart session
5051- ` closeSession() ` : Cancel session, disconnect
52+ - ` updateAvailableCommands() ` : Handle slash command updates
53+ - ` updateCurrentMode() ` : Handle mode change updates
5154
52- ** useChat** : Messaging
55+ ** useChat** : Messaging and session update handling
5356- ` sendMessage() ` : Prepare (auto-mention, path conversion) → send via IAgentClient
5457- ` handleNewChat() ` : Export if enabled, restart session
55- - Callbacks: addMessage, updateLastMessage, updateMessage
58+ - ` handleSessionUpdate() ` : Unified handler for all session updates (agent_message_chunk, tool_call, etc.)
59+ - ` upsertToolCall() ` : Create or update tool call in single ` setMessages ` callback (avoids race conditions)
60+ - ` updateLastMessage() ` : Append text/thought chunks to last assistant message
61+ - ` updateMessage() ` : Update specific message by tool call ID
5662
5763** usePermission** : Permission handling
5864- ` handlePermissionResponse() ` : Respond with selected option
@@ -67,8 +73,9 @@ Implements IAgentClient + IAcpClient (terminal ops)
6773
6874- ** Process** : spawn() with login shell (macOS/Linux -l, Windows shell: true )
6975- ** Protocol** : JSON-RPC over stdin/stdout via ndJsonStream
70- - ** Flow** : initialize() → newSession() → sendMessage() → sessionUpdate() callbacks
71- - ** Updates** : agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan, available_commands_update
76+ - ** Flow** : initialize() → newSession() → sendMessage() → sessionUpdate via ` onSessionUpdate `
77+ - ** Updates** : agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan, available_commands_update, current_mode_update
78+ - ** Unified Callback** : Single ` onSessionUpdate(callback) ` replaces legacy ` onMessage ` , ` onError ` , ` onPermissionRequest `
7279- ** Permissions** : Promise-based Map<requestId, resolver>
7380- ** Terminal** : createTerminal, terminalOutput, killTerminal, releaseTerminal
7481
@@ -83,6 +90,25 @@ Pure functions (non-React):
8390- ` prepareMessage() ` : Auto-mention, convert @[[ note]] → paths
8491- ` sendPreparedMessage() ` : Send via IAgentClient, auth retry
8592
93+ ## Domain Models
94+
95+ ### SessionUpdate (` domain/models/session-update.ts ` )
96+ Union type for all session update events from the agent:
97+
98+ ``` typescript
99+ type SessionUpdate =
100+ | AgentMessageChunkUpdate // Text chunk from agent's response
101+ | AgentThoughtChunkUpdate // Text chunk from agent's reasoning
102+ | ToolCallUpdate // New tool call event
103+ | ToolCallUpdateUpdate // Update to existing tool call
104+ | PlanUpdate // Agent's task plan
105+ | AvailableCommandsUpdate // Slash commands changed
106+ | CurrentModeUpdate // Mode changed
107+ | ErrorUpdate ; // Error from agent operations
108+ ```
109+
110+ This domain type abstracts ACP's ` SessionNotification.update.sessionUpdate ` values, allowing the application layer to handle events without depending on ACP protocol specifics.
111+
86112## Ports (Interfaces)
87113
88114``` typescript
@@ -93,10 +119,15 @@ interface IAgentClient {
93119 sendMessage(sessionId : string , message : string ): Promise <void >;
94120 cancel(sessionId : string ): Promise <void >;
95121 disconnect(): Promise <void >;
96- onMessage(callback : (message : ChatMessage ) => void ): void ;
97- onError(callback : (error : AgentError ) => void ): void ;
98- onPermissionRequest(callback : (request : PermissionRequest ) => void ): void ;
122+
123+ // Unified callback for all session updates
124+ onSessionUpdate(callback : (update : SessionUpdate ) => void ): void ;
125+
99126 respondToPermission(requestId : string , optionId : string ): Promise <void >;
127+ isInitialized(): boolean ;
128+ getCurrentAgentId(): string | null ;
129+ setSessionMode(sessionId : string , modeId : string ): Promise <void >;
130+ setSessionModel(sessionId : string , modelId : string ): Promise <void >;
100131}
101132
102133interface IVaultAccess {
@@ -120,6 +151,7 @@ interface ISettingsAccess {
1201512 . ** Pure functions in shared/** : Non-React business logic
1211523 . ** Ports for ACP resistance** : IAgentClient interface isolates protocol changes
1221534 . ** Domain has zero deps** : No ` obsidian ` , ` @agentclientprotocol/sdk `
154+ 5 . ** Unified callbacks** : Use ` onSessionUpdate ` for all agent events (not multiple callbacks)
123155
124156### Obsidian Plugin Review (CRITICAL)
1251571 . No innerHTML/outerHTML - use createEl/createDiv/createSpan
@@ -141,6 +173,7 @@ interface ISettingsAccess {
1411733 . useRef for cleanup function access
1421744 . Error handling: try-catch async ops
1431755 . Logging: Logger class (respects debugMode)
176+ 6 . ** Upsert pattern** : Use ` setMessages ` functional updates to avoid race conditions with tool_call updates
144177
145178## Common Tasks
146179
@@ -158,8 +191,17 @@ interface ISettingsAccess {
158191
159192### Modify Message Types
1601931 . Update ` ChatMessage ` /` MessageContent ` in ` domain/models/chat-message.ts `
161- 2 . Update ` AcpAdapter.sessionUpdate() ` to handle new type
162- 3 . Update ` MessageContentRenderer ` to render new type
194+ 2 . If adding new session update type:
195+ - Add to ` SessionUpdate ` union in ` domain/models/session-update.ts `
196+ - Handle in ` useChat.handleSessionUpdate() `
197+ 3 . Update ` AcpAdapter.sessionUpdate() ` to emit the new type
198+ 4 . Update ` MessageContentRenderer ` to render new type
199+
200+ ### Add New Session Update Type
201+ 1 . Define interface in ` domain/models/session-update.ts `
202+ 2 . Add to ` SessionUpdate ` union type
203+ 3 . Handle in ` useChat.handleSessionUpdate() ` (for message-level updates)
204+ 4 . Or handle in ` ChatView ` (for session-level updates like ` available_commands_update ` )
163205
164206### Debug
1652071 . Settings → Developer Settings → Debug Mode ON
@@ -170,8 +212,8 @@ interface ISettingsAccess {
170212
171213** Communication** : JSON-RPC 2.0 over stdin/stdout
172214
173- ** Methods** : initialize, newSession, authenticate, prompt, cancel
174- ** Notifications** : session/update (agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan, available_commands_update)
215+ ** Methods** : initialize, newSession, authenticate, prompt, cancel, setSessionMode, setSessionModel
216+ ** Notifications** : session/update (agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan, available_commands_update, current_mode_update )
175217** Requests** : requestPermission
176218
177219** Agents** :
@@ -181,4 +223,4 @@ interface ISettingsAccess {
181223
182224---
183225
184- ** Last Updated** : November 2025 | ** Architecture** : React Hooks | ** Version** : 0.3 .0
226+ ** Last Updated** : December 2025 | ** Architecture** : React Hooks | ** Version** : 0.4 .0
0 commit comments