feat(ui): answer a running task's question inline in Mission Control

TaskMonitorViewModel surfaces a pending AskUser question (TaskQuestionAsked /
TaskQuestionResolved events) with an AnswerDraft + SubmitAnswerCommand that calls
the new IWorkerClient.AnswerTaskQuestionAsync; MonitorPaneView shows an accent
question banner with an input box above the terminal. Pending question is cleared
on answer/resolve/finish and re-hydrated on attach via GetPendingQuestionAsync.
en/de localization for missionControl.question.*; test fakes updated.
This commit is contained in:
Mika Kuns
2026-06-25 22:53:46 +02:00
parent c7f8280106
commit 917301d61c
10 changed files with 224 additions and 2 deletions

View File

@@ -120,4 +120,73 @@ public class TaskMonitorViewModelTests : IDisposable
vm.DetachCommand.Execute(null);
Assert.Same(vm, requested);
}
[Fact]
public void TaskQuestionAsked_SurfacesQuestion()
{
var worker = new FakeWorker();
using var vm = Build(worker);
vm.SetTaskId("t1");
worker.RaiseTaskQuestionAsked("t1", "q1", "DPAPI or plaintext?");
Assert.True(vm.HasPendingQuestion);
Assert.Equal("DPAPI or plaintext?", vm.PendingQuestion);
Assert.True(vm.SubmitAnswerCommand.CanExecute(null) is false); // no draft yet
}
[Fact]
public void TaskQuestionAsked_ForOtherTask_IsIgnored()
{
var worker = new FakeWorker();
using var vm = Build(worker);
vm.SetTaskId("t1");
worker.RaiseTaskQuestionAsked("other", "q1", "not mine");
Assert.False(vm.HasPendingQuestion);
}
[Fact]
public async Task SubmitAnswer_InvokesClient_AndClears()
{
var worker = new FakeWorker();
using var vm = Build(worker);
vm.SetTaskId("t1");
worker.RaiseTaskQuestionAsked("t1", "q1", "DPAPI or plaintext?");
vm.AnswerDraft = "DPAPI please";
Assert.True(vm.SubmitAnswerCommand.CanExecute(null));
await vm.SubmitAnswerCommand.ExecuteAsync(null);
Assert.Equal(("t1", "q1", "DPAPI please"), worker.LastAnswer);
Assert.False(vm.HasPendingQuestion);
Assert.Equal(string.Empty, vm.AnswerDraft);
}
[Fact]
public void TaskQuestionResolved_ClearsMatchingQuestion()
{
var worker = new FakeWorker();
using var vm = Build(worker);
vm.SetTaskId("t1");
worker.RaiseTaskQuestionAsked("t1", "q1", "?");
worker.RaiseTaskQuestionResolved("t1", "q1");
Assert.False(vm.HasPendingQuestion);
}
[Fact]
public void TaskFinished_ClearsPendingQuestion()
{
var worker = new FakeWorker();
using var vm = Build(worker);
vm.SetTaskId("t1");
worker.RaiseTaskQuestionAsked("t1", "q1", "?");
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
Assert.False(vm.HasPendingQuestion);
}
}