feat(ui): mission-control pane title shows usage limit reset time

When a list-handler handoff is deferred waiting for the 5h usage limit to
reset, the Mission Control pane title is augmented with ⏳ waiting for
limit reset (HH:mm) so the wait is visible at a glance. Extends
HubBroadcaster.HandoffRequested with an optional deferredUntil DateTime
parameter, wired through SignalR to MissionControlViewModel, which formats
it into the pane title via Loc.T(missionControl.waitingForLimitReset).
This commit is contained in:
mika kuns
2026-09-10 13:18:09 +02:00
parent 1177463a82
commit 5be972564a
9 changed files with 77 additions and 9 deletions
@@ -26,9 +26,10 @@ public interface IWorkerClient : INotifyPropertyChanged
event Action<string, string>? TaskQuestionResolvedEvent;
/// <summary>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").</summary>
event Action<string, IReadOnlyList<string>, string>? HandoffRequestedEvent;
/// (handlerTaskId, survivingTaskIds, nextPhase, deferredUntil). The UI opens a second ConPTY tile
/// for the same task to carry out nextPhase ("wait" | "merge" | "wait_final" | "merge_final").
/// deferredUntil hints when the session is suspended waiting for the usage limit to reset.</summary>
event Action<string, IReadOnlyList<string>, string, DateTime?>? HandoffRequestedEvent;
/// <summary>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.</summary>
@@ -37,6 +38,9 @@ public interface IWorkerClient : INotifyPropertyChanged
/// (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.</summary>
event Action<string, string>? HandlerUsageWarningEvent;
/// <summary>A list-handler handoff is deferred because the 5h usage window is exhausted:
/// (handlerTaskId, dueAt). Mission Control updates the pane title to show the reset time.</summary>
event Action<string, DateTimeOffset>? HandlerHandoffDeferredEvent;
/// <summary>An agent added a note through the MCP tool — an open notes editor reloads.</summary>
event Action? NotesUpdatedEvent;
+9 -3
View File
@@ -48,9 +48,10 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
public event Action<string>? TaskUpdatedEvent;
public event Action<string, string, string>? TaskQuestionAskedEvent;
public event Action<string, string>? TaskQuestionResolvedEvent;
public event Action<string, IReadOnlyList<string>, string>? HandoffRequestedEvent;
public event Action<string, IReadOnlyList<string>, string, DateTime?>? HandoffRequestedEvent;
public event Action<string, IReadOnlyList<string>>? ListHandlerStartRequestedEvent;
public event Action<string, string>? HandlerUsageWarningEvent;
public event Action<string, DateTimeOffset>? HandlerHandoffDeferredEvent;
public event Action? ConnectionRestoredEvent;
public event Action<string>? WorktreeUpdatedEvent;
public event Action<string>? ListUpdatedEvent;
@@ -156,9 +157,9 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
Dispatcher.UIThread.Post(() => TaskQuestionResolvedEvent?.Invoke(taskId, questionId));
});
_hub.On<string, IReadOnlyList<string>, string>("HandoffRequested", (taskId, survivingTaskIds, nextPhase) =>
_hub.On<string, IReadOnlyList<string>, string, DateTime?>("HandoffRequested", (taskId, survivingTaskIds, nextPhase, deferredUntil) =>
{
Dispatcher.UIThread.Post(() => HandoffRequestedEvent?.Invoke(taskId, survivingTaskIds, nextPhase));
Dispatcher.UIThread.Post(() => HandoffRequestedEvent?.Invoke(taskId, survivingTaskIds, nextPhase, deferredUntil));
});
_hub.On<string, IReadOnlyList<string>>("ListHandlerStartRequested", (listId, taskIds) =>
@@ -171,6 +172,11 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
Dispatcher.UIThread.Post(() => HandlerUsageWarningEvent?.Invoke(taskId, message));
});
_hub.On<string, DateTimeOffset>("HandlerHandoffDeferred", (taskId, dueAt) =>
{
Dispatcher.UIThread.Post(() => HandlerHandoffDeferredEvent?.Invoke(taskId, dueAt));
});
_hub.On<string>("WorktreeUpdated", taskId =>
{
Dispatcher.UIThread.Post(() => WorktreeUpdatedEvent?.Invoke(taskId));