diff --git a/tools/server/webui/docs/architecture/high-level-architecture-simplified.md b/tools/server/webui/docs/architecture/high-level-architecture-simplified.md
index dde630be37..500f477c9a 100644
--- a/tools/server/webui/docs/architecture/high-level-architecture-simplified.md
+++ b/tools/server/webui/docs/architecture/high-level-architecture-simplified.md
@@ -12,11 +12,13 @@ flowchart TB
C_Form["ChatForm"]
C_Messages["ChatMessages"]
C_Message["ChatMessage"]
- C_AgenticContent["AgenticContent"]
+ C_ChatMessageAgenticContent["ChatMessageAgenticContent"]
C_MessageEditForm["ChatMessageEditForm"]
C_ModelsSelector["ModelsSelector"]
C_Settings["ChatSettings"]
- C_McpSettings["McpSettingsSection"]
+ C_McpSettings["McpServersSettings"]
+ C_McpResourceBrowser["McpResourceBrowser"]
+ C_McpServersSelector["McpServersSelector"]
end
subgraph Hooks["🪝 Hooks"]
@@ -26,24 +28,22 @@ flowchart TB
subgraph Stores["🗄️ Stores"]
S1["chatStore
Chat interactions & streaming"]
- S2["conversationsStore
Conversation data & messages"]
+ SA["agenticStore
Multi-turn agentic loop orchestration"]
+ S2["conversationsStore
Conversation data, messages & MCP overrides"]
S3["modelsStore
Model selection & loading"]
S4["serverStore
Server props & role detection"]
S5["settingsStore
User configuration incl. MCP"]
+ S6["mcpStore
MCP servers, tools, prompts"]
+ S7["mcpResourceStore
MCP resources & attachments"]
end
subgraph Services["⚙️ Services"]
- SV1["ChatService
incl. agentic loop"]
+ SV1["ChatService"]
SV2["ModelsService"]
SV3["PropsService"]
SV4["DatabaseService"]
SV5["ParameterSyncService"]
- end
-
- subgraph MCP["🔧 MCP (Model Context Protocol)"]
- MCP1["MCPClient
@modelcontextprotocol/sdk"]
- MCP2["mcpStore
reactive state"]
- MCP3["OpenAISseClient"]
+ SV6["MCPService
protocol operations"]
end
subgraph Storage["💾 Storage"]
@@ -59,7 +59,7 @@ flowchart TB
end
subgraph ExternalMCP["🔌 External MCP Servers"]
- EXT1["MCP Server 1"]
+ EXT1["MCP Server 1
WebSocket/HTTP/SSE"]
EXT2["MCP Server N"]
end
@@ -67,13 +67,18 @@ flowchart TB
R1 & R2 --> C_Screen
RL --> C_Sidebar
+ %% Layout runs MCP health checks
+ RL --> S6
+
%% Component hierarchy
C_Screen --> C_Form & C_Messages & C_Settings
C_Messages --> C_Message
- C_Message --> C_AgenticContent
+ C_Message --> C_ChatMessageAgenticContent
C_Message --> C_MessageEditForm
C_Form & C_MessageEditForm --> C_ModelsSelector
+ C_Form --> C_McpServersSelector
C_Settings --> C_McpSettings
+ C_McpSettings --> C_McpResourceBrowser
%% Components → Hooks → Stores
C_Form & C_Messages --> H1 & H2
@@ -85,7 +90,15 @@ flowchart TB
C_Sidebar --> S2
C_ModelsSelector --> S3 & S4
C_Settings --> S5
- C_McpSettings --> S5
+ C_McpSettings --> S6
+ C_McpResourceBrowser --> S6 & S7
+ C_McpServersSelector --> S6
+ C_Form --> S6
+
+ %% chatStore → agenticStore → mcpStore (agentic loop)
+ S1 --> SA
+ SA --> SV1
+ SA --> S6
%% Stores → Services
S1 --> SV1 & SV4
@@ -93,12 +106,8 @@ flowchart TB
S3 --> SV2 & SV3
S4 --> SV3
S5 --> SV5
-
- %% ChatService → MCP (Agentic Mode)
- SV1 --> MCP2
- MCP2 --> MCP1
- SV1 --> MCP3
- MCP3 --> API1
+ S6 --> SV6
+ S7 --> SV6
%% Services → Storage
SV4 --> ST1
@@ -110,7 +119,7 @@ flowchart TB
SV3 --> API2
%% MCP → External Servers
- MCP1 --> EXT1 & EXT2
+ SV6 --> EXT1 & EXT2
%% Styling
classDef routeStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px
@@ -121,15 +130,16 @@ flowchart TB
classDef storageStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef apiStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef mcpStyle fill:#e0f2f1,stroke:#00695c,stroke-width:2px
+ classDef agenticStyle fill:#e8eaf6,stroke:#283593,stroke-width:2px
classDef externalStyle fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px,stroke-dasharray: 5 5
class R1,R2,RL routeStyle
- class C_Sidebar,C_Screen,C_Form,C_Messages,C_Message,C_AgenticContent,C_MessageEditForm,C_ModelsSelector,C_Settings,C_McpSettings componentStyle
+ class C_Sidebar,C_Screen,C_Form,C_Messages,C_Message,C_ChatMessageAgenticContent,C_MessageEditForm,C_ModelsSelector,C_Settings componentStyle
+ class C_McpSettings,C_McpResourceBrowser,C_McpServersSelector componentStyle
class H1,H2 hookStyle
- class S1,S2,S3,S4,S5 storeStyle
- class SV1,SV2,SV3,SV4,SV5 serviceStyle
+ class S1,S2,S3,S4,S5,SA,S6,S7 storeStyle
+ class SV1,SV2,SV3,SV4,SV5,SV6 serviceStyle
class ST1,ST2 storageStyle
class API1,API2,API3,API4 apiStyle
- class MCP1,MCP2,MCP3 mcpStyle
class EXT1,EXT2 externalStyle
```
diff --git a/tools/server/webui/docs/architecture/high-level-architecture.md b/tools/server/webui/docs/architecture/high-level-architecture.md
index 0a1e5a745e..42ddb3f4f5 100644
--- a/tools/server/webui/docs/architecture/high-level-architecture.md
+++ b/tools/server/webui/docs/architecture/high-level-architecture.md
@@ -22,6 +22,13 @@ end
C_ModelsSelector["ModelsSelector"]
C_Settings["ChatSettings"]
end
+ subgraph MCPComponents["MCP UI"]
+ C_McpSettings["McpServersSettings"]
+ C_McpServerCard["McpServerCard"]
+ C_McpResourceBrowser["McpResourceBrowser"]
+ C_McpResourcePreview["McpResourcePreview"]
+ C_McpServersSelector["McpServersSelector"]
+ end
end
subgraph Hooks["🪝 Hooks"]
@@ -43,14 +50,20 @@ end
S1Edit["Editing:
editAssistantMessage()
editUserMessagePreserveResponses()
editMessageWithBranching()
clearEditMode()
isEditModeActive()
getAddFilesHandler()
setEditModeActive()"]
S1Utils["Utilities:
getApiOptions()
parseTimingData()
getOrCreateAbortController()
getConversationModel()"]
end
+ subgraph SA["agenticStore"]
+ SAState["State:
sessions (Map)
isAnyRunning"]
+ SASession["Session Management:
getSession()
updateSession()
clearSession()
getActiveSessions()
isRunning()
currentTurn()
totalToolCalls()
lastError()
streamingToolCall()"]
+ SAConfig["Configuration:
getConfig()
maxTurns, maxToolPreviewLines"]
+ SAFlow["Agentic Loop:
runAgenticFlow()
executeAgenticLoop()
normalizeToolCalls()
emitToolCallResult()
extractBase64Attachments()"]
+ end
subgraph S2["conversationsStore"]
- S2State["State:
conversations
activeConversation
activeMessages
usedModalities
isInitialized
titleUpdateConfirmationCallback"]
- S2Modal["Modalities:
getModalitiesUpToMessage()
calculateModalitiesFromMessages()"]
+ S2State["State:
conversations
activeConversation
activeMessages
isInitialized
pendingMcpServerOverrides
titleUpdateConfirmationCallback"]
S2Lifecycle["Lifecycle:
initialize()
loadConversations()
clearActiveConversation()"]
- S2ConvCRUD["Conversation CRUD:
createConversation()
loadConversation()
deleteConversation()
updateConversationName()
updateConversationTitleWithConfirmation()"]
+ S2ConvCRUD["Conversation CRUD:
createConversation()
loadConversation()
deleteConversation()
deleteAll()
updateConversationName()
updateConversationTitleWithConfirmation()"]
S2MsgMgmt["Message Management:
refreshActiveMessages()
addMessageToActive()
updateMessageAtIndex()
findMessageIndex()
sliceActiveMessages()
removeMessageAtIndex()
getConversationMessages()"]
S2Nav["Navigation:
navigateToSibling()
updateCurrentNode()
updateConversationTimestamp()"]
- S2Export["Import/Export:
downloadConversation()
exportAllConversations()
importConversations()
triggerDownload()"]
+ S2McpOverrides["MCP Per-Chat Overrides:
getMcpServerOverride()
getAllMcpServerOverrides()
setMcpServerOverride()
toggleMcpServerForChat()
removeMcpServerOverride()
isMcpServerEnabledForChat()
clearPendingMcpServerOverrides()"]
+ S2Export["Import/Export:
downloadConversation()
exportAllConversations()
importConversations()
importConversationsData()
triggerDownload()"]
S2Utils["Utilities:
setTitleUpdateConfirmationCallback()"]
end
subgraph S3["modelsStore"]
@@ -77,6 +90,21 @@ end
S5Sync["Server Sync:
syncWithServerDefaults()
forceSyncWithServerDefaults()"]
S5Utils["Utilities:
getConfig()
getAllConfig()
getParameterInfo()
getParameterDiff()
getServerDefaults()
clearAllUserOverrides()"]
end
+ subgraph S6["mcpStore"]
+ S6State["State:
isInitializing, error
toolCount, connectedServers
healthChecks (Map)
connections (Map)
toolsIndex (Map)"]
+ S6Lifecycle["Lifecycle:
ensureInitialized()
initialize()
shutdown()
acquireConnection()
releaseConnection()"]
+ S6Health["Health Checks:
runHealthCheck()
runHealthChecksForServers()
updateHealthCheck()
getHealthCheckState()
clearHealthCheck()"]
+ S6Servers["Server Management:
getServers()
addServer()
updateServer()
removeServer()
getServerById()
getServerDisplayName()"]
+ S6Tools["Tool Operations:
getToolDefinitionsForLLM()
getToolNames()
hasTool()
getToolServer()
executeTool()
executeToolByName()"]
+ S6Prompts["Prompt Operations:
getAllPrompts()
getPrompt()
hasPromptsCapability()
getPromptCompletions()"]
+ end
+ subgraph S7["mcpResourceStore"]
+ S7State["State:
serverResources (Map)
cachedResources (Map)
subscriptions (Map)
attachments[]
isLoading"]
+ S7Resources["Resource Discovery:
setServerResources()
getServerResources()
getAllResourceInfos()
getAllTemplateInfos()
clearServerResources()"]
+ S7Cache["Caching:
cacheResourceContent()
getCachedContent()
invalidateCache()
clearCache()"]
+ S7Subs["Subscriptions:
addSubscription()
removeSubscription()
isSubscribed()
handleResourceUpdate()"]
+ S7Attach["Attachments:
addAttachment()
updateAttachmentContent()
removeAttachment()
clearAttachments()
toMessageExtras()"]
+ end
subgraph ReactiveExports["⚡ Reactive Exports"]
direction LR
@@ -95,12 +123,19 @@ end
RE9c["setEditModeActive()"]
RE9d["clearEditMode()"]
end
+ subgraph AgenticExports["agenticStore"]
+ REA1["agenticIsRunning()"]
+ REA2["agenticCurrentTurn()"]
+ REA3["agenticTotalToolCalls()"]
+ REA4["agenticLastError()"]
+ REA5["agenticStreamingToolCall()"]
+ REA6["agenticIsAnyRunning()"]
+ end
subgraph ConvExports["conversationsStore"]
RE10["conversations()"]
RE11["activeConversation()"]
RE12["activeMessages()"]
RE13["isConversationsInitialized()"]
- RE14["usedModalities()"]
end
subgraph ModelsExports["modelsStore"]
RE15["modelOptions()"]
@@ -131,6 +166,13 @@ end
RE36["theme()"]
RE37["isInitialized()"]
end
+ subgraph MCPExports["mcpStore / mcpResourceStore"]
+ RE38["mcpResources()"]
+ RE39["mcpResourceAttachments()"]
+ RE40["mcpHasResourceAttachments()"]
+ RE41["mcpTotalResourceCount()"]
+ RE42["mcpResourcesLoading()"]
+ end
end
end
@@ -138,9 +180,9 @@ end
direction TB
subgraph SV1["ChatService"]
SV1Msg["Messaging:
sendMessage()"]
- SV1Stream["Streaming:
handleStreamResponse()
parseSSEChunk()"]
- SV1Convert["Conversion:
convertMessageToChatData()
convertExtraToApiFormat()"]
- SV1Utils["Utilities:
extractReasoningContent()
getServerProps()
getModels()"]
+ SV1Stream["Streaming:
handleStreamResponse()
handleNonStreamResponse()"]
+ SV1Convert["Conversion:
convertDbMessageToApiChatMessageData()
mergeToolCallDeltas()"]
+ SV1Utils["Utilities:
stripReasoningContent()
extractModelName()
parseErrorResponse()"]
end
subgraph SV2["ModelsService"]
SV2List["Listing:
list()
listRouter()"]
@@ -152,7 +194,7 @@ end
end
subgraph SV4["DatabaseService"]
SV4Conv["Conversations:
createConversation()
getConversation()
getAllConversations()
updateConversation()
deleteConversation()"]
- SV4Msg["Messages:
createMessageBranch()
createRootMessage()
getConversationMessages()
updateMessage()
deleteMessage()
deleteMessageCascading()"]
+ SV4Msg["Messages:
createMessageBranch()
createRootMessage()
createSystemMessage()
getConversationMessages()
updateMessage()
deleteMessage()
deleteMessageCascading()"]
SV4Node["Navigation:
updateCurrentNode()"]
SV4Import["Import:
importConversations()"]
end
@@ -162,31 +204,18 @@ end
SV5Info["Info:
getParameterInfo()
canSyncParameter()
getSyncableParameterKeys()
validateServerParameter()"]
SV5Diff["Diff:
createParameterDiff()"]
end
- end
-
- subgraph MCP["🔧 MCP (Model Context Protocol)"]
- direction TB
- subgraph MCPStoreBox["mcpStore"]
- MCPStoreState["State:
client, isInitializing
error, availableTools"]
- MCPStoreLifecycle["Lifecycle:
ensureClient()
shutdown()"]
- MCPStoreExec["Execution:
execute()"]
- end
- subgraph MCPClient["MCPClient"]
- MCP1Init["Lifecycle:
initialize()
shutdown()"]
- MCP1Tools["Tools:
listTools()
getToolsDefinition()
execute()"]
- MCP1Transport["Transport:
StreamableHTTPClientTransport
SSEClientTransport (fallback)"]
- end
- subgraph MCPSse["OpenAISseClient"]
- MCP3Stream["Streaming:
streamChatCompletion()"]
- MCP3Parse["Parsing:
tool call delta merging"]
- end
- subgraph MCPConfig["config/mcp"]
- MCP4Parse["Parsing:
parseServersFromSettings()"]
+ subgraph SV6["MCPService"]
+ SV6Transport["Transport:
createTransport()
WebSocket / StreamableHTTP / SSE"]
+ SV6Conn["Connection:
connect()
disconnect()"]
+ SV6Tools["Tools:
listTools()
callTool()"]
+ SV6Prompts["Prompts:
listPrompts()
getPrompt()"]
+ SV6Resources["Resources:
listResources()
listResourceTemplates()
readResource()
subscribeResource()
unsubscribeResource()"]
+ SV6Complete["Completions:
complete()"]
end
end
subgraph ExternalMCP["🔌 External MCP Servers"]
- EXT1["MCP Server 1
(StreamableHTTP/SSE)"]
+ EXT1["MCP Server 1
(WebSocket/StreamableHTTP/SSE)"]
EXT2["MCP Server N"]
end
@@ -197,6 +226,7 @@ end
ST5["LocalStorage"]
ST6["config"]
ST7["userOverrides"]
+ ST8["mcpServers"]
end
subgraph APIs["🌐 llama-server API"]
@@ -211,6 +241,9 @@ end
R2 --> C_Screen
RL --> C_Sidebar
+ %% Layout runs MCP health checks on startup
+ RL --> S6
+
%% Component hierarchy
C_Screen --> C_Form & C_Messages & C_Settings
C_Messages --> C_Message
@@ -220,8 +253,15 @@ end
C_MessageEditForm --> C_Attach
C_Form --> C_ModelsSelector
C_Form --> C_Attach
+ C_Form --> C_McpServersSelector
C_Message --> C_Attach
+ %% MCP Components hierarchy
+ C_Settings --> C_McpSettings
+ C_McpSettings --> C_McpServerCard
+ C_McpServerCard --> C_McpResourceBrowser
+ C_McpResourceBrowser --> C_McpResourcePreview
+
%% Components use Hooks
C_Form --> H1
C_Message --> H1 & H2
@@ -236,17 +276,29 @@ end
C_Screen --> S1 & S2
C_Messages --> S2
C_Message --> S1 & S2 & S3
- C_Form --> S1 & S3
+ C_Form --> S1 & S3 & S6
C_Sidebar --> S2
C_ModelsSelector --> S3 & S4
C_Settings --> S5
+ C_McpSettings --> S6
+ C_McpServerCard --> S6
+ C_McpResourceBrowser --> S6 & S7
+ C_McpServersSelector --> S6
%% Stores export Reactive State
S1 -. exports .-> ChatExports
+ SA -. exports .-> AgenticExports
S2 -. exports .-> ConvExports
S3 -. exports .-> ModelsExports
S4 -. exports .-> ServerExports
S5 -. exports .-> SettingsExports
+ S6 -. exports .-> MCPExports
+ S7 -. exports .-> MCPExports
+
+ %% chatStore → agenticStore (agentic loop orchestration)
+ S1 --> SA
+ SA --> SV1
+ SA --> S6
%% Stores use Services
S1 --> SV1 & SV4
@@ -254,40 +306,34 @@ end
S3 --> SV2 & SV3
S4 --> SV3
S5 --> SV5
+ S6 --> SV6
+ S7 --> SV6
%% Services to Storage
SV4 --> ST1
ST1 --> ST2 & ST3
SV5 --> ST5
- ST5 --> ST6 & ST7
+ ST5 --> ST6 & ST7 & ST8
%% Services to APIs
SV1 --> API1
SV2 --> API3 & API4
SV3 --> API2
- %% ChatService → MCP (Agentic Mode)
- SV1 --> MCPStoreBox
- MCPStoreBox --> MCPClient
- SV1 --> MCPSse
- MCPSse --> API1
- MCPConfig --> MCPStoreBox
-
%% MCP → External Servers
- MCPClient --> EXT1 & EXT2
+ SV6 --> EXT1 & EXT2
%% Styling
classDef routeStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef componentStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef componentGroupStyle fill:#e1bee7,stroke:#7b1fa2,stroke-width:1px
+ classDef hookStyle fill:#fff8e1,stroke:#ff8f00,stroke-width:2px
classDef storeStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef stateStyle fill:#ffe0b2,stroke:#e65100,stroke-width:1px
classDef methodStyle fill:#ffecb3,stroke:#e65100,stroke-width:1px
classDef reactiveStyle fill:#fffde7,stroke:#f9a825,stroke-width:1px
classDef serviceStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef serviceMStyle fill:#c8e6c9,stroke:#2e7d32,stroke-width:1px
- classDef mcpStyle fill:#e0f2f1,stroke:#00695c,stroke-width:2px
- classDef mcpMethodStyle fill:#b2dfdb,stroke:#00695c,stroke-width:1px
classDef externalStyle fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px,stroke-dasharray: 5 5
classDef storageStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef apiStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
@@ -296,26 +342,32 @@ end
class C_Sidebar,C_Screen,C_Form,C_Messages,C_Message,C_MessageUser,C_MessageEditForm componentStyle
class C_ModelsSelector,C_Settings componentStyle
class C_Attach componentStyle
- class H1,H2,H3 methodStyle
- class LayoutComponents,ChatUIComponents componentGroupStyle
- class Hooks storeStyle
- class S1,S2,S3,S4,S5 storeStyle
- class S1State,S2State,S3State,S4State,S5State stateStyle
+ class C_McpSettings,C_McpServerCard,C_McpResourceBrowser,C_McpResourcePreview,C_McpServersSelector componentStyle
+ class H1,H2,H3 hookStyle
+ class LayoutComponents,ChatUIComponents,MCPComponents componentGroupStyle
+ class Hooks hookStyle
+ classDef agenticStyle fill:#e8eaf6,stroke:#283593,stroke-width:2px
+ classDef agenticMethodStyle fill:#c5cae9,stroke:#283593,stroke-width:1px
+
+ class S1,S2,S3,S4,S5,SA,S6,S7 storeStyle
+ class S1State,S2State,S3State,S4State,S5State,SAState,S6State,S7State stateStyle
class S1Msg,S1Regen,S1Edit,S1Stream,S1LoadState,S1ProcState,S1Error,S1Utils methodStyle
- class S2Lifecycle,S2ConvCRUD,S2MsgMgmt,S2Nav,S2Modal,S2Export,S2Utils methodStyle
+ class SASession,SAConfig,SAFlow methodStyle
+ class S2Lifecycle,S2ConvCRUD,S2MsgMgmt,S2Nav,S2McpOverrides,S2Export,S2Utils methodStyle
class S3Getters,S3Modal,S3Status,S3Fetch,S3Select,S3LoadUnload,S3Utils methodStyle
class S4Getters,S4Data,S4Utils methodStyle
class S5Lifecycle,S5Update,S5Reset,S5Sync,S5Utils methodStyle
- class ChatExports,ConvExports,ModelsExports,ServerExports,SettingsExports reactiveStyle
- class SV1,SV2,SV3,SV4,SV5 serviceStyle
- class MCPStoreBox,MCPClient,MCPSse,MCPConfig mcpStyle
- class MCPStoreState,MCPStoreLifecycle,MCPStoreExec,MCP1Init,MCP1Tools,MCP1Transport,MCP3Stream,MCP3Parse,MCP4Build,MCP4Parse mcpMethodStyle
+ class S6Lifecycle,S6Health,S6Servers,S6Tools,S6Prompts methodStyle
+ class S7Resources,S7Cache,S7Subs,S7Attach methodStyle
+ class ChatExports,AgenticExports,ConvExports,ModelsExports,ServerExports,SettingsExports,MCPExports reactiveStyle
+ class SV1,SV2,SV3,SV4,SV5,SV6 serviceStyle
+ class SV6Transport,SV6Conn,SV6Tools,SV6Prompts,SV6Resources,SV6Complete serviceMStyle
class EXT1,EXT2 externalStyle
class SV1Msg,SV1Stream,SV1Convert,SV1Utils serviceMStyle
class SV2List,SV2LoadUnload,SV2Status serviceMStyle
class SV3Fetch serviceMStyle
class SV4Conv,SV4Msg,SV4Node,SV4Import serviceMStyle
class SV5Extract,SV5Merge,SV5Info,SV5Diff serviceMStyle
- class ST1,ST2,ST3,ST5,ST6,ST7 storageStyle
+ class ST1,ST2,ST3,ST5,ST6,ST7,ST8 storageStyle
class API1,API2,API3,API4 apiStyle
```
diff --git a/tools/server/webui/docs/flows/chat-flow.md b/tools/server/webui/docs/flows/chat-flow.md
index 05e1df385a..296693c6a5 100644
--- a/tools/server/webui/docs/flows/chat-flow.md
+++ b/tools/server/webui/docs/flows/chat-flow.md
@@ -2,8 +2,10 @@
sequenceDiagram
participant UI as 🧩 ChatForm / ChatMessage
participant chatStore as 🗄️ chatStore
+ participant agenticStore as 🗄️ agenticStore
participant convStore as 🗄️ conversationsStore
participant settingsStore as 🗄️ settingsStore
+ participant mcpStore as 🗄️ mcpStore
participant ChatSvc as ⚙️ ChatService
participant DbSvc as ⚙️ DatabaseService
participant API as 🌐 /v1/chat/completions
@@ -25,6 +27,9 @@ sequenceDiagram
Note over convStore: → see conversations-flow.mmd
end
+ chatStore->>mcpStore: consumeResourceAttachmentsAsExtras()
+ Note right of mcpStore: Converts pending MCP resource
attachments into message extras
+
chatStore->>chatStore: addMessage("user", content, extras)
chatStore->>DbSvc: createMessageBranch(userMsg, parentId)
chatStore->>convStore: addMessageToActive(userMsg)
@@ -38,7 +43,7 @@ sequenceDiagram
deactivate chatStore
%% ═══════════════════════════════════════════════════════════════════════════
- Note over UI,API: 🌊 STREAMING
+ Note over UI,API: 🌊 STREAMING (with agentic flow detection)
%% ═══════════════════════════════════════════════════════════════════════════
activate chatStore
@@ -52,10 +57,17 @@ sequenceDiagram
chatStore->>chatStore: getApiOptions()
Note right of chatStore: Merge from settingsStore.config:
temperature, max_tokens, top_p, etc.
- chatStore->>ChatSvc: sendMessage(messages, options, signal)
+ alt agenticConfig.enabled && mcpStore has connected servers
+ chatStore->>agenticStore: runAgenticFlow(convId, messages, assistantMsg, options, signal)
+ Note over agenticStore: Multi-turn agentic loop:
1. Call ChatService.sendMessage()
2. If response has tool_calls → execute via mcpStore
3. Append tool results as messages
4. Loop until no more tool_calls or maxTurns
→ see agentic flow details below
+ agenticStore-->>chatStore: final response with timings
+ else standard (non-agentic) flow
+ chatStore->>ChatSvc: sendMessage(messages, options, signal)
+ end
+
activate ChatSvc
- ChatSvc->>ChatSvc: convertMessageToChatData(messages)
+ ChatSvc->>ChatSvc: convertDbMessageToApiChatMessageData(messages)
Note right of ChatSvc: DatabaseMessage[] → ApiChatMessageData[]
Process attachments (images, PDFs, audio)
ChatSvc->>API: POST /v1/chat/completions
@@ -63,7 +75,7 @@ sequenceDiagram
loop SSE chunks
API-->>ChatSvc: data: {"choices":[{"delta":{...}}]}
- ChatSvc->>ChatSvc: parseSSEChunk(line)
+ ChatSvc->>ChatSvc: handleStreamResponse(response)
alt content chunk
ChatSvc-->>chatStore: onChunk(content)
@@ -154,12 +166,15 @@ sequenceDiagram
Note over UI,API: ✏️ EDIT USER MESSAGE
%% ═══════════════════════════════════════════════════════════════════════════
- UI->>chatStore: editUserMessagePreserveResponses(msgId, newContent)
+ UI->>chatStore: editMessageWithBranching(msgId, newContent, extras)
activate chatStore
chatStore->>chatStore: Get parent of target message
chatStore->>DbSvc: createMessageBranch(editedMsg, parentId)
chatStore->>convStore: refreshActiveMessages()
Note right of chatStore: Creates new branch, original preserved
+ chatStore->>chatStore: createAssistantMessage(editedMsg.id)
+ chatStore->>chatStore: streamChatCompletion(...)
+ Note right of chatStore: Automatically regenerates response
deactivate chatStore
%% ═══════════════════════════════════════════════════════════════════════════
@@ -171,4 +186,43 @@ sequenceDiagram
Note right of chatStore: errorDialogState = {type: 'timeout'|'server', message}
chatStore->>convStore: removeMessageAtIndex(failedMsgIdx)
chatStore->>DbSvc: deleteMessage(failedMsgId)
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🤖 AGENTIC LOOP (when agenticConfig.enabled)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over agenticStore: agenticStore.runAgenticFlow(convId, messages, assistantMsg, options, signal)
+ activate agenticStore
+ agenticStore->>agenticStore: getSession(convId) or create new
+ agenticStore->>agenticStore: updateSession(turn: 0, running: true)
+
+ loop executeAgenticLoop (until no tool_calls or maxTurns)
+ agenticStore->>agenticStore: turn++
+ agenticStore->>ChatSvc: sendMessage(messages, options, signal)
+ ChatSvc->>API: POST /v1/chat/completions
+ API-->>ChatSvc: response with potential tool_calls
+ ChatSvc-->>agenticStore: onComplete(content, reasoning, timings, toolCalls)
+
+ alt response has tool_calls
+ agenticStore->>agenticStore: normalizeToolCalls(toolCalls)
+ loop for each tool_call
+ agenticStore->>agenticStore: updateSession(streamingToolCall)
+ agenticStore->>mcpStore: executeTool(mcpCall, signal)
+ mcpStore-->>agenticStore: tool result
+ agenticStore->>agenticStore: extractBase64Attachments(result)
+ agenticStore->>agenticStore: emitToolCallResult(convId, ...)
+ agenticStore->>convStore: addMessageToActive(toolResultMsg)
+ agenticStore->>DbSvc: createMessageBranch(toolResultMsg)
+ end
+ agenticStore->>agenticStore: Create new assistantMsg for next turn
+ Note right of agenticStore: Continue loop with updated messages
+ else no tool_calls (final response)
+ agenticStore->>agenticStore: buildFinalTimings(allTurns)
+ Note right of agenticStore: Break loop, return final response
+ end
+ end
+
+ agenticStore->>agenticStore: updateSession(running: false)
+ agenticStore-->>chatStore: final content, timings, model
+ deactivate agenticStore
```
diff --git a/tools/server/webui/docs/flows/conversations-flow.md b/tools/server/webui/docs/flows/conversations-flow.md
index 185ed16e0c..bd2309bc03 100644
--- a/tools/server/webui/docs/flows/conversations-flow.md
+++ b/tools/server/webui/docs/flows/conversations-flow.md
@@ -6,7 +6,7 @@ sequenceDiagram
participant DbSvc as ⚙️ DatabaseService
participant IDB as 💾 IndexedDB
- Note over convStore: State:
conversations: DatabaseConversation[]
activeConversation: DatabaseConversation | null
activeMessages: DatabaseMessage[]
isInitialized: boolean
usedModalities: $derived({vision, audio})
+ Note over convStore: State:
conversations: DatabaseConversation[]
activeConversation: DatabaseConversation | null
activeMessages: DatabaseMessage[]
isInitialized: boolean
pendingMcpServerOverrides: Map<string, McpServerOverride>
%% ═══════════════════════════════════════════════════════════════════════════
Note over UI,IDB: 🚀 INITIALIZATION
@@ -37,6 +37,13 @@ sequenceDiagram
convStore->>convStore: conversations.unshift(conversation)
convStore->>convStore: activeConversation = $state(conversation)
convStore->>convStore: activeMessages = $state([])
+
+ alt pendingMcpServerOverrides has entries
+ loop each pending override
+ convStore->>DbSvc: Store MCP server override for new conversation
+ end
+ convStore->>convStore: clearPendingMcpServerOverrides()
+ end
deactivate convStore
%% ═══════════════════════════════════════════════════════════════════════════
@@ -58,8 +65,7 @@ sequenceDiagram
Note right of convStore: Filter to show only current branch path
convStore->>convStore: activeMessages = $state(filtered)
- convStore->>chatStore: syncLoadingStateForChat(convId)
- Note right of chatStore: Sync isLoading/currentResponse if streaming
+ Note right of convStore: Route (+page.svelte) then calls:
chatStore.syncLoadingStateForChat(convId)
deactivate convStore
%% ═══════════════════════════════════════════════════════════════════════════
@@ -121,16 +127,36 @@ sequenceDiagram
end
deactivate convStore
+ UI->>convStore: deleteAll()
+ activate convStore
+ convStore->>DbSvc: Delete all conversations and messages
+ convStore->>convStore: conversations = []
+ convStore->>convStore: clearActiveConversation()
+ deactivate convStore
+
%% ═══════════════════════════════════════════════════════════════════════════
- Note over UI,IDB: 📊 MODALITY TRACKING
+ Note over UI,IDB: � MCP SERVER PER-CHAT OVERRIDES
%% ═══════════════════════════════════════════════════════════════════════════
- Note over convStore: usedModalities = $derived.by(() => {
calculateModalitiesFromMessages(activeMessages)
})
+ Note over convStore: Conversations can override which MCP servers are enabled.
+ Note over convStore: Uses pendingMcpServerOverrides before conversation
is created, then persists to conversation metadata.
- Note over convStore: Scans activeMessages for attachments:
- IMAGE → vision: true
- PDF (processedAsImages) → vision: true
- AUDIO → audio: true
+ UI->>convStore: setMcpServerOverride(convId, serverName, override)
+ Note right of convStore: override = {enabled: boolean}
- UI->>convStore: getModalitiesUpToMessage(msgId)
- Note right of convStore: Used for regeneration validation
Only checks messages BEFORE target
+ UI->>convStore: toggleMcpServerForChat(convId, serverName, enabled)
+ activate convStore
+ convStore->>convStore: setMcpServerOverride(convId, serverName, {enabled})
+ deactivate convStore
+
+ UI->>convStore: isMcpServerEnabledForChat(convId, serverName)
+ Note right of convStore: Check override → fall back to global MCP config
+
+ UI->>convStore: getAllMcpServerOverrides(convId)
+ Note right of convStore: Returns all overrides for a conversation
+
+ UI->>convStore: removeMcpServerOverride(convId, serverName)
+ UI->>convStore: getMcpServerOverride(convId, serverName)
%% ═══════════════════════════════════════════════════════════════════════════
Note over UI,IDB: 📤 EXPORT / 📥 IMPORT
@@ -148,8 +174,10 @@ sequenceDiagram
UI->>convStore: importConversations(file)
activate convStore
convStore->>convStore: Parse JSON file
+ convStore->>convStore: importConversationsData(parsed)
convStore->>DbSvc: importConversations(parsed)
- DbSvc->>IDB: Bulk INSERT conversations + messages
+ Note right of DbSvc: Skips duplicate conversations
(checks existing by ID)
+ DbSvc->>IDB: INSERT conversations + messages (skip existing)
convStore->>convStore: loadConversations()
deactivate convStore
```
diff --git a/tools/server/webui/docs/flows/database-flow.md b/tools/server/webui/docs/flows/database-flow.md
index 50f8284e3c..38cd6941cf 100644
--- a/tools/server/webui/docs/flows/database-flow.md
+++ b/tools/server/webui/docs/flows/database-flow.md
@@ -66,6 +66,14 @@ sequenceDiagram
DbSvc-->>Store: rootMessageId
deactivate DbSvc
+ Store->>DbSvc: createSystemMessage(convId, content, parentId)
+ activate DbSvc
+ DbSvc->>DbSvc: Create message {role: "system", parent: parentId}
+ DbSvc->>Dexie: db.messages.add(systemMsg)
+ Dexie->>IDB: INSERT
+ DbSvc-->>Store: DatabaseMessage
+ deactivate DbSvc
+
Store->>DbSvc: createMessageBranch(message, parentId)
activate DbSvc
DbSvc->>DbSvc: Generate UUID for new message
@@ -116,6 +124,13 @@ sequenceDiagram
end
DbSvc->>Dexie: db.messages.delete(msgId)
Dexie->>IDB: DELETE target message
+
+ alt target message has a parent
+ DbSvc->>Dexie: db.messages.get(parentId)
+ DbSvc->>DbSvc: parent.children.filter(id !== msgId)
+ DbSvc->>Dexie: db.messages.update(parentId, {children})
+ Note right of DbSvc: Remove deleted message from parent's children[]
+ end
deactivate DbSvc
%% ═══════════════════════════════════════════════════════════════════════════
@@ -125,12 +140,16 @@ sequenceDiagram
Store->>DbSvc: importConversations(data)
activate DbSvc
loop each conversation in data
- DbSvc->>DbSvc: Generate new UUIDs (avoid conflicts)
- DbSvc->>Dexie: db.conversations.add(conversation)
- Dexie->>IDB: INSERT conversation
- loop each message
- DbSvc->>Dexie: db.messages.add(message)
- Dexie->>IDB: INSERT message
+ DbSvc->>Dexie: db.conversations.get(conv.id)
+ alt conversation already exists
+ Note right of DbSvc: Skip duplicate (keep existing)
+ else conversation is new
+ DbSvc->>Dexie: db.conversations.add(conversation)
+ Dexie->>IDB: INSERT conversation
+ loop each message
+ DbSvc->>Dexie: db.messages.add(message)
+ Dexie->>IDB: INSERT message
+ end
end
end
deactivate DbSvc
diff --git a/tools/server/webui/docs/flows/mcp-flow.md b/tools/server/webui/docs/flows/mcp-flow.md
new file mode 100644
index 0000000000..ee08419e7e
--- /dev/null
+++ b/tools/server/webui/docs/flows/mcp-flow.md
@@ -0,0 +1,226 @@
+```mermaid
+sequenceDiagram
+ participant UI as 🧩 McpServersSettings / ChatForm
+ participant chatStore as 🗄️ chatStore
+ participant mcpStore as 🗄️ mcpStore
+ participant mcpResStore as 🗄️ mcpResourceStore
+ participant convStore as 🗄️ conversationsStore
+ participant MCPSvc as ⚙️ MCPService
+ participant LS as 💾 LocalStorage
+ participant ExtMCP as 🔌 External MCP Server
+
+ Note over mcpStore: State:
isInitializing, error
toolCount, connectedServers
healthChecks (Map)
connections (Map)
toolsIndex (Map)
serverConfigs (Map)
+
+ Note over mcpResStore: State:
serverResources (Map)
cachedResources (Map)
subscriptions (Map)
attachments[]
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: 🚀 INITIALIZATION (App Startup)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>mcpStore: ensureInitialized()
+ activate mcpStore
+
+ mcpStore->>LS: get(MCP_SERVERS_LOCALSTORAGE_KEY)
+ LS-->>mcpStore: MCPServerSettingsEntry[]
+
+ mcpStore->>mcpStore: parseServerSettings(servers)
+ Note right of mcpStore: Filter enabled servers
Build MCPServerConfig objects
Per-chat overrides checked via convStore
+
+ loop For each enabled server
+ mcpStore->>mcpStore: runHealthCheck(serverId)
+ mcpStore->>mcpStore: updateHealthCheck(id, CONNECTING)
+
+ mcpStore->>MCPSvc: connect(serverName, config, clientInfo, capabilities, onPhase)
+ activate MCPSvc
+
+ MCPSvc->>MCPSvc: createTransport(config)
+ Note right of MCPSvc: WebSocket / StreamableHTTP / SSE
with optional CORS proxy
+
+ MCPSvc->>ExtMCP: Transport handshake
+ ExtMCP-->>MCPSvc: Connection established
+
+ MCPSvc->>ExtMCP: Initialize request
+ Note right of ExtMCP: Exchange capabilities
Server info, protocol version
+
+ ExtMCP-->>MCPSvc: InitializeResult (serverInfo, capabilities)
+
+ MCPSvc->>ExtMCP: listTools()
+ ExtMCP-->>MCPSvc: Tool[]
+
+ MCPSvc-->>mcpStore: MCPConnection
+ deactivate MCPSvc
+
+ mcpStore->>mcpStore: connections.set(serverName, connection)
+ mcpStore->>mcpStore: indexTools(connection.tools, serverName)
+ Note right of mcpStore: toolsIndex.set(toolName, serverName)
Handle name conflicts with prefixes
+
+ mcpStore->>mcpStore: updateHealthCheck(id, SUCCESS)
+ mcpStore->>mcpStore: _connectedServers.push(serverName)
+
+ alt Server supports resources
+ mcpStore->>MCPSvc: listAllResources(connection)
+ MCPSvc->>ExtMCP: listResources()
+ ExtMCP-->>MCPSvc: MCPResource[]
+ MCPSvc-->>mcpStore: resources
+
+ mcpStore->>MCPSvc: listAllResourceTemplates(connection)
+ MCPSvc->>ExtMCP: listResourceTemplates()
+ ExtMCP-->>MCPSvc: MCPResourceTemplate[]
+ MCPSvc-->>mcpStore: templates
+
+ mcpStore->>mcpResStore: setServerResources(serverName, resources, templates)
+ end
+ end
+
+ mcpStore->>mcpStore: _isInitializing = false
+ deactivate mcpStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: 🔧 TOOL EXECUTION (Chat with Tools)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>mcpStore: executeTool(mcpCall: MCPToolCall, signal?)
+ activate mcpStore
+
+ mcpStore->>mcpStore: toolsIndex.get(mcpCall.function.name)
+ Note right of mcpStore: Resolve serverName from toolsIndex
MCPToolCall = {id, type, function: {name, arguments}}
+
+ mcpStore->>mcpStore: acquireConnection()
+ Note right of mcpStore: activeFlowCount++
Prevent shutdown during execution
+
+ mcpStore->>mcpStore: connection = connections.get(serverName)
+
+ mcpStore->>MCPSvc: callTool(connection, {name, arguments}, signal)
+ activate MCPSvc
+
+ MCPSvc->>MCPSvc: throwIfAborted(signal)
+ MCPSvc->>ExtMCP: callTool(name, arguments)
+
+ alt Tool execution success
+ ExtMCP-->>MCPSvc: ToolCallResult (content, isError)
+ MCPSvc->>MCPSvc: formatToolResult(result)
+ Note right of MCPSvc: Handle text, image (base64),
embedded resource content
+ MCPSvc-->>mcpStore: ToolExecutionResult
+ else Tool execution error
+ ExtMCP-->>MCPSvc: Error
+ MCPSvc-->>mcpStore: throw Error
+ else Aborted
+ MCPSvc-->>mcpStore: throw AbortError
+ end
+
+ deactivate MCPSvc
+
+ mcpStore->>mcpStore: releaseConnection()
+ Note right of mcpStore: activeFlowCount--
+
+ mcpStore-->>UI: ToolExecutionResult
+ deactivate mcpStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: � RESOURCE ATTACHMENT CONSUMPTION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ chatStore->>mcpStore: consumeResourceAttachmentsAsExtras()
+ activate mcpStore
+ mcpStore->>mcpResStore: getAttachments()
+ mcpResStore-->>mcpStore: MCPResourceAttachment[]
+ mcpStore->>mcpStore: Convert attachments to message extras
+ mcpStore->>mcpResStore: clearAttachments()
+ mcpStore-->>chatStore: MessageExtra[] (for user message)
+ deactivate mcpStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: �📝 PROMPT OPERATIONS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>mcpStore: getAllPrompts()
+ activate mcpStore
+
+ loop For each connected server with prompts capability
+ mcpStore->>MCPSvc: listPrompts(connection)
+ MCPSvc->>ExtMCP: listPrompts()
+ ExtMCP-->>MCPSvc: Prompt[]
+ MCPSvc-->>mcpStore: prompts
+ end
+
+ mcpStore-->>UI: MCPPromptInfo[] (with serverName)
+ deactivate mcpStore
+
+ UI->>mcpStore: getPrompt(serverName, promptName, args?)
+ activate mcpStore
+
+ mcpStore->>MCPSvc: getPrompt(connection, name, args)
+ MCPSvc->>ExtMCP: getPrompt({name, arguments})
+ ExtMCP-->>MCPSvc: GetPromptResult (messages)
+ MCPSvc-->>mcpStore: GetPromptResult
+
+ mcpStore-->>UI: GetPromptResult
+ deactivate mcpStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: 📁 RESOURCE OPERATIONS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>mcpResStore: addAttachment(resourceInfo)
+ activate mcpResStore
+ mcpResStore->>mcpResStore: Create MCPResourceAttachment (loading: true)
+ mcpResStore-->>UI: attachment
+
+ UI->>mcpStore: readResource(serverName, uri)
+ activate mcpStore
+
+ mcpStore->>MCPSvc: readResource(connection, uri)
+ MCPSvc->>ExtMCP: readResource({uri})
+ ExtMCP-->>MCPSvc: MCPReadResourceResult (contents)
+ MCPSvc-->>mcpStore: contents
+
+ mcpStore-->>UI: MCPResourceContent[]
+ deactivate mcpStore
+
+ UI->>mcpResStore: updateAttachmentContent(attachmentId, content)
+ mcpResStore->>mcpResStore: cacheResourceContent(resource, content)
+ deactivate mcpResStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: 🔄 AUTO-RECONNECTION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over mcpStore: On WebSocket close or connection error:
+ mcpStore->>mcpStore: autoReconnect(serverName, attempt)
+ activate mcpStore
+
+ mcpStore->>mcpStore: Calculate backoff delay
+ Note right of mcpStore: delay = min(30s, 1s * 2^attempt)
+
+ mcpStore->>mcpStore: Wait for delay
+ mcpStore->>mcpStore: reconnectServer(serverName)
+
+ alt Reconnection success
+ mcpStore->>mcpStore: updateHealthCheck(id, SUCCESS)
+ else Max attempts reached
+ mcpStore->>mcpStore: updateHealthCheck(id, ERROR)
+ end
+ deactivate mcpStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,ExtMCP: 🛑 SHUTDOWN
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>mcpStore: shutdown()
+ activate mcpStore
+
+ mcpStore->>mcpStore: Wait for activeFlowCount == 0
+
+ loop For each connection
+ mcpStore->>MCPSvc: disconnect(connection)
+ MCPSvc->>MCPSvc: transport.onclose = undefined
+ MCPSvc->>ExtMCP: close()
+ end
+
+ mcpStore->>mcpStore: connections.clear()
+ mcpStore->>mcpStore: toolsIndex.clear()
+ mcpStore->>mcpStore: _connectedServers = []
+
+ mcpStore->>mcpResStore: clear()
+ deactivate mcpStore
+```
diff --git a/tools/server/webui/docs/flows/settings-flow.md b/tools/server/webui/docs/flows/settings-flow.md
index 474aef01b0..40ad3bd94d 100644
--- a/tools/server/webui/docs/flows/settings-flow.md
+++ b/tools/server/webui/docs/flows/settings-flow.md
@@ -49,14 +49,20 @@ sequenceDiagram
settingsStore->>serverStore: defaultParams
serverStore-->>settingsStore: {temperature, top_p, top_k, ...}
- settingsStore->>ParamSvc: extractServerDefaults(defaultParams)
- ParamSvc-->>settingsStore: Record
+ loop each SYNCABLE_PARAMETER
+ alt key NOT in userOverrides
+ settingsStore->>settingsStore: config[key] = serverDefault[key]
+ Note right of settingsStore: Non-overridden params adopt server default
+ else key in userOverrides
+ Note right of settingsStore: Keep user value, skip server default
+ end
+ end
- settingsStore->>ParamSvc: mergeWithServerDefaults(config, serverDefaults)
- Note right of ParamSvc: For each syncable parameter:
- If NOT in userOverrides → use server default
- If in userOverrides → keep user value
- ParamSvc-->>settingsStore: mergedConfig
+ alt serverStore.props has webuiSettings
+ settingsStore->>settingsStore: Apply webuiSettings from server
+ Note right of settingsStore: Server-provided UI settings
(e.g. showRawOutputSwitch)
+ end
- settingsStore->>settingsStore: config = mergedConfig
settingsStore->>settingsStore: saveConfig()
deactivate settingsStore
@@ -67,11 +73,18 @@ sequenceDiagram
UI->>settingsStore: updateConfig(key, value)
activate settingsStore
settingsStore->>settingsStore: config[key] = value
- settingsStore->>settingsStore: userOverrides.add(key)
- Note right of settingsStore: Mark as user-modified (won't be overwritten by server)
+
+ alt value matches server default for key
+ settingsStore->>settingsStore: userOverrides.delete(key)
+ Note right of settingsStore: Matches server default, remove override
+ else value differs from server default
+ settingsStore->>settingsStore: userOverrides.add(key)
+ Note right of settingsStore: Mark as user-modified (won't be overwritten)
+ end
+
settingsStore->>settingsStore: saveConfig()
- settingsStore->>LS: set("llama-config", config)
- settingsStore->>LS: set("llama-userOverrides", [...userOverrides])
+ settingsStore->>LS: set(CONFIG_LOCALSTORAGE_KEY, config)
+ settingsStore->>LS: set(USER_OVERRIDES_LOCALSTORAGE_KEY, [...userOverrides])
deactivate settingsStore
UI->>settingsStore: updateMultipleConfig({key1: val1, key2: val2})
@@ -88,10 +101,9 @@ sequenceDiagram
UI->>settingsStore: resetConfig()
activate settingsStore
- settingsStore->>settingsStore: config = SETTING_CONFIG_DEFAULT
+ settingsStore->>settingsStore: config = {...SETTING_CONFIG_DEFAULT}
settingsStore->>settingsStore: userOverrides.clear()
- settingsStore->>settingsStore: syncWithServerDefaults()
- Note right of settingsStore: Apply server defaults for syncable params
+ Note right of settingsStore: All params reset to defaults
Next syncWithServerDefaults will adopt server values
settingsStore->>settingsStore: saveConfig()
deactivate settingsStore