9abf76f5f2
## Context Feedback on the `ask_questions` (Ask AI) tool: - When answering a select prompt with a free-form message, the answer wasn't surfaced as expected in the conversation history. - The selected value also looked dropped once picked. Root cause: answered `ask_questions` parts were caught by the thinking-steps grouping and rendered as a generic collapsible "Ran ask_questions" tool step (JSON output), so the dedicated renderer was never reached. ## Changes - **Render answered questions as a card** (`AiChatQuestionStatusRenderer`): an "Answers" card that shows each full question with the chosen option label(s) or the free-text answer beneath it, instead of the faint inline `header: value` line. - **Free-text keyboard navigation** (`AiChatQuestionCard`): pressing Enter in the free-text area now advances to the next question, or submits when on the last question (mirroring the option-select flow). Shift+Enter still inserts a newline. ## Notes - No schema/GraphQL changes; display + interaction only. - Existing `thinkingStepsDisplayState` grouping test is unaffected (only `web_search`/`create_task`/`code_interpreter` are used there). <img width="421" height="301" alt="Screenshot 2026-07-20 at 17 48 04" src="https://github.com/user-attachments/assets/3845055d-7061-40c0-b263-aa1c670329cd" /> <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23075?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> fixes https://discord.com/channels/1130383047699738754/1526871110300209282
46 lines
1.2 KiB
TypeScript
46 lines
1.2 KiB
TypeScript
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
|
|
|
|
import { type AssistantMessageRenderItem } from '@/ai/utils/assistantMessageRenderItem';
|
|
import { isAskQuestionsToolPart } from '@/ai/utils/isAskQuestionsToolPart';
|
|
import { isThinkingStepPart } from '@/ai/utils/isThinkingStepPart';
|
|
import { type ThinkingStepPart } from '@/ai/utils/thinkingStepPart';
|
|
|
|
export const groupContiguousThinkingStepParts = (
|
|
parts: ExtendedUIMessagePart[],
|
|
): AssistantMessageRenderItem[] => {
|
|
const renderItems: AssistantMessageRenderItem[] = [];
|
|
let currentThinkingParts: ThinkingStepPart[] = [];
|
|
|
|
const flushThinkingParts = () => {
|
|
if (currentThinkingParts.length > 0) {
|
|
renderItems.push({
|
|
type: 'thinking-steps',
|
|
parts: currentThinkingParts,
|
|
});
|
|
currentThinkingParts = [];
|
|
}
|
|
};
|
|
|
|
for (const part of parts) {
|
|
if (part.type === 'step-start') {
|
|
continue;
|
|
}
|
|
|
|
if (isThinkingStepPart(part) && !isAskQuestionsToolPart(part)) {
|
|
currentThinkingParts.push(part);
|
|
continue;
|
|
}
|
|
|
|
flushThinkingParts();
|
|
|
|
renderItems.push({
|
|
type: 'part',
|
|
part,
|
|
});
|
|
}
|
|
|
|
flushThinkingParts();
|
|
|
|
return renderItems;
|
|
};
|