diff --git a/lib/app/app_controller_desktop_thread_actions.dart b/lib/app/app_controller_desktop_thread_actions.dart index 8570972e..b764012b 100644 --- a/lib/app/app_controller_desktop_thread_actions.dart +++ b/lib/app/app_controller_desktop_thread_actions.dart @@ -742,6 +742,7 @@ extension AppControllerDesktopThreadActions on AppController { math.max(1, pollDelay.inSeconds)) .ceil(), ); + var artifactRetries = 0; for (var attempt = 0; attempt < maxAttempts; attempt += 1) { if (disposedInternal) { return; @@ -774,6 +775,17 @@ extension AppControllerDesktopThreadActions on AppController { continue; } if (aiGatewayPendingSessionKeysInternal.contains(sessionKey)) { + final hasRequiredExts = current.requiredArtifactExtensions.isNotEmpty; + final hasEnoughArtifacts = !hasRequiredExts || + current.requiredArtifactExtensions.every((ext) { + return result.artifacts.any( + (a) => a.relativePath.toLowerCase().endsWith(ext.toLowerCase()), + ); + }); + if (!hasEnoughArtifacts && artifactRetries < 3) { + artifactRetries += 1; + continue; + } await applyGatewayChatResultInternal( sessionKey: sessionKey, target: target, diff --git a/lib/runtime/external_code_agent_acp_desktop_transport.dart b/lib/runtime/external_code_agent_acp_desktop_transport.dart index a23d4be6..029ef255 100644 --- a/lib/runtime/external_code_agent_acp_desktop_transport.dart +++ b/lib/runtime/external_code_agent_acp_desktop_transport.dart @@ -175,6 +175,14 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient { ); } final result = _recoveredResultFromTaskSnapshot(snapshot); + final resultArtifacts = _castMap(result['artifacts']); + final artifactItems = resultArtifacts['items'] ?? resultArtifacts; + final hasArtifacts = result.isNotEmpty && + (artifactItems is List && artifactItems.isNotEmpty || + result['artifacts'] is List && (result['artifacts'] as List).isNotEmpty); + if (!hasArtifacts && status == 'completed' && attempt < attempts - 1) { + continue; + } if (result.isNotEmpty) { return goTaskServiceResultFromAcpResponse( { @@ -295,14 +303,14 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient { String status, ) { final error = _castMap(snapshot['error']); - final message = _firstNonEmptyDisplayText( - {...error, ...snapshot}, - const ['message', 'error', 'errorMessage', 'reason', 'code'], - ); - final code = _firstNonEmptyDisplayText( - {...error, ...snapshot}, - const ['code', 'errorCode'], - ); + final rawMessage = + error['message'] ?? + error['error'] ?? + snapshot['message'] ?? + snapshot['error']; + final message = rawMessage?.toString().trim() ?? ''; + final rawCode = error['code'] ?? snapshot['code']; + final code = rawCode?.toString().trim() ?? ''; final result = { 'success': false, 'status': status, @@ -316,68 +324,6 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient { return result; } - String _firstNonEmptyDisplayText( - Map values, - List keys, - ) { - for (final key in keys) { - final value = _displayText(values[key]).trim(); - if (value.isNotEmpty) { - return value; - } - } - return ''; - } - - String _displayText(Object? value, [Set? visited]) { - final seen = visited ?? {}; - if (value == null) { - return ''; - } - if (value is String) { - return value.trim(); - } - if (value is Map) { - if (!seen.add(value)) { - return ''; - } - final map = value.cast(); - for (final key in const [ - 'output', - 'summary', - 'resultSummary', - 'message', - 'content', - 'text', - 'delta', - 'output_text', - ]) { - final extracted = _displayText(map[key], seen); - if (extracted.isNotEmpty) { - return extracted; - } - } - for (final key in const ['result', 'payload', 'data']) { - final extracted = _displayText(map[key], seen); - if (extracted.isNotEmpty) { - return extracted; - } - } - return ''; - } - if (value is List) { - if (!seen.add(value)) { - return ''; - } - return value - .map((item) => _displayText(item, seen)) - .where((item) => item.isNotEmpty) - .join('\n') - .trim(); - } - return value.toString().trim(); - } - Map _castMap(Object? value) { if (value is Map) { return value; diff --git a/test/runtime/assistant_execution_target_test.dart b/test/runtime/assistant_execution_target_test.dart index 928822ed..b90a24e0 100644 --- a/test/runtime/assistant_execution_target_test.dart +++ b/test/runtime/assistant_execution_target_test.dart @@ -4174,13 +4174,13 @@ void main() { controller, 'openclaw-missing-screenshot', 'ready', - const Duration(milliseconds: 500), + const Duration(seconds: 10), ); await _waitForThreadArtifactSyncStatusWithin( controller, 'openclaw-missing-screenshot', 'no-exported-artifacts', - const Duration(milliseconds: 500), + const Duration(seconds: 10), ); final thread = controller.requireTaskThreadForSessionInternal(