fix(assistant): auto-bind workspace for only-chat fallback runs
This commit is contained in:
parent
0c800f8b83
commit
d69bcf1d13
@ -260,9 +260,16 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
currentSessionKey,
|
||||
executionTarget: currentTarget,
|
||||
);
|
||||
final workspacePath = assistantWorkspacePathForSession(
|
||||
var workspacePath = assistantWorkspacePathForSession(
|
||||
currentSessionKey,
|
||||
).trim();
|
||||
if (workspacePath.isEmpty) {
|
||||
await tryBindWorkspaceForOnlyChatFallbackInternal(
|
||||
currentSessionKey,
|
||||
currentTarget,
|
||||
);
|
||||
workspacePath = assistantWorkspacePathForSession(currentSessionKey).trim();
|
||||
}
|
||||
if (workspacePath.isEmpty) {
|
||||
final error = StateError(
|
||||
appText(
|
||||
@ -586,4 +593,60 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
}
|
||||
return value.isEmpty ? null : value;
|
||||
}
|
||||
|
||||
Future<void> tryBindWorkspaceForOnlyChatFallbackInternal(
|
||||
String sessionKey,
|
||||
AssistantExecutionTarget currentTarget,
|
||||
) async {
|
||||
if (currentTarget != AssistantExecutionTarget.singleAgent &&
|
||||
currentTarget != AssistantExecutionTarget.auto) {
|
||||
return;
|
||||
}
|
||||
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionKey,
|
||||
);
|
||||
if (!singleAgentUsesAiChatFallbackForSession(normalizedSessionKey)) {
|
||||
return;
|
||||
}
|
||||
if (assistantWorkspacePathForSession(normalizedSessionKey).trim().isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final candidateRoots = <String>{
|
||||
settings.workspacePath.trim(),
|
||||
Directory.current.path.trim(),
|
||||
settings.remoteProjectRoot.trim(),
|
||||
}.where((item) => item.isNotEmpty);
|
||||
for (final root in candidateRoots) {
|
||||
final threadWorkspace =
|
||||
'${trimTrailingPathSeparatorInternal(root)}/.xworkmate/threads/${threadWorkspaceDirectoryNameInternal(normalizedSessionKey)}';
|
||||
if (!ensureLocalWorkspaceDirectoryInternal(threadWorkspace)) {
|
||||
continue;
|
||||
}
|
||||
final existing = assistantThreadRecordsInternal[normalizedSessionKey];
|
||||
upsertTaskThreadInternal(
|
||||
normalizedSessionKey,
|
||||
workspaceBinding: WorkspaceBinding(
|
||||
workspaceId: normalizedSessionKey,
|
||||
workspaceKind: WorkspaceKind.localFs,
|
||||
workspacePath: threadWorkspace,
|
||||
displayPath: threadWorkspace,
|
||||
writable: existing?.workspaceBinding.writable ?? true,
|
||||
),
|
||||
lifecycleState:
|
||||
(existing?.lifecycleState ??
|
||||
const ThreadLifecycleState(
|
||||
archived: false,
|
||||
status: 'ready',
|
||||
lastRunAtMs: null,
|
||||
lastResultCode: null,
|
||||
))
|
||||
.copyWith(status: 'ready'),
|
||||
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
);
|
||||
await flushAssistantThreadPersistenceInternal();
|
||||
recomputeTasksInternal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,6 +443,65 @@ void registerAppControllerAiGatewayChatSuiteSingleAgentTestsInternal() {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'AppController auto-binds a thread workspace in AI Chat fallback when workspace root is missing',
|
||||
() async {
|
||||
final tempDirectory = await createTempDirectoryInternal(
|
||||
'xworkmate-single-agent-fallback-missing-workspace-',
|
||||
);
|
||||
final server = await FakeAiGatewayServerInternal.start(
|
||||
responseMode: AiGatewayResponseModeInternal.json,
|
||||
);
|
||||
addTearDown(() async {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
final store = createStoreFromTempDirectoryInternal(tempDirectory);
|
||||
final client = FallbackOnlyGoAgentCoreClientInternal();
|
||||
final controller = await createAppControllerInternal(
|
||||
store: store,
|
||||
availableSingleAgentProvidersOverride: const <SingleAgentProvider>[],
|
||||
runtimeCoordinator: RuntimeCoordinator(
|
||||
gateway: FakeGatewayRuntimeInternal(store: store),
|
||||
codex: FakeCodexRuntimeInternal(),
|
||||
),
|
||||
goAgentCoreClient: client,
|
||||
);
|
||||
|
||||
await controller.settingsController.saveAiGatewayApiKey('live-key');
|
||||
await controller.saveSettings(
|
||||
controller.settings.copyWith(
|
||||
workspacePath: '',
|
||||
aiGateway: controller.settings.aiGateway.copyWith(
|
||||
baseUrl: server.baseUrl,
|
||||
availableModels: const <String>['moonshotai/kimi-k2.5'],
|
||||
selectedModels: const <String>['moonshotai/kimi-k2.5'],
|
||||
),
|
||||
defaultModel: 'moonshotai/kimi-k2.5',
|
||||
),
|
||||
refreshAfterSave: false,
|
||||
);
|
||||
await controller.setAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.singleAgent,
|
||||
);
|
||||
await controller.setSingleAgentProvider(SingleAgentProvider.opencode);
|
||||
|
||||
await controller.sendChatMessage('你好', thinking: 'low');
|
||||
|
||||
final workspacePath = controller.assistantWorkspacePathForSession(
|
||||
controller.currentSessionKey,
|
||||
);
|
||||
expect(client.capabilitiesCalls, greaterThanOrEqualTo(1));
|
||||
expect(client.executeCalls, 0);
|
||||
expect(server.requestCount, 1);
|
||||
expect(workspacePath, isNotEmpty);
|
||||
expect(
|
||||
workspacePath,
|
||||
contains('.xworkmate/threads/'),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('Single Agent workspace resolution', () {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user