namespace AxCopilot.Services; /// /// 장시간 실행되거나 메인 응답 흐름과 분리된 작업을 앱 전역에서 추적합니다. /// 현재는 서브에이전트/분리 작업을 우선 배경 작업으로 취급합니다. /// public sealed class BackgroundJobService { public sealed class BackgroundJobState { public string Id { get; set; } = ""; public string Kind { get; set; } = ""; public string Title { get; set; } = ""; public string Summary { get; set; } = ""; public string Status { get; set; } = "running"; public DateTime StartedAt { get; set; } = DateTime.Now; public DateTime UpdatedAt { get; set; } = DateTime.Now; } private readonly List _active = []; private readonly List _recent = []; public IReadOnlyList ActiveJobs => _active; public IReadOnlyList RecentJobs => _recent; public event Action? Changed; public void Upsert(string id, string kind, string title, string summary, string status = "running") { var existing = _active.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); if (existing == null) { _active.Insert(0, new BackgroundJobState { Id = id, Kind = kind, Title = title, Summary = summary, Status = status, StartedAt = DateTime.Now, UpdatedAt = DateTime.Now, }); } else { existing.Kind = kind; existing.Title = title; existing.Summary = summary; existing.Status = status; existing.UpdatedAt = DateTime.Now; } Changed?.Invoke(); } public void Complete(string id, string? summary = null, string status = "completed") { var existing = _active.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); if (existing == null) return; _active.Remove(existing); existing.Status = status; if (!string.IsNullOrWhiteSpace(summary)) existing.Summary = summary; existing.UpdatedAt = DateTime.Now; _recent.Insert(0, existing); if (_recent.Count > 20) _recent.RemoveRange(20, _recent.Count - 20); Changed?.Invoke(); } }