fix: keep manual task target selection

This commit is contained in:
Haitao Pan 2026-04-06 17:02:55 +08:00
parent b739df27e4
commit cd962b57fb
4 changed files with 102 additions and 13 deletions

View File

@ -196,6 +196,19 @@ extension AppControllerDesktopThreadBinding on AppController {
);
}
AssistantExecutionTarget resolveDraftThreadExecutionTargetInternal(
String sessionKey, {
required Iterable<AssistantExecutionTarget> supportedTargets,
}) {
return pickDraftThreadExecutionTargetInternal(
currentTarget: assistantExecutionTargetForSession(sessionKey),
visibleTargets: visibleAssistantExecutionTargets(supportedTargets),
localWorkspaceAvailable: localThreadWorkspacePathInternal(
sessionKey,
).trim().isNotEmpty,
);
}
ExecutionBinding buildDesktopExecutionBindingInternal({
required AssistantExecutionTarget executionTarget,
required SingleAgentProvider singleAgentProvider,
@ -276,3 +289,23 @@ extension AppControllerDesktopThreadBinding on AppController {
);
}
}
AssistantExecutionTarget pickDraftThreadExecutionTargetInternal({
required AssistantExecutionTarget currentTarget,
required Iterable<AssistantExecutionTarget> visibleTargets,
required bool localWorkspaceAvailable,
}) {
final orderedTargets = <AssistantExecutionTarget>[
if (visibleTargets.contains(currentTarget)) currentTarget,
...visibleTargets.where((target) => target != currentTarget),
];
for (final target in orderedTargets) {
if (!localWorkspaceAvailable &&
(target == AssistantExecutionTarget.singleAgent ||
target == AssistantExecutionTarget.local)) {
continue;
}
return target;
}
return currentTarget;
}

View File

@ -10,6 +10,7 @@ import '../widgets/detail_drawer.dart';
import '../widgets/pane_resize_handle.dart';
import '../widgets/sidebar_navigation.dart';
import 'app_controller.dart';
import 'app_controller_desktop_thread_binding.dart';
import 'ui_feature_manifest.dart';
import 'workspace_page_registry.dart';
@ -82,12 +83,13 @@ class _AppShellState extends State<AppShell> {
List<AssistantExecutionTarget> visibleTargets,
) async {
final sessionKey = 'draft:${DateTime.now().millisecondsSinceEpoch}';
final target =
visibleTargets.contains(controller.currentAssistantExecutionTarget)
? controller.currentAssistantExecutionTarget
: (visibleTargets.isNotEmpty
? visibleTargets.first
: controller.currentAssistantExecutionTarget);
final target = pickDraftThreadExecutionTargetInternal(
currentTarget: controller.currentAssistantExecutionTarget,
visibleTargets: visibleTargets,
localWorkspaceAvailable: controller.settings.workspacePath
.trim()
.isNotEmpty,
);
controller.initializeAssistantThreadContext(
sessionKey,
title: appText('新对话', 'New conversation'),

View File

@ -12,6 +12,7 @@ import 'package:markdown/markdown.dart' as md;
import 'package:path_provider/path_provider.dart';
import 'package:super_clipboard/super_clipboard.dart';
import '../../app/app_controller.dart';
import '../../app/app_controller_desktop_thread_binding.dart';
import '../../app/app_metadata.dart';
import '../../app/ui_feature_manifest.dart';
import '../../i18n/app_language.dart';
@ -437,13 +438,17 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal {
Future<void> createNewThreadInternal() async {
final sessionKey = buildDraftSessionKeyInternal(widget.controller);
final inheritedTarget = resolvedVisibleExecutionTargetInternal(
widget.controller,
supportedTargets: const <AssistantExecutionTarget>[
AssistantExecutionTarget.singleAgent,
AssistantExecutionTarget.local,
AssistantExecutionTarget.remote,
],
final inheritedTarget = pickDraftThreadExecutionTargetInternal(
currentTarget: widget.controller.currentAssistantExecutionTarget,
visibleTargets: widget.controller
.visibleAssistantExecutionTargets(const <AssistantExecutionTarget>[
AssistantExecutionTarget.singleAgent,
AssistantExecutionTarget.local,
AssistantExecutionTarget.remote,
]),
localWorkspaceAvailable: widget.controller.settings.workspacePath
.trim()
.isNotEmpty,
);
final inheritedViewMode = widget.controller.currentAssistantMessageViewMode;
setState(() {

View File

@ -0,0 +1,49 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/app/app_controller_desktop_thread_binding.dart';
import 'package:xworkmate/runtime/runtime_models.dart';
void main() {
group('pickDraftThreadExecutionTargetInternal', () {
test('prefers the first visible manual target for new drafts', () {
final target = pickDraftThreadExecutionTargetInternal(
currentTarget: AssistantExecutionTarget.auto,
visibleTargets: const <AssistantExecutionTarget>[
AssistantExecutionTarget.singleAgent,
AssistantExecutionTarget.local,
AssistantExecutionTarget.remote,
],
localWorkspaceAvailable: true,
);
expect(target, AssistantExecutionTarget.singleAgent);
});
test('skips local targets when the local workspace is unavailable', () {
final target = pickDraftThreadExecutionTargetInternal(
currentTarget: AssistantExecutionTarget.auto,
visibleTargets: const <AssistantExecutionTarget>[
AssistantExecutionTarget.singleAgent,
AssistantExecutionTarget.local,
AssistantExecutionTarget.remote,
],
localWorkspaceAvailable: false,
);
expect(target, AssistantExecutionTarget.remote);
});
test('keeps the current visible manual target when it is usable', () {
final target = pickDraftThreadExecutionTargetInternal(
currentTarget: AssistantExecutionTarget.remote,
visibleTargets: const <AssistantExecutionTarget>[
AssistantExecutionTarget.singleAgent,
AssistantExecutionTarget.local,
AssistantExecutionTarget.remote,
],
localWorkspaceAvailable: false,
);
expect(target, AssistantExecutionTarget.remote);
});
});
}