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