flesh out the Claude prompt UX: options, stepper, collapse
Builds on the in-composer prompt surface (D-78): - Permission prompts (T-175): Allow / Allow-and-don't-ask-again / Deny. "Don't ask again" appears only when the request carries a permission_suggestion and echoes it back as updatedPermissions. An optional note rides Deny as the message, or Allow as a follow-up user message (the protocol has no allow-with-message). - AskUserQuestion picker (T-176): a single question renders bare; 2-4 questions step one at a time (nav shows "N · Header", ✓ when answered) then a review/confirm screen. Each question offers an "Other" free-text choice and a per-choice note; multi-select joins labels. A "chat instead" escape denies the prompt so the user can type freely. On submit the answer is echoed into the log, since the card is ephemeral. - Collapsed tool cards (T-177): multi-line tool_use / tool_result start collapsed behind a one-line summary; one-line output renders inline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -179,6 +179,50 @@ void main() {
|
||||
expect((decision['updatedInput'] as Map)['content'], 'banana');
|
||||
});
|
||||
|
||||
test('a permission request carries its permission_suggestions', () async {
|
||||
proc.emit(jsonEncode({
|
||||
'type': 'control_request',
|
||||
'request_id': 'rs',
|
||||
'request': {
|
||||
'subtype': 'can_use_tool',
|
||||
'tool_name': 'Write',
|
||||
'input': {'file_path': '/tmp/x'},
|
||||
'permission_suggestions': [
|
||||
{'type': 'setMode', 'mode': 'acceptEdits', 'destination': 'session'}
|
||||
],
|
||||
},
|
||||
}));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(session.pendingPrompt!.permissionSuggestions, hasLength(1));
|
||||
});
|
||||
|
||||
test('resolvePrompt(allow with updatedPermissions) echoes them in the response', () async {
|
||||
proc.emit(canUseTool('rp'));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
session.resolvePrompt(
|
||||
'rp',
|
||||
AllowTool(const {
|
||||
'x': 1
|
||||
}, updatedPermissions: const [
|
||||
{'type': 'setMode'}
|
||||
]));
|
||||
final decision = ((jsonDecode(proc.writes.single) as Map)['response'] as Map)['response'] as Map;
|
||||
expect(decision['behavior'], 'allow');
|
||||
expect(decision['updatedPermissions'], hasLength(1));
|
||||
});
|
||||
|
||||
test('resolvePrompt(allow with a follow-up note) sends the note as a user message', () async {
|
||||
proc.emit(canUseTool('rn'));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
session.resolvePrompt('rn', AllowTool(const {'x': 1}, followUpNote: 'use docs/ instead'));
|
||||
|
||||
// first write = control_response (allow), second = the follow-up message
|
||||
expect(proc.writes, hasLength(2));
|
||||
final follow = jsonDecode(proc.writes[1]) as Map;
|
||||
expect(follow['type'], 'user');
|
||||
expect((follow['message'] as Map)['content'], 'use docs/ instead');
|
||||
});
|
||||
|
||||
test('resolvePrompt(deny) writes a deny decision with a message', () async {
|
||||
proc.emit(canUseTool('req-3'));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
@@ -189,6 +233,29 @@ void main() {
|
||||
expect(decision['message'], 'nope');
|
||||
});
|
||||
|
||||
test('resolving an AskUserQuestion leaves an answered echo in the log', () async {
|
||||
proc.emit(jsonEncode({
|
||||
'type': 'control_request',
|
||||
'request_id': 'aq',
|
||||
'request': {
|
||||
'subtype': 'can_use_tool',
|
||||
'tool_name': 'AskUserQuestion',
|
||||
'input': {'questions': <dynamic>[]},
|
||||
},
|
||||
}));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
session.resolvePrompt(
|
||||
'aq',
|
||||
AllowTool(const {
|
||||
'answers': {'Pet': 'Dogs'}
|
||||
}));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
final echo = items.whereType<UserMessage>().toList();
|
||||
expect(echo, hasLength(1));
|
||||
expect(echo.single.text, contains('Pet → Dogs'));
|
||||
});
|
||||
|
||||
test('prompts queue: resolving the head surfaces the next', () async {
|
||||
proc.emit(canUseTool('q1'));
|
||||
proc.emit(canUseTool('q2'));
|
||||
|
||||
Reference in New Issue
Block a user