refactor: polish assistant-ui chat surface and rename tools

- Remove useExternalStoreRuntime (caused duplicate ID crashes on re-render)
- Render messages directly from projected data for read-only transcripts
- ToolCard open by default for single tools, collapsed inside ToolGroupCard
- Remove result bubble from ToolCard (was unstyled)
- Move run button below thread (chat-style layout)
- Add user/assistant visual distinction (right-aligned cyan bubble for user)
- Rename startPreparedReportRun -> startRun
- Add 3 workflow.run_once tool calls to Scene 2
- Add overflow handling with max-height + hidden overflow
- Update all tests for new behavior
This commit is contained in:
lda
2026-07-10 07:36:42 +07:00 Verified
parent 029da57f03
commit df026b553a
13 changed files with 137 additions and 127 deletions
@@ -77,7 +77,7 @@ describe("OperatorChat", () => {
expect(screen.getByRole("button", { name: /selectWorkflowNode/i })).toBeInTheDocument();
});
it("renders tool calls as collapsed tool cards", async () => {
it("renders tool calls as open tool cards", async () => {
const user = userEvent.setup();
const messages: ReadonlyArray<AgentMessage> = [
{
@@ -92,9 +92,10 @@ describe("OperatorChat", () => {
render(<OperatorChat state={initialPresentationState} messages={messages} />);
const tool = screen.getByRole("button", { name: /readRunTrace/i });
expect(screen.queryByText(/run_1/)).not.toBeInTheDocument();
await user.click(tool);
expect(tool).toHaveAttribute("aria-expanded", "true");
expect(screen.getByText(/run_1/)).toBeInTheDocument();
await user.click(tool);
expect(screen.queryByText(/run_1/)).not.toBeInTheDocument();
});
it("renders schema approval surface inside chat approval request", async () => {
@@ -157,8 +158,9 @@ describe("OperatorChat", () => {
const tool = screen.getByRole("button", { name: /resumeIssueReview/i });
expect(screen.queryByRole("button", { name: /Approve/i })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: /Deny/i })).not.toBeInTheDocument();
await user.click(tool);
expect(screen.getByLabelText("tool input")).toBeInTheDocument();
await user.click(tool);
expect(screen.queryByLabelText("tool input")).not.toBeInTheDocument();
});
it("renders error and presentation action parts", () => {
@@ -256,7 +258,7 @@ describe("OperatorChat", () => {
parts: [
{
type: "tool-call",
call: { id: "call-1", name: "startPreparedReportRun", input: { deploymentId: "demo" } },
call: { id: "call-1", name: "startRun", input: { deploymentId: "demo" } },
},
],
},
@@ -264,7 +266,7 @@ describe("OperatorChat", () => {
render(<OperatorChat state={initialPresentationState} messages={messages} />);
expect(screen.getByRole("button", { name: /startPreparedReportRun/i })).toBeInTheDocument();
expect(screen.getByRole("button", { name: /startRun/i })).toBeInTheDocument();
});
it("disables schema approval buttons when approval actions are unavailable", () => {