feat(ui): triage model picker (opus/fable) in the merge helper start modal

This commit is contained in:
mika kuns
2026-09-10 09:42:55 +02:00
parent 5477ae5302
commit 1177463a82
14 changed files with 84 additions and 36 deletions
+4 -3
View File
@@ -770,12 +770,13 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
// Builds the launch spec for an embedded ConPTY "merge helper" session that drives the given
// tasks to a merged/Done state via the mcp__claudedo__* tools. listId scopes the brief label
// and cwd to that list. handlerTaskId is the id returned by CreateMergeHelperTask -- it is
// rendered into the brief so the session can call handoff_list_handler on itself.
public Task<LaunchSpec> GetMergeHelperLaunchSpec(string[] taskIds, string listId, string handlerTaskId) => HubGuard(() =>
// rendered into the brief so the session can call handoff_list_handler on itself. model
// overrides the triage session's model (null = default opus).
public Task<LaunchSpec> GetMergeHelperLaunchSpec(string[] taskIds, string listId, string handlerTaskId, string? model) => HubGuard(() =>
{
if (_interactiveLaunchSpec is null)
throw new InvalidOperationException("Interactive launch spec service is not configured.");
return _interactiveLaunchSpec.BuildForMergeHelperAsync(taskIds, listId, handlerTaskId, Context.ConnectionAborted);
return _interactiveLaunchSpec.BuildForMergeHelperAsync(taskIds, listId, handlerTaskId, model, Context.ConnectionAborted);
});
// Creates the ClaudeDo task that owns a list-handler run, before the ConPTY tile opens --
@@ -290,8 +290,10 @@ public sealed class InteractiveLaunchSpecService
/// handlerTaskId (the id returned by CreateMergeHelperTaskAsync) is rendered into the brief so
/// the session can call handoff_list_handler/submit_task_for_review on its own handler task.
/// Throws KeyNotFoundException if the list doesn't exist; InvalidOperationException if
/// taskIds is empty or the list has no existing working directory.</summary>
public async Task<LaunchSpec> BuildForMergeHelperAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct)
/// taskIds is empty or the list has no existing working directory.
/// model overrides the triage session's model (start-modal dropdown); null/unknown falls
/// back to <see cref="ModelRegistry.HandlerTriageAlias"/>. Handoff phases are unaffected.</summary>
public async Task<LaunchSpec> BuildForMergeHelperAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, string? model, CancellationToken ct)
{
if (taskIds.Count == 0)
throw new InvalidOperationException("No tasks selected for the list handler.");
@@ -344,12 +346,13 @@ public sealed class InteractiveLaunchSpecService
// longer influences a handler session at all, it would otherwise apply the LIST's effort
// preset to a completely different model.
var settings = await new AppSettingsRepository(ctx).GetAsync(ct);
var triageModel = ModelRegistry.TryNormalizeAlias(model) ?? ModelRegistry.HandlerTriageAlias;
var args = new List<string>
{
"--model", ModelRegistry.HandlerTriageAlias,
"--effort", EffortFor(settings, ModelRegistry.HandlerTriageAlias),
"--permission-mode", PermissionModeResolver.Resolve(ModelRegistry.HandlerTriageAlias, "auto"),
"--model", triageModel,
"--effort", EffortFor(settings, triageModel),
"--permission-mode", PermissionModeResolver.Resolve(triageModel, "auto"),
"--allowedTools", MergeHelperAllowedTools,
"--add-dir", sessionDir, repoDir,
"--append-system-prompt-file", systemPromptPath,