Sync inline ACP artifacts from task results
This commit is contained in:
parent
32ed1e129c
commit
65ced40ced
@ -503,7 +503,7 @@ class GoTaskServiceResult {
|
||||
_castMapList(raw['memorySources']);
|
||||
|
||||
List<GoTaskServiceArtifact> get artifacts {
|
||||
final rawArtifacts = raw['artifacts'];
|
||||
final rawArtifacts = _firstGoTaskArtifactList(raw);
|
||||
if (rawArtifacts is! List) {
|
||||
return const <GoTaskServiceArtifact>[];
|
||||
}
|
||||
@ -545,6 +545,20 @@ class GoTaskServiceResult {
|
||||
}
|
||||
}
|
||||
|
||||
Object? _firstGoTaskArtifactList(Map<String, dynamic> result) {
|
||||
for (final candidate in <Object?>[
|
||||
result['artifacts'],
|
||||
_castMap(result['payload'])['artifacts'],
|
||||
_castMap(result['result'])['artifacts'],
|
||||
_castMap(result['data'])['artifacts'],
|
||||
]) {
|
||||
if (candidate is List) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? goTaskServiceGatewayEntryState({
|
||||
required AssistantExecutionTarget requestedTarget,
|
||||
required GoTaskServiceResult result,
|
||||
|
||||
@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/app/app_controller.dart';
|
||||
import 'package:xworkmate/app/app_controller_desktop_runtime_coordination_impl.dart';
|
||||
import 'package:xworkmate/runtime/go_task_service_client.dart';
|
||||
import 'package:xworkmate/runtime/runtime_models.dart';
|
||||
|
||||
void main() {
|
||||
@ -52,4 +53,61 @@ void main() {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('writes inline ACP artifacts into the local thread workspace', () async {
|
||||
final controller = AppController();
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
final localWorkspace = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-artifact-workspace-',
|
||||
);
|
||||
addTearDown(() async {
|
||||
if (await localWorkspace.exists()) {
|
||||
await localWorkspace.delete(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
controller.upsertTaskThreadInternal(
|
||||
'session-1',
|
||||
workspaceBinding: WorkspaceBinding(
|
||||
workspaceId: 'session-1',
|
||||
workspaceKind: WorkspaceKind.localFs,
|
||||
workspacePath: localWorkspace.path,
|
||||
displayPath: localWorkspace.path,
|
||||
writable: true,
|
||||
),
|
||||
);
|
||||
|
||||
final result = GoTaskServiceResult(
|
||||
success: true,
|
||||
message: 'hello',
|
||||
turnId: 'turn-1',
|
||||
raw: <String, dynamic>{
|
||||
'artifacts': <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
'relativePath': 'notes/hello.txt',
|
||||
'content': 'artifact body',
|
||||
'contentType': 'text/plain',
|
||||
},
|
||||
],
|
||||
},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
);
|
||||
|
||||
await controller.persistGoTaskArtifactsForSessionInternal(
|
||||
'session-1',
|
||||
result,
|
||||
);
|
||||
|
||||
final artifact = File('${localWorkspace.path}/notes/hello.txt');
|
||||
expect(await artifact.readAsString(), 'artifact body');
|
||||
expect(
|
||||
controller
|
||||
.requireTaskThreadForSessionInternal('session-1')
|
||||
.lastArtifactSyncStatus,
|
||||
'synced',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,6 +94,54 @@ void main() {
|
||||
expect(result.success, isFalse);
|
||||
expect(result.message, 'codex execution environment is unavailable');
|
||||
});
|
||||
|
||||
test('keeps bridge message and inline artifacts together', () {
|
||||
final result = goTaskServiceResultFromAcpResponse(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'request-id',
|
||||
'result': <String, dynamic>{
|
||||
'success': true,
|
||||
'message': 'hello',
|
||||
'artifacts': <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
'relativePath': 'notes/hello.txt',
|
||||
'content': 'artifact body',
|
||||
'contentType': 'text/plain',
|
||||
},
|
||||
],
|
||||
},
|
||||
}, route: GoTaskServiceRoute.externalAcpSingle);
|
||||
|
||||
expect(result.success, isTrue);
|
||||
expect(result.message, 'hello');
|
||||
expect(result.artifacts, hasLength(1));
|
||||
expect(result.artifacts.single.relativePath, 'notes/hello.txt');
|
||||
expect(result.artifacts.single.content, 'artifact body');
|
||||
});
|
||||
|
||||
test('uses nested bridge inline artifacts when provider wraps payload', () {
|
||||
final result = goTaskServiceResultFromAcpResponse(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'request-id',
|
||||
'result': <String, dynamic>{
|
||||
'success': true,
|
||||
'payload': <String, dynamic>{
|
||||
'message': 'hello',
|
||||
'artifacts': <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
'relativePath': 'hello.txt',
|
||||
'content': 'nested artifact body',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}, route: GoTaskServiceRoute.externalAcpSingle);
|
||||
|
||||
expect(result.message, 'hello');
|
||||
expect(result.artifacts, hasLength(1));
|
||||
expect(result.artifacts.single.relativePath, 'hello.txt');
|
||||
expect(result.artifacts.single.content, 'nested artifact body');
|
||||
});
|
||||
});
|
||||
|
||||
group('GatewayAcpClient authorization', () {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user