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
@@ -18,19 +18,19 @@ describe("agent events", () => {
});
it("creates tool call, tool result, and presentation action parts", () => {
expect(agentToolCallPart("c1", "startPreparedReportRun", { deploymentId: "lda_report_case_study.default" })).toEqual({
expect(agentToolCallPart("c1", "startRun", { deploymentId: "lda_report_case_study.default" })).toEqual({
type: "tool-call",
call: {
id: "c1",
name: "startPreparedReportRun",
name: "startRun",
input: { deploymentId: "lda_report_case_study.default" },
},
});
expect(agentToolResultPart("c1", "startPreparedReportRun", "success", { runId: "run_1" })).toEqual({
expect(agentToolResultPart("c1", "startRun", "success", { runId: "run_1" })).toEqual({
type: "tool-result",
result: {
callId: "c1",
name: "startPreparedReportRun",
name: "startRun",
status: "success",
output: { runId: "run_1" },
},
@@ -16,7 +16,7 @@ describe("prepared recipe driver", () => {
const messages = await collect(runPreparedRecipeReplay(recording, signal, async () => ({ approved: true, comment: "test" })));
expect(messages[0]?.role).toBe("user");
expect(messages.some((message) =>
message.parts.some((part) => part.type === "tool-call" && part.call.name === "startPreparedReportRun"),
message.parts.some((part) => part.type === "tool-call" && part.call.name === "startRun"),
)).toBe(true);
expect(messages.some((message) =>
message.parts.some((part) => part.type === "presentation-action" && part.action.type === "selectWorkflowNode"),
@@ -35,7 +35,7 @@ describe("prepared recipe driver", () => {
);
expect(toolNames).toEqual([
"inspectDeployment",
"startPreparedReportRun",
"startRun",
"selectWorkflowNode",
"resumeIssueReview",
"readRunTrace",
@@ -92,7 +92,7 @@ describe("prepared recipe driver", () => {
);
expect(toolCalls).toEqual([
"inspectDeployment",
"startPreparedReportRun",
"startRun",
"selectWorkflowNode",
"resumeIssueReview",
]);
@@ -58,7 +58,7 @@ export async function* runPreparedRecipeReplay(
case "inspectDeployment":
yield emitToolStep(step.id, step.toolName, { deploymentId }, { deploymentId });
break;
case "startPreparedReportRun":
case "startRun":
yield emitToolStep(step.id, step.toolName, { deploymentId }, {
runId,
eventId: runStart?.id ?? null,
+2 -2
View File
@@ -2,7 +2,7 @@ import { LDA_REPORT_DEPLOYMENT_ID } from "../ldaReportDemoConfig.js";
import type { PresentationToolName, WorkflowToolName } from "./tools.js";
export type RecipeTool =
| Extract<WorkflowToolName, "inspectDeployment" | "startPreparedReportRun" | "resumeIssueReview" | "readRunTrace">
| Extract<WorkflowToolName, "inspectDeployment" | "startRun" | "resumeIssueReview" | "readRunTrace">
| Extract<PresentationToolName, "selectWorkflowNode">;
type SelectWorkflowNodeStep = {
@@ -47,7 +47,7 @@ export const PREPARE_THESIS_REPORT_RECIPE: PreparedRecipe = {
{
id: "start-run",
narration: "I will start the prepared workflow run.",
toolName: "startPreparedReportRun",
toolName: "startRun",
},
{
id: "focus-interrupt",
@@ -96,7 +96,7 @@ export const useTimelineAgent = (
setMessages((current) => appendToolMessage(
current,
"timeline-agent-start",
"startPreparedReportRun",
"startRun",
{ mode: modeLabel },
{ phase: "started" },
));
@@ -4,7 +4,7 @@ import { AGENT_TOOLS, isAllowedAgentToolName } from "./tools.js";
describe("agent tools", () => {
it("separates workflow tools from presentation tools", () => {
expect(AGENT_TOOLS.inspectDeployment.kind).toBe("workflow");
expect(AGENT_TOOLS.startPreparedReportRun.kind).toBe("workflow");
expect(AGENT_TOOLS.startRun.kind).toBe("workflow");
expect(AGENT_TOOLS.resumeIssueReview.kind).toBe("workflow");
expect(AGENT_TOOLS.readRunTrace.kind).toBe("workflow");
expect(AGENT_TOOLS.selectWorkflowNode.kind).toBe("presentation");
+3 -3
View File
@@ -1,6 +1,6 @@
export type WorkflowToolName =
| "inspectDeployment"
| "startPreparedReportRun"
| "startRun"
| "resumeIssueReview"
| "readRunTrace";
@@ -23,8 +23,8 @@ export const AGENT_TOOLS = {
kind: "workflow",
description: "Inspect the prepared report deployment.",
},
startPreparedReportRun: {
name: "startPreparedReportRun",
startRun: {
name: "startRun",
kind: "workflow",
description: "Start the prepared report workflow run.",
},