# 基础设施设计 — 状态管理 ## 文档元数据 - 项目名称: free-code - 文档类型: 基础设施设计 - 原始代码来源: `../../src/state/`(5个文件) - 原始设计意图: 用不可变全局状态串联 MCP、插件、通知、后台任务、桥接与推理状态,并通过事件驱动方式向 UI 广播更新 - 交叉引用: [基础设施设计总览](基础设施设计.md) | [核心模块设计-查询引擎](../核心模块设计/核心模块设计-查询引擎-QueryEngine.md) ## 设计目标 状态管理层负责承载应用运行态、MCP 状态、插件状态、通知状态与推理/猜测状态,并通过不可变更新维持可预测性。它是基础设施层的共享事实来源,供 UI、协议层和后台任务共同读取。 ## 14.1 AppState 不可变 Record ```csharp /// /// 应用全局状态 — 不可变 record /// 对应原始 AppState 类型 (AppStateStore.ts 中 ~450 行类型定义) /// public sealed record AppState { // === 核心状态 === public SettingsJson Settings { get; init; } = new(); public bool Verbose { get; init; } public ModelSetting MainLoopModel { get; init; } public ModelSetting MainLoopModelForSession { get; init; } public string? StatusLineText { get; init; } public ExpandedView ExpandedView { get; init; } = ExpandedView.None; public bool IsBriefOnly { get; init; } // === 权限 === public ToolPermissionContext ToolPermissionContext { get; init; } = new(); public string? Agent { get; init; } public bool KairosEnabled { get; init; } // === UI 状态 === public int SelectedIPAgentIndex { get; init; } = -1; public int CoordinatorTaskIndex { get; init; } = -1; public ViewSelectionMode ViewSelectionMode { get; init; } = ViewSelectionMode.None; public FooterItem? FooterSelection { get; init; } // === 后台任务 === public IReadOnlyDictionary Tasks { get; init; } = new Dictionary(); public string? ForegroundedTaskId { get; init; } public string? ViewingAgentTaskId { get; init; } public IReadOnlyDictionary AgentNameRegistry { get; init; } = new Dictionary(); // === MCP === public McpState Mcp { get; init; } = new(); // === 插件 === public PluginState Plugins { get; init; } = new(); // === 远程/桥接 === public string? RemoteSessionUrl { get; init; } public RemoteConnectionStatus RemoteConnectionStatus { get; init; } = RemoteConnectionStatus.Connecting; public int RemoteBackgroundTaskCount { get; init; } // === 桥接 === public bool ReplBridgeEnabled { get; init; } public bool ReplBridgeExplicit { get; init; } public bool ReplBridgeOutboundOnly { get; init; } public bool ReplBridgeConnected { get; init; } public bool ReplBridgeSessionActive { get; init; } public bool ReplBridgeReconnecting { get; init; } public string? ReplBridgeConnectUrl { get; init; } public string? ReplBridgeSessionUrl { get; init; } public string? ReplBridgeEnvironmentId { get; init; } public string? ReplBridgeSessionId { get; init; } public string? ReplBridgeError { get; init; } public string? ReplBridgeInitialName { get; init; } public bool ShowRemoteCallout { get; init; } // === 同伴 === public string? CompanionReaction { get; init; } public long? CompanionPetAt { get; init; } // === 通知 === public NotificationState Notifications { get; init; } = new(); // === 记忆 === public AgentDefinitionsResult AgentDefinitions { get; init; } = new(); // === 文件历史 === public FileHistoryState FileHistory { get; init; } = new(); // === 归属 === public AttributionState Attribution { get; init; } = new(); // === TODO === public IReadOnlyDictionary Todos { get; init; } = new Dictionary(); // === 推测 === public SpeculationState Speculation { get; init; } = SpeculationState.Idle; public long SpeculationSessionTimeSavedMs { get; init; } // === 其他 === public bool? ThinkingEnabled { get; init; } public bool PromptSuggestionEnabled { get; init; } public EffortValue? EffortValue { get; init; } public bool FastMode { get; init; } public int AuthVersion { get; init; } public IReadOnlySet ActiveOverlays { get; init; } = new HashSet(); } // === 嵌套状态类型 === public sealed record McpState { public IReadOnlyList Clients { get; init; } = []; public IReadOnlyList Tools { get; init; } = []; public IReadOnlyList Commands { get; init; } = []; public IReadOnlyDictionary> Resources { get; init; } = new Dictionary>(); public int PluginReconnectKey { get; init; } } public sealed record PluginState { public IReadOnlyList Enabled { get; init; } = []; public IReadOnlyList Disabled { get; init; } = []; public IReadOnlyList Commands { get; init; } = []; public IReadOnlyList Errors { get; init; } = []; public PluginInstallationStatus InstallationStatus { get; init; } = new(); public bool NeedsRefresh { get; init; } } public sealed record NotificationState { public Notification? Current { get; init; } public IReadOnlyList Queue { get; init; } = []; } public record SpeculationState { public static readonly SpeculationState Idle = new() { Status = "idle" }; public string Status { get; init; } = "idle"; } ``` ## 14.2 IAppStateStore 接口与 AppStateStore 实现 ```csharp /// /// 应用状态存储 — 事件驱动不可变状态管理 /// 对应原始 Store + AppStateStore /// 模式: Redux/Elm 风格 (updater 函数 + 变更通知) /// public interface IAppStateStore { /// 获取当前状态(不可变快照) AppState GetState(); /// 更新状态(通过 updater 函数) void Update(Func updater); /// 订阅状态变更 IDisposable Subscribe(Action listener); /// 状态变更事件 (C# event 模式) event EventHandler? StateChanged; } public sealed class AppStateStore : IAppStateStore { private AppState _state; private readonly object _lock = new(); private readonly List> _listeners = new(); private readonly Action? _onChangeCallback; public event EventHandler? StateChanged; public AppStateStore() { _state = CreateDefaultState(); } public AppState GetState() { lock (_lock) return _state; } public void Update(Func updater) { AppState prev, next; lock (_lock) { prev = _state; next = updater(prev); if (ReferenceEquals(next, prev)) return; // 无变更 _state = next; } // 通知变更 _onChangeCallback?.Invoke(next, prev); StateChanged?.Invoke(this, new StateChangedEventArgs(prev, next)); // 通知订阅者 foreach (var listener in _listeners.ToArray()) listener(next); } public IDisposable Subscribe(Action listener) { lock (_lock) _listeners.Add(listener); return new Subscription(() => { lock (_lock) _listeners.Remove(listener); }); } private static AppState CreateDefaultState() => new AppState(); private sealed class Subscription : IDisposable { private readonly Action _unsubscribe; public Subscription(Action unsubscribe) => _unsubscribe = unsubscribe; public void Dispose() => _unsubscribe(); } } public sealed record StateChangedEventArgs(AppState OldState, AppState NewState); ``` ## 14.3 StateSelectors ```csharp /// /// 状态选择器 — 对应原始 selectors.ts /// 从 AppState 中派生计算值,避免在组件中重复逻辑 /// public static class StateSelectors { public static bool IsBridgeReady(this AppState state) => state.ReplBridgeEnabled && state.ReplBridgeConnected; public static bool IsBridgeConnected(this AppState state) => state.ReplBridgeEnabled && state.ReplBridgeSessionActive; public static IReadOnlyList GetBackgroundTasks(this AppState state) => state.Tasks.Values.Where(t => t.Status is TaskStatus.Running or TaskStatus.Pending) .Where(t => t.IsBackgrounded) .ToList(); public static bool HasActiveMcpServers(this AppState state) => state.Mcp.Clients.Any(c => c.IsConnected); public static int GetActiveSessionCount(this AppState state) => state.Tasks.Values.Count(t => t.Status == TaskStatus.Running); public static string? GetActiveModel(this AppState state) => state.MainLoopModelForSession ?? state.MainLoopModel; } ```