# 基础设施设计 — 后台任务管理 ## 文档元数据 - 项目名称: free-code - 文档类型: 基础设施设计 - 原始代码来源: `../../src/tasks/`(10个文件) - 原始设计意图: 将 Shell、Agent、远程会话、工作流、监控与记忆合并任务统一纳入后台调度和状态同步框架 - 交叉引用: [基础设施设计总览](基础设施设计.md) | [服务子系统设计-会话记忆](../服务子系统设计/服务子系统设计-会话记忆与上下文.md) ## 设计目标 后台任务管理层统一承载本地 shell、agent、远程 agent、监控与工作流型任务,并保证可调度、可观测与可取消。它必须和全局状态存储、查询引擎、桥接层协同工作,且在宿主关闭时能优雅终止。 ## 13.1 BackgroundTask 任务层次 ```csharp /// /// 后台任务基类 — 所有任务类型的公共抽象 /// 对应原始 types.ts 中的 TaskState 联合类型 /// public abstract record BackgroundTask { public required string TaskId { get; init; } public abstract BackgroundTaskType TaskType { get; } public TaskStatus Status { get; set; } = TaskStatus.Pending; public DateTime? StartedAt { get; set; } public DateTime? CompletedAt { get; set; } public string? ErrorMessage { get; set; } public bool IsBackgrounded { get; set; } = true; } public enum BackgroundTaskType { LocalShell, LocalAgent, RemoteAgent, InProcessTeammate, LocalWorkflow, MonitorMcp, Dream } public enum TaskStatus { Pending, Running, Completed, Failed, Stopped } // === 具体任务类型 === /// /// 本地 Shell 任务 — 后台 bash 命令 /// 对应原始 LocalShellTask /// public sealed record LocalShellTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.LocalShell; public required string Command { get; init; } public ProcessStartInfo? ProcessStartInfo { get; init; } public string? Stdout { get; set; } public string? Stderr { get; set; } public int? ExitCode { get; set; } } /// /// 本地 Agent 任务 — 子代理(forked process 或 worktree 隔离) /// 对应原始 LocalAgentTask /// public sealed record LocalAgentTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.LocalAgent; public required string Prompt { get; init; } public string? Model { get; init; } public string? AgentType { get; init; } // explore, librarian, oracle, metis, momus public string? WorkingDirectory { get; init; } public List Messages { get; } = new(); } /// /// 远程 Agent 任务 — claude.ai 上的远程会话 /// 对应原始 RemoteAgentTask /// public sealed record RemoteAgentTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.RemoteAgent; public required string SessionUrl { get; init; } public string? Plan { get; set; } public string? Status { get; set; } } /// /// 进程内 Teammate 任务 — 协作代理 /// 对应原始 InProcessTeammateTask /// public sealed record InProcessTeammateTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.InProcessTeammate; public required string AgentName { get; init; } public string? AgentType { get; init; } public string? Color { get; init; } public required string WorkingDirectory { get; init; } } /// /// 本地工作流任务 — 多步骤工作流 /// 对应原始 LocalWorkflowTask /// public sealed record LocalWorkflowTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.LocalWorkflow; public required string WorkflowName { get; init; } public required List Steps { get; init; } public int CurrentStepIndex { get; set; } } /// /// MCP SSE 监控任务 — 监控 MCP 服务器连接状态 /// 对应原始 MonitorMcpTask /// public sealed record MonitorMcpTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.MonitorMcp; public required string ServerName { get; init; } public int ReconnectAttempt { get; set; } } /// /// Dream 任务 — 后台记忆合并 /// 对应原始 DreamTask /// public sealed record DreamTask : BackgroundTask { public override BackgroundTaskType TaskType => BackgroundTaskType.Dream; public required string TriggerReason { get; init; } // time | session_count } ``` ## 13.2 IBackgroundTaskManager 接口 ```csharp /// /// 后台任务管理器 — Channel-based 任务调度 /// 对应原始 task 工具 + 后台任务 UI 指示器 /// public interface IBackgroundTaskManager { /// 创建 Shell 任务 Task CreateShellTaskAsync(string command, ProcessStartInfo psi); /// 创建 Agent 任务 Task CreateAgentTaskAsync(string prompt, string? agentType, string? model); /// 创建远程 Agent 任务 Task CreateRemoteAgentTaskAsync(string sessionUrl); /// 创建 Dream 任务 Task CreateDreamTaskAsync(string triggerReason); /// 停止任务 Task StopTaskAsync(string taskId); /// 获取任务输出 Task GetTaskOutputAsync(string taskId); /// 列出所有任务 IReadOnlyList ListTasks(); /// 获取指定任务 BackgroundTask? GetTask(string taskId); /// 任务状态变更事件 event EventHandler? TaskStateChanged; } ``` ## 13.3 BackgroundTaskManager 实现 ```csharp public class BackgroundTaskManager : IBackgroundTaskManager, IHostedService { private readonly Channel _taskChannel = Channel.CreateUnbounded(); private readonly ConcurrentDictionary _tasks = new(); private readonly IAppStateStore _stateStore; private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly CancellationTokenSource _shutdownCts = new(); public event EventHandler? TaskStateChanged; public async Task StartAsync(CancellationToken ct) { // 启动任务调度循环 _ = Task.Run(() => DispatchLoopAsync(_shutdownCts.Token), _shutdownCts.Token); await Task.CompletedTask; } /// /// 任务调度循环 — 从 channel 读取任务并执行 /// private async Task DispatchLoopAsync(CancellationToken ct) { await foreach (var task in _taskChannel.Reader.ReadAllAsync(ct)) { _ = Task.Run(async () => { task.Status = TaskStatus.Running; task.StartedAt = DateTime.UtcNow; UpdateState(); TaskStateChanged?.Invoke(this, new(task.TaskId, task.Status)); try { await ExecuteTaskAsync(task, ct); task.Status = TaskStatus.Completed; } catch (OperationCanceledException) { task.Status = TaskStatus.Stopped; } catch (Exception ex) { task.Status = TaskStatus.Failed; task.ErrorMessage = ex.Message; _logger.LogError(ex, "Task {TaskId} failed", task.TaskId); } finally { task.CompletedAt = DateTime.UtcNow; UpdateState(); TaskStateChanged?.Invoke(this, new(task.TaskId, task.Status)); } }, ct); } } private Task ExecuteTaskAsync(BackgroundTask task, CancellationToken ct) => task switch { LocalShellTask shell => ExecuteShellTaskAsync(shell, ct), LocalAgentTask agent => ExecuteAgentTaskAsync(agent, ct), RemoteAgentTask remote => ExecuteRemoteAgentTaskAsync(remote, ct), DreamTask dream => ExecuteDreamTaskAsync(dream, ct), MonitorMcpTask monitor => ExecuteMonitorTaskAsync(monitor, ct), LocalWorkflowTask workflow => ExecuteWorkflowTaskAsync(workflow, ct), InProcessTeammateTask teammate => ExecuteTeammateTaskAsync(teammate, ct), _ => Task.CompletedTask }; private async Task ExecuteShellTaskAsync(LocalShellTask task, CancellationToken ct) { using var process = new Process { StartInfo = task.ProcessStartInfo! }; process.Start(); var stdout = await process.StandardOutput.ReadToEndAsync(ct); var stderr = await process.StandardError.ReadToEndAsync(ct); await process.WaitForExitAsync(ct); task.Stdout = stdout; task.Stderr = stderr; task.ExitCode = process.ExitCode; } private async Task ExecuteAgentTaskAsync(LocalAgentTask task, CancellationToken ct) { // 使用 QueryEngine 在隔离 context 中执行子代理查询 var engine = _services.GetRequiredService(); await foreach (var msg in engine.SubmitMessageAsync(task.Prompt, new(Model: task.Model), ct)) { // 收集消息到 task.Messages } } private async Task ExecuteDreamTaskAsync(DreamTask task, CancellationToken ct) { var dreamService = _services.GetRequiredService(); await dreamService.RunDreamCycleAsync(ct); } public Task CreateShellTaskAsync(string command, ProcessStartInfo psi) { var task = new LocalShellTask { TaskId = Guid.NewGuid().ToString("N")[..8], Command = command, ProcessStartInfo = psi, }; _tasks[task.TaskId] = task; _taskChannel.Writer.TryWrite(task); UpdateState(); return Task.FromResult(task); } public Task StopTaskAsync(string taskId) { if (_tasks.TryGetValue(taskId, out var task) && task.Status == TaskStatus.Running) { task.Status = TaskStatus.Stopped; // 进程级取消由具体执行器处理 } return Task.CompletedTask; } public IReadOnlyList ListTasks() => _tasks.Values.ToList(); public BackgroundTask? GetTask(string taskId) => _tasks.GetValueOrDefault(taskId); /// 更新 AppState 中的任务状态 private void UpdateState() { _stateStore.Update(state => { var tasks = state.Tasks.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); foreach (var task in _tasks.Values) tasks[task.TaskId] = task; return state with { Tasks = tasks }; }); } public async Task StopAsync(CancellationToken ct) { _shutdownCts.Cancel(); _taskChannel.Writer.TryComplete(); foreach (var task in _tasks.Values.Where(t => t.Status == TaskStatus.Running)) await StopTaskAsync(task.TaskId); } } ```