using System.ComponentModel; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using TaskStatus = ClaudeDo.Data.Models.TaskStatus; namespace ClaudeDo.Ui.Services; public interface IWorkerClient : INotifyPropertyChanged { bool IsConnected { get; } bool IsReconnecting { get; } event Action? TaskStartedEvent; event Action? TaskFinishedEvent; event Action? TaskUpdatedEvent; /// Raised once when the SignalR connection is first established, and again on every reconnect. event Action? ConnectionRestoredEvent; event Action? WorktreeUpdatedEvent; event Action? ListUpdatedEvent; event Action? TaskMessageEvent; event Action? WorkerLogReceivedEvent; /// A running task raised a question via AskUser: (taskId, questionId, question). event Action? TaskQuestionAskedEvent; /// A pending question was answered, timed out, or the run ended: (taskId, questionId). event Action? TaskQuestionResolvedEvent; /// A running list-handler session called handoff_list_handler at the end of a phase: /// (handlerTaskId, survivingTaskIds, nextPhase). The UI opens a second ConPTY tile for the same /// task to carry out nextPhase ("wait" | "merge" | "wait_final" | "merge_final"). event Action, string>? HandoffRequestedEvent; /// start_list_handler was called through the external MCP: (listId, taskIds). The UI /// starts a list-handler run for them exactly like the "Let Claude handle it" button. event Action>? ListHandlerStartRequestedEvent; /// The 5h usage window is running out under a running list-handler session: /// (handlerTaskId, message). Mission Control types the message into that task's ConPTY pane so /// the session can hand off cleanly; no pane for the task means nothing happens. event Action? HandlerUsageWarningEvent; /// An agent added a note through the MCP tool — an open notes editor reloads. event Action? NotesUpdatedEvent; event Action? PrepStartedEvent; event Action? PrepLineEvent; event Action? PrepFinishedEvent; /// (opKey, phase, current, total) — generic progress channel for long-running worker /// operations (merge phases, worktree cleanup, startup recovery, planning integration, ...). /// opKey is the TaskId for a task-bound operation, otherwise a stable string. current/total /// is meant to be shown as text, never a progress bar — most operations have no meaningful /// total. event Action? OperationProgressEvent; event Action? PlanningMergeStartedEvent; event Action? PlanningSubtaskMergedEvent; /// (planningTaskId, subtaskId, conflictedFiles, externallyDriven). externallyDriven /// is true when an MCP session (not the UI) started the unit merge — the resolver must not /// auto-open in that case. event Action, bool>? PlanningMergeConflictEvent; /// (planningTaskId, reason). reason is set when the merge stopped on a real failure /// (blocked/verify_failed/untracked_collision) rather than a deliberate abort. event Action? PlanningMergeAbortedEvent; event Action? PlanningCompletedEvent; event Action? PrimeFired; string? LastApproveTarget { get; } IReadOnlyList GetActiveTasks(); Task WakeQueueAsync(); Task RunNowAsync(string taskId); Task ContinueTaskAsync(string taskId, string followUpPrompt); /// Answer a question a running task raised via AskUser. Task AnswerTaskQuestionAsync(string taskId, string questionId, string answer); /// The question a running task is currently blocked on, if any (for re-attach). Task GetPendingQuestionAsync(string taskId); Task ResetTaskAsync(string taskId); Task CancelTaskAsync(string taskId); /// Deletes a task via the worker (mirrors the MCP delete_task tool), so a deleted /// child correctly advances a WaitingForChildren parent. Returns (false, message) instead of /// throwing when the task has children or is running, preserving the FK-error UX. Task<(bool Ok, string? Error)> DeleteTaskAsync(string taskId); Task> GetAgentsAsync(); Task RefreshAgentsAsync(); Task RestoreDefaultAgentsAsync(); Task GetListConfigAsync(string listId); Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto); /// Repo-import folders remembered for the "Add repos as lists" dialog. Throws on /// a failed hub call — the caller surfaces the failure rather than showing an empty list. Task> GetRepoImportFoldersAsync(); /// Persists the repo-import folder list. Throws on a failed hub call. Task SetRepoImportFoldersAsync(List folders); Task> GetSessionSkillsAsync(); Task> InstallSessionSkillAsync(string url); Task UpdateSessionSkillAsync(string sourceUrl); Task RemoveSessionSkillAsync(string sourceUrl); /// Returns the base-dirty heads-up when this transition just queued the task against /// a list whose working dir has uncommitted changes (null otherwise) — a new worktree forks /// from the last commit, not the working tree, so those changes won't be included. Task SetTaskStatusAsync(string taskId, TaskStatus status); /// Guarded "manual done toggle" (checks the task is currently Idle server-side /// before flipping it to Done) — replaces a raw EF write in the task-list checkbox path. Task SetTaskDoneAsync(string taskId); /// Guarded un-toggle (checks the task is currently Done server-side before /// flipping it back to Idle). Task UnsetTaskDoneAsync(string taskId); /// Guarded "remove from queue" (checks the task is currently Queued server-side /// and clears BlockedByTaskId in the same update) — call once per row (parent and each /// queued child) rather than cascading server-side. Task DequeueTaskAsync(string taskId); /// Retry flag for status "dirty_target": stash the target working /// tree's uncommitted changes before merging and restore them afterwards (git --autostash). Task ApproveReviewAsync(string taskId, string targetBranch, bool autostash = false); Task PreviewMergeAsync(string taskId, string targetBranch); Task MergeTaskAsync(string taskId, string targetBranch, bool removeWorktree, string commitMessage, bool autostash = false); Task RejectReviewToQueueAsync(string taskId, string feedback); Task RejectReviewToIdleAsync(string taskId); Task CancelReviewAsync(string taskId); // ── Conflict resolution (worker hub side implemented by Layer C) ── Task StartConflictMergeAsync(string taskId, string targetBranch); Task GetMergeConflictDocumentsAsync(string taskId); Task WriteConflictResolutionAsync(string taskId, string path, string resolvedContent); Task ContinueConflictMergeAsync(string taskId); Task AbortConflictMergeAsync(string taskId); Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default); /// Commits an interactively-worked task's worktree and moves it to WaitingForReview /// (the only path that flips a hand-driven ConPTY session into the review/merge pipeline). Task SubmitTaskForReviewAsync(string taskId, CancellationToken ct = default); /// Launch spec for an embedded ConPTY terminal to open an interactive session /// in a task's worktree (same worktree prep as an autonomous run). Task GetInteractiveLaunchSpecAsync(string taskId, CancellationToken ct = default); /// Launch spec for an ad-hoc interactive session in an arbitrary directory -- /// no task, no worktree. Task GetAdHocLaunchSpecAsync(string directory, CancellationToken ct = default); /// Launch spec for an embedded ConPTY "merge helper" session that drives the given /// tasks to a merged/Done state. listId scopes the session (and cwd) to that list. /// handlerTaskId (from CreateMergeHelperTaskAsync) is rendered into the brief so the session /// can call handoff_list_handler on its own handler task. model overrides the triage /// session's model (null = default). Task GetMergeHelperLaunchSpecAsync(IReadOnlyList taskIds, string listId, string handlerTaskId, string? model = null, CancellationToken ct = default); /// Creates the ClaudeDo task that owns a list-handler run (one per run, Idle/IsManual, /// never queued) so the ConPTY tile can be task-based instead of ad-hoc. Returns the new task id. Task CreateMergeHelperTaskAsync( IReadOnlyList taskIds, string listId, string title, string descriptionHeader, CancellationToken ct = default); /// Launch spec for the fresh ConPTY session a merge-helper run hands off to once a phase /// is done -- reuses the SAME handler task id (no new task created). nextPhase picks the next /// session's role: "wait" | "merge" | "wait_final" | "merge_final". Task GetMergeHelperHandoffLaunchSpecAsync( string taskId, IReadOnlyList survivingTaskIds, string nextPhase, CancellationToken ct = default); /// Reports that a task-based ConPTY session's process exited non-zero after it had been /// running. The exit code only exists in this process; the worker decides whether it was a /// list-handler session killed by the usage limit and reopens the same phase after the 5h reset. Task ReportInteractiveSessionExitedAsync(string taskId, int exitCode, CancellationToken ct = default); /// The handler round's still-open (Queued/Running) tasks, for the Mission Control /// "abandon run" action. Empty when the round has no entry or nothing open -- the caller then /// closes the pane without a confirmation dialog. Task> GetHandlerOpenTasksAsync(string handlerTaskId, CancellationToken ct = default); /// Starts a planning session and returns the launch spec for an embedded ConPTY /// planning terminal (replaces StartPlanningSessionAsync's external wt window). Task GetPlanningStartLaunchSpecAsync(string taskId, CancellationToken ct = default); /// Resumes a planning session and returns its embedded-ConPTY launch spec. Task GetPlanningResumeLaunchSpecAsync(string taskId, CancellationToken ct = default); Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default); Task DiscardPlanningSessionAsync(string taskId, bool dequeueQueuedChildren = false, CancellationToken ct = default); Task FinalizePlanningSessionAsync(string taskId, bool queueAgentTasks = true, CancellationToken ct = default); Task GetPendingDraftCountAsync(string taskId, CancellationToken ct = default); Task GetMergeTargetsAsync(string taskId); Task> GetPlanningAggregateAsync(string planningTaskId); Task BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch); Task ContinuePlanningMergeAsync(string planningTaskId); Task AbortPlanningMergeAsync(string planningTaskId); /// Unit merges currently paused on a conflict that an MCP session started. Called /// on (re)connect to recover the "don't auto-open" banner state after a UI restart. Task> GetActiveExternalPlanningMergeConflictsAsync(); Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default); Task GetWeekReportAsync(DateOnly start, DateOnly end); Task GenerateWeekReportAsync(DateOnly start, DateOnly end); Task RunDailyPrepNowAsync(); Task RefineTaskAsync(string taskId); event Action? RefineStartedEvent; event Action? RefineFinishedEvent; Task ClearMyDayAsync(); Task GetAppSettingsAsync(); Task UpdateAppSettingsAsync(AppSettingsDto dto); Task> GetDailyNotesAsync(DateOnly day); /// Both ends inclusive. Task> GetDailyNotesBetweenAsync(DateOnly start, DateOnly end); Task AddDailyNoteAsync(DateOnly day, string text); Task UpdateDailyNoteAsync(string id, string text); Task DeleteDailyNoteAsync(string id); Task GetLastPrepLogAsync(); Task> GetRecentLogsAsync(); Task> GetPrimeSchedulesAsync(); Task UpsertPrimeScheduleAsync(PrimeScheduleDto dto); Task DeletePrimeScheduleAsync(Guid id); Task UpdateListAsync(UpdateListDto dto); Task UpdateListConfigAsync(UpdateListConfigDto dto); Task CleanupFinishedWorktreesAsync(string? listId = null); Task ResetAllWorktreesAsync(); Task> GetWorktreesOverviewAsync(string? listId); Task<(bool Ok, string? Error)> SetWorktreeStateAsync(string taskId, WorktreeState newState); Task ForceRemoveWorktreeAsync(string taskId); Task GetClaudeBinAsync(); Task SetClaudeBinAsync(string? value); Task GetOnlineInboxStateAsync(); Task SetOnlineInboxConfigAsync(OnlineInboxConfigInputDto input); Task SetOnlineInboxAuthAsync(string refreshToken); Task ClearOnlineInboxAuthAsync(); /// Raised whenever the worker's usage poller ticks (success or failure). event Action? UsageUpdatedEvent; Task GetUsageSnapshotAsync(); /// Forces an out-of-band usage poll on the worker and returns the fresh snapshot. Task RefreshUsageAsync(); Task> GetModelUsageAsync(DateOnly from, DateOnly to); Task> GetTaskUsageAsync(DateOnly from, DateOnly to); Task GetTokenTrackerStatusAsync(); Task RefreshTokenTrackerAsync(); Task InstallTokenTrackerAsync(); /// Starts TokenTracker's local dashboard on the worker and returns its URL; opening /// the browser is the caller's job. Task OpenTokenTrackerDashboardAsync(); Task GetTicketSettingsAsync(); Task SetTicketApiBaseUrlAsync(string? baseUrl); Task SetTicketTokenAsync(string token); Task ClearTicketTokenAsync(); Task TestTicketConnectionAsync(); Task> GetTicketProjectsAsync(); /// Imports open tickets assigned to the token owner into the given list. Throws /// HubException (with a user-readable message) on failure so the caller can surface it via /// the footer error strip. Task ImportTicketsAsync(string listId); }