diff --git a/packages/platform/src/react/chat-items.ts b/packages/platform/src/react/chat-items.ts index f827ec28..3bb8e3b9 100644 --- a/packages/platform/src/react/chat-items.ts +++ b/packages/platform/src/react/chat-items.ts @@ -81,7 +81,7 @@ function toStep(toolCall: ToolCall, answerToolCall: AnswerToolCall | undefined): id: toolCall.id!, tool: toolName, files, - label: stepLabel(toolName, files, summary, display), + label: stepLabel(toolName, files, summary, display, status), status, ...(question && { question }), ...(answered && { answered }), @@ -89,8 +89,14 @@ function toStep(toolCall: ToolCall, answerToolCall: AnswerToolCall | undefined): } // Base44's own description of the step reads best; then the entity it touched; then the files. -function stepLabel(toolName: string, files: string[], summary: string | undefined, display: ToolDisplay | null | undefined): string { - if (summary) return summary; +function stepLabel( + toolName: string, + files: string[], + summary: string | undefined, + display: ToolDisplay | null | undefined, + status: ChatStep["status"], +): string { + if (summary) return summaryForStatus(summary, status); const entityName = display?.entity_name; if (entityName) { const recordCount = display?.record_count; @@ -99,6 +105,15 @@ function stepLabel(toolName: string, files: string[], summary: string | undefine return [toolName, ...files].join(" "); } +// The AI often writes a summary as "present | past", such as "Looking at your app | Looked at your +// app". Like Base44's builder, a step still going shows the first half and a finished one the last. +function summaryForStatus(summary: string, status: ChatStep["status"]): string { + const halves = summary.split("|").map(half => half.trim()).filter(Boolean); + if (halves.length < 2) return summary.trim(); + const isGoing = status === "running" || status === "waiting"; + return isGoing ? halves[0] : halves[halves.length - 1]; +} + // What the user answered on a choice, each answer next to the question it answers. function answersOf(toolCall: ToolCall): ChatAnswer[] | undefined { const userInput = "user_input" in toolCall ? (toolCall.user_input as QuestionInput | null | undefined) : undefined; diff --git a/packages/platform/tests/unit/react/chat-items.test.ts b/packages/platform/tests/unit/react/chat-items.test.ts index 49c40f1f..22dbbe23 100644 --- a/packages/platform/tests/unit/react/chat-items.test.ts +++ b/packages/platform/tests/unit/react/chat-items.test.ts @@ -141,6 +141,14 @@ describe("steps", () => { expect(step({ id: "t4", name: "write_file", status: "success", display: { file_paths: ["src/App.jsx"] } }).label).toBe("write_file src/App.jsx"); }); + test("a 'present | past' summary shows the half that matches the step, as in Base44's builder", () => { + const summary = "Looking at your app | Looked at your app"; + expect(step({ id: "t1", name: "exec_tool", status: "running", display: { summary } }).label).toBe("Looking at your app"); + expect(step({ id: "t2", name: "exec_tool", status: "waiting_for_user_input", display: { summary } }).label).toBe("Looking at your app"); + expect(step({ id: "t3", name: "exec_tool", status: "success", display: { summary } }).label).toBe("Looked at your app"); + expect(step({ id: "t4", name: "exec_tool", status: "error", display: { summary } }).label).toBe("Looked at your app"); + }); + test("an answered choice keeps what the user answered, next to each question", () => { const answered = step({ id: "t1",