fold sub-agent prompt into the Agent card; relabel you → agent prompt (T-263)
A sidechain sub-agent prompt was rendered with the blue "you" label, falsely implying the user typed it. Now: - transcript_reader parses parentUuid (was dropped) onto every ConversationItem. - conversation_view resolves each sidechain prompt to its spawning Agent/Task card via parentUuid (nearest-preceding Agent as fallback), folds the prompt into that card as a collapsed "prompt" segment, and suppresses the standalone block. Layered order when expanded: call → prompt → result (note E). - A sidechain UserMessage never gets the "you" treatment: folded into its card, or — when orphaned — rendered as a muted standalone "agent prompt". Tests: parentUuid parse, fold + suppression, parallel-agent attachment (would fail under a nearest-preceding heuristic), orphan relabel, and a golden for the Agent card's call → prompt → result layering. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -282,6 +282,54 @@ void main() {
|
||||
expect(find.text('thanks'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a sidechain prompt folds into its Agent card; never labelled "you" (T-263)', (tester) async {
|
||||
await pumpWith(tester, [
|
||||
AssistantToolUse(
|
||||
uuid: 'agt-msg', timestamp: _t, isSidechain: false, toolUseId: 'task1', name: 'Task', input: const {'description': 'explore the codebase'}),
|
||||
UserMessage(uuid: 'p1', timestamp: _t, isSidechain: true, text: 'find all the widgets'),
|
||||
]);
|
||||
// Never the blue "you", and no standalone block (folded → suppressed).
|
||||
expect(find.text('you'), findsNothing);
|
||||
expect(find.text('agent prompt'), findsNothing); // not a standalone card here
|
||||
expect(find.text('Task'), findsOneWidget);
|
||||
// Collapsed by default: the prompt is hidden.
|
||||
expect(find.text('find all the widgets'), findsNothing);
|
||||
|
||||
// Expand the Agent card → a "prompt" segment reveals the folded prompt.
|
||||
await tester.tap(find.bySemanticsLabel('Expand'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('prompt'), findsOneWidget); // segment sub-label
|
||||
expect(find.text('find all the widgets'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('an orphan sidechain prompt renders as muted "agent prompt", never "you" (T-263)', (tester) async {
|
||||
// No Agent tool-use to attach to → stays standalone, but relabelled.
|
||||
await pumpWith(tester, [
|
||||
UserMessage(uuid: 'orphan', timestamp: _t, isSidechain: true, text: 'orphaned agent instructions'),
|
||||
]);
|
||||
expect(find.text('you'), findsNothing);
|
||||
expect(find.text('agent prompt'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('parallel agents: each prompt attaches to its own card via parentUuid (T-263)', (tester) async {
|
||||
// Document order scrambles the prompts so a nearest-preceding heuristic
|
||||
// would misattach BOTH to agent B; parentUuid must route them correctly.
|
||||
await pumpWith(tester, [
|
||||
AssistantToolUse(uuid: 'mA', timestamp: _t, isSidechain: false, toolUseId: 'tA', name: 'Task', input: const {'description': 'agent A'}),
|
||||
AssistantToolUse(uuid: 'mB', timestamp: _t, isSidechain: false, toolUseId: 'tB', name: 'Task', input: const {'description': 'agent B'}),
|
||||
UserMessage(uuid: 'pB', timestamp: _t, isSidechain: true, parentUuid: 'mB', text: 'PROMPT FOR B'),
|
||||
UserMessage(uuid: 'pA', timestamp: _t, isSidechain: true, parentUuid: 'mA', text: 'PROMPT FOR A'),
|
||||
]);
|
||||
// Two collapsed Agent cards; the first Expand caret belongs to card A.
|
||||
expect(find.text('you'), findsNothing);
|
||||
await tester.tap(find.bySemanticsLabel('Expand').first);
|
||||
await tester.pumpAndSettle();
|
||||
// Only card A is expanded → its prompt (A) shows; B's stays folded away.
|
||||
// Nearest-preceding would have put A's prompt under B, revealing nothing.
|
||||
expect(find.text('PROMPT FOR A'), findsOneWidget);
|
||||
expect(find.text('PROMPT FOR B'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('a permission-prompted tool-use is hidden but its result is kept', (tester) async {
|
||||
await pumpWith(
|
||||
tester,
|
||||
|
||||
@@ -361,6 +361,23 @@ void main() {
|
||||
final items = parseAll([raw]);
|
||||
expect(items.first.isSidechain, isTrue);
|
||||
});
|
||||
|
||||
test('parentUuid is parsed; empty parentUuid normalises to null (T-263)', () {
|
||||
final withParent = envelope(
|
||||
type: 'user',
|
||||
uuid: 'u8',
|
||||
parentUuid: 'msg-A',
|
||||
message: {'role': 'user', 'content': 'a sidechain prompt'},
|
||||
);
|
||||
final withoutParent = envelope(
|
||||
type: 'user',
|
||||
uuid: 'u9',
|
||||
message: {'role': 'user', 'content': 'top-level'}, // parentUuid defaults to ''
|
||||
);
|
||||
final items = parseAll([withParent, withoutParent]);
|
||||
expect(items[0].parentUuid, 'msg-A');
|
||||
expect(items[1].parentUuid, isNull); // '' → null
|
||||
});
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -51,6 +51,25 @@ void main() {
|
||||
),
|
||||
),
|
||||
),
|
||||
GoldenTestScenario(
|
||||
name: 'agent / expanded (call → prompt → result layering, T-263)',
|
||||
child: _wrap(
|
||||
f,
|
||||
const ConversationCard(
|
||||
variant: ConversationCardVariant.bordered,
|
||||
accent: Color(0xFF4C9AFF),
|
||||
label: 'Task',
|
||||
status: ConversationCardStatus.success,
|
||||
collapsible: true,
|
||||
collapsedByDefault: false,
|
||||
body: Text('{ "description": "explore the codebase" }', textDirection: TextDirection.ltr),
|
||||
extraSegments: [
|
||||
CardSegment(label: 'prompt', child: Text('find all the widgets and summarise', textDirection: TextDirection.ltr)),
|
||||
CardSegment(label: 'result', child: Text('found 42 widgets', textDirection: TextDirection.ltr)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
GoldenTestScenario(
|
||||
name: 'error / header mark (call card stays separate from the red result card)',
|
||||
child: _wrap(
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 43 KiB |
Reference in New Issue
Block a user