Preserve artifacts after interrupted bridge responses
This commit is contained in:
parent
55a1ce2af4
commit
c4ca5a2c34
@ -366,6 +366,38 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<List<String>> recoverGatewayFailureArtifactPathsInternal(
|
||||
String sessionKey,
|
||||
Object error,
|
||||
) async {
|
||||
if (interruptedAcpHttpTransportCodeInternal(error) !=
|
||||
'ACP_HTTP_CONNECTION_CLOSED') {
|
||||
return const <String>[];
|
||||
}
|
||||
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionKey,
|
||||
);
|
||||
final thread = taskThreadForSessionInternal(normalizedSessionKey);
|
||||
if (thread == null ||
|
||||
thread.workspaceBinding.workspaceKind != WorkspaceKind.localFs) {
|
||||
return const <String>[];
|
||||
}
|
||||
final root = Directory(thread.workspaceBinding.workspacePath);
|
||||
try {
|
||||
final policy = await _loadArtifactSyncPolicyInternal(
|
||||
root,
|
||||
thread.selectedSkillKeys,
|
||||
);
|
||||
return _workspaceArtifactPathsModifiedSinceInternal(
|
||||
root,
|
||||
thread.lifecycleState.lastRunAtMs,
|
||||
policy,
|
||||
);
|
||||
} catch (_) {
|
||||
return const <String>[];
|
||||
}
|
||||
}
|
||||
|
||||
String jsonLikeTextForDiagnosticsInternal(Object? value) {
|
||||
try {
|
||||
return jsonEncode(value);
|
||||
|
||||
@ -626,7 +626,7 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
clearAiGatewayStreamingTextInternal(sessionKey);
|
||||
return;
|
||||
}
|
||||
applyGatewayChatFailureInternal(
|
||||
await applyGatewayChatFailureInternal(
|
||||
sessionKey: sessionKey,
|
||||
target: target,
|
||||
error: error,
|
||||
@ -1087,7 +1087,7 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
);
|
||||
} catch (error) {
|
||||
if (!disposedInternal) {
|
||||
applyGatewayChatFailureInternal(
|
||||
await applyGatewayChatFailureInternal(
|
||||
sessionKey: turn.sessionKey,
|
||||
target: turn.target,
|
||||
error: error,
|
||||
@ -1252,13 +1252,15 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
await persistGoTaskArtifactsForSessionInternal(sessionKey, result);
|
||||
}
|
||||
|
||||
void applyGatewayChatFailureInternal({
|
||||
Future<void> applyGatewayChatFailureInternal({
|
||||
required String sessionKey,
|
||||
required AssistantExecutionTarget target,
|
||||
required Object error,
|
||||
}) {
|
||||
}) async {
|
||||
clearAiGatewayStreamingTextInternal(sessionKey);
|
||||
final completedAtMs = DateTime.now().millisecondsSinceEpoch.toDouble();
|
||||
final recoveredArtifactPaths =
|
||||
await recoverGatewayFailureArtifactPathsInternal(sessionKey, error);
|
||||
upsertTaskThreadInternal(
|
||||
sessionKey,
|
||||
lifecycleStatus: 'ready',
|
||||
@ -1266,8 +1268,10 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
lastResultCode: gatewayFailureResultCodeInternal(error),
|
||||
lastRemoteWorkingDirectory: '',
|
||||
lastArtifactSyncAtMs: completedAtMs,
|
||||
lastArtifactSyncStatus: 'failed',
|
||||
lastTaskArtifactRelativePaths: const <String>[],
|
||||
lastArtifactSyncStatus: recoveredArtifactPaths.isEmpty
|
||||
? 'failed'
|
||||
: 'interrupted',
|
||||
lastTaskArtifactRelativePaths: recoveredArtifactPaths,
|
||||
clearOpenClawTaskAssociation: true,
|
||||
updatedAtMs: completedAtMs,
|
||||
);
|
||||
|
||||
@ -1370,6 +1370,14 @@ void main() {
|
||||
}
|
||||
});
|
||||
final fakeGoTaskService = _RecordingGoTaskServiceClient()
|
||||
..onExecuteTask = ((request) async {
|
||||
await Directory(
|
||||
'${request.workingDirectory}/assets/images',
|
||||
).create(recursive: true);
|
||||
await File(
|
||||
'${request.workingDirectory}/assets/images/final.v2.png',
|
||||
).writeAsBytes(<int>[1, 2, 3, 4]);
|
||||
})
|
||||
..updatesBeforeNextOutcome.add(
|
||||
const GoTaskServiceUpdate(
|
||||
sessionId: 'unit-fixture-task-a',
|
||||
@ -1432,7 +1440,13 @@ void main() {
|
||||
controller
|
||||
.taskThreadForSessionInternal('unit-fixture-task-a')
|
||||
?.lastArtifactSyncStatus,
|
||||
'failed',
|
||||
'interrupted',
|
||||
);
|
||||
expect(
|
||||
controller
|
||||
.taskThreadForSessionInternal('unit-fixture-task-a')
|
||||
?.lastTaskArtifactRelativePaths,
|
||||
<String>['assets/images/final.v2.png'],
|
||||
);
|
||||
|
||||
await controller.sendChatMessage('follow up');
|
||||
@ -1869,6 +1883,11 @@ void main() {
|
||||
expect(fakeGoTaskService.requests.last.resumeSession, isFalse);
|
||||
await _waitForLastChatMessageText(controller, '全部 6 个文件已生成 ✅');
|
||||
expect(controller.chatMessages.last.text, '全部 6 个文件已生成 ✅');
|
||||
await _waitForThreadLastResultCode(
|
||||
controller,
|
||||
'unit-fixture-task-a',
|
||||
'SUCCESS',
|
||||
);
|
||||
final thread = controller.taskThreadForSessionInternal(
|
||||
'unit-fixture-task-a',
|
||||
);
|
||||
@ -4185,6 +4204,7 @@ class _RecordingGoTaskServiceClient implements GoTaskServiceClient {
|
||||
final List<GoTaskServiceUpdate> updatesBeforeNextOutcome =
|
||||
<GoTaskServiceUpdate>[];
|
||||
final List<Object> outcomes = <Object>[];
|
||||
Future<void> Function(GoTaskServiceRequest request)? onExecuteTask;
|
||||
|
||||
@override
|
||||
Future<ExternalCodeAgentAcpCapabilities> loadExternalAcpCapabilities({
|
||||
@ -4207,6 +4227,7 @@ class _RecordingGoTaskServiceClient implements GoTaskServiceClient {
|
||||
}) async {
|
||||
executeCount += 1;
|
||||
requests.add(request);
|
||||
await onExecuteTask?.call(request);
|
||||
for (final update in List<GoTaskServiceUpdate>.from(
|
||||
updatesBeforeNextOutcome,
|
||||
)) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user