From 109dbd219ff76f11955b0976fc627033ffefeae3 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 29 May 2026 13:31:48 +0800 Subject: [PATCH] fix(assistant): pin task session on submit --- lib/app/app_controller_desktop_core.dart | 2 + ...app_controller_desktop_thread_actions.dart | 21 +++++--- .../assistant_page_state_actions.dart | 16 +++++-- .../assistant_execution_target_test.dart | 48 +++++++++++++++++++ 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/lib/app/app_controller_desktop_core.dart b/lib/app/app_controller_desktop_core.dart index 4b77df43..7a3c3319 100644 --- a/lib/app/app_controller_desktop_core.dart +++ b/lib/app/app_controller_desktop_core.dart @@ -604,12 +604,14 @@ class AppController extends ChangeNotifier { Future sendChatMessage( String message, { + String? sessionKey, String thinking = 'off', List attachments = const [], List localAttachments = const [], List selectedSkillLabels = const [], }) => AppControllerDesktopThreadActions(this).sendChatMessage( message, + sessionKey: sessionKey, thinking: thinking, attachments: attachments, localAttachments: localAttachments, diff --git a/lib/app/app_controller_desktop_thread_actions.dart b/lib/app/app_controller_desktop_thread_actions.dart index 031bd64c..a863de71 100644 --- a/lib/app/app_controller_desktop_thread_actions.dart +++ b/lib/app/app_controller_desktop_thread_actions.dart @@ -232,6 +232,7 @@ extension AppControllerDesktopThreadActions on AppController { Future sendChatMessage( String message, { + String? sessionKey, String thinking = 'off', List attachments = const [], @@ -239,20 +240,28 @@ extension AppControllerDesktopThreadActions on AppController { const [], List selectedSkillLabels = const [], }) async { - var sessionKey = normalizedAssistantSessionKeyInternal( - sessionsControllerInternal.currentSessionKey, + var targetSessionKey = normalizedAssistantSessionKeyInternal( + sessionKey ?? sessionsControllerInternal.currentSessionKey, ); - if (!isAppOwnedAssistantSessionKeyInternal(sessionKey)) { + if (!isAppOwnedAssistantSessionKeyInternal(targetSessionKey)) { + if (sessionKey != null && sessionKey.trim().isNotEmpty) { + throw StateError( + appText( + '提交目标会话无效,请重新选择任务后提交。', + 'The submit target session is invalid. Select the task again before submitting.', + ), + ); + } await ensureActiveAssistantThreadInternal(); - sessionKey = normalizedAssistantSessionKeyInternal( + targetSessionKey = normalizedAssistantSessionKeyInternal( sessionsControllerInternal.currentSessionKey, ); } final resumeSessionHint = shouldResumeGatewaySessionForNextSendInternal( - sessionKey, + targetSessionKey, ); await dispatchGatewayChatTurnInternal( - sessionKey: sessionKey, + sessionKey: targetSessionKey, message: message, thinking: thinking, attachments: attachments, diff --git a/lib/features/assistant/assistant_page_state_actions.dart b/lib/features/assistant/assistant_page_state_actions.dart index fb74d393..765ea877 100644 --- a/lib/features/assistant/assistant_page_state_actions.dart +++ b/lib/features/assistant/assistant_page_state_actions.dart @@ -129,10 +129,10 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal { autoAgent?.name ?? conversationOwnerLabelInternal(controller); attachmentsInternal = const []; touchTaskSeedInternal( - sessionKey: controller.currentSessionKey, + sessionKey: submittedSessionKey, title: - taskSeedsInternal[controller.currentSessionKey]?.title ?? - fallbackSessionTitleInternal(controller.currentSessionKey), + taskSeedsInternal[submittedSessionKey]?.title ?? + fallbackSessionTitleInternal(submittedSessionKey), preview: rawPrompt, status: controller.hasAssistantPendingRun || connectionState.connected ? 'running' @@ -140,7 +140,7 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal { owner: autoAgent?.name ?? conversationOwnerLabelInternal(controller), surface: 'Assistant', executionTarget: executionTarget, - draft: controller.currentSessionKey.trim().startsWith('draft:'), + draft: submittedSessionKey.trim().startsWith('draft:'), ); }); inputControllerInternal.clear(); @@ -148,6 +148,7 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal { try { await controller.sendChatMessage( prompt, + sessionKey: submittedSessionKey, thinking: thinkingLabelInternal, attachments: attachmentPayloads, localAttachments: submittedAttachments @@ -166,7 +167,12 @@ extension AssistantPageStateActionsInternal on AssistantPageStateInternal { if (!mounted) { rethrow; } - if (inputControllerInternal.text.trim().isEmpty) { + if (!sessionKeysMatchInternal( + widget.controller.currentSessionKey, + submittedSessionKey, + )) { + composerDraftBySessionKeyInternal[submittedSessionKey] = rawPrompt; + } else if (inputControllerInternal.text.trim().isEmpty) { inputControllerInternal.value = TextEditingValue( text: rawPrompt, selection: TextSelection.collapsed(offset: rawPrompt.length), diff --git a/test/runtime/assistant_execution_target_test.dart b/test/runtime/assistant_execution_target_test.dart index 17d01d25..43cf7ab0 100644 --- a/test/runtime/assistant_execution_target_test.dart +++ b/test/runtime/assistant_execution_target_test.dart @@ -1812,6 +1812,54 @@ void main() { ); }); + test('sendChatMessage can pin submit to the captured session', () async { + final fakeGoTaskService = _BlockingGoTaskServiceClient(); + final controller = _connectedController(fakeGoTaskService); + addTearDown(controller.dispose); + + await controller.switchSession('same-prompt-old-task'); + await controller.switchSession('same-prompt-new-task'); + final taskFuture = controller.sendChatMessage( + '连续制作7张图片', + sessionKey: 'same-prompt-new-task', + ); + await fakeGoTaskService.waitForRequestCount(1); + + final request = fakeGoTaskService.requests.single; + expect(request.sessionId, 'same-prompt-new-task'); + expect(request.threadId, 'same-prompt-new-task'); + expect(request.workingDirectory, endsWith('/same-prompt-new-task')); + expect( + request.remoteWorkingDirectoryHint, + endsWith('/threads/same-prompt-new-task'), + ); + + fakeGoTaskService.complete( + 'same-prompt-new-task', + const GoTaskServiceResult( + success: true, + message: 'new task result', + turnId: 'turn-new', + raw: {}, + errorMessage: '', + resolvedModel: '', + route: GoTaskServiceRoute.externalAcpSingle, + ), + ); + await taskFuture; + + expect( + controller.localSessionMessagesInternal['same-prompt-new-task']!.map( + (message) => message.text, + ), + contains('new task result'), + ); + expect( + controller.localSessionMessagesInternal['same-prompt-old-task'], + isNot(contains('new task result')), + ); + }); + test( 'sendChatMessage queues follow-up turns on the same session', () async {