# 基础设施设计 — 状态管理
## 文档元数据
- 项目名称: 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;
}
```