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
@@ -10,17 +10,14 @@ afterEach(() => cleanup());
describe("ProblemLoopScene", () => {
it("uses the assistant transcript surface for the direct-action side", async () => {
const user = userEvent.setup();
render(<ProblemLoopScene scene={problemScene} beat={findBeat("problem", "direct-actions")!} />);
const transcript = screen.getByRole("log", { name: /one-off assistant transcript/i });
expect(transcript).toHaveClass("assistant-operator-thread");
expect(within(transcript).getByText("Can you finish this workspace task?")).toBeInTheDocument();
expect(within(transcript).getByRole("button", { name: /workspace.run_once/i })).toBeInTheDocument();
expect(within(transcript).getByText("Reports success, but leaves no reusable workflow behind.")).toBeInTheDocument();
await user.click(within(transcript).getByRole("button", { name: /workspace.run_once/i }));
expect(within(transcript).getByText(/ephemeral/i)).toBeInTheDocument();
const toolButtons = within(transcript).getAllByRole("button", { name: /workflow.run_once/i });
expect(toolButtons).toHaveLength(3);
expect(within(transcript).getByText("Done. But none of this is recorded in a durable workflow.")).toBeInTheDocument();
});
it("renders reusable automation as a durable workflow blueprint", () => {
@@ -19,12 +19,28 @@ const oneOffToolLoopMessages: ReadonlyArray<AgentMessage> = [
{
type: "tool-call",
call: {
id: "scene-2-tool",
name: "workspace.run_once" as AgentToolName,
input: { persistence: "ephemeral", reusable_workflow: false },
id: "scene-2-tool-1",
name: "workflow.run_once" as AgentToolName,
input: { source: "api", endpoint: "/tasks" },
},
},
{ type: "text", text: "Reports success, but leaves no reusable workflow behind." },
{
type: "tool-call",
call: {
id: "scene-2-tool-2",
name: "workflow.run_once" as AgentToolName,
input: { format: "json", validate: true },
},
},
{
type: "tool-call",
call: {
id: "scene-2-tool-3",
name: "workflow.run_once" as AgentToolName,
input: { destination: "file", path: "/tmp/result.json" },
},
},
{ type: "text", text: "Done. But none of this is recorded in a durable workflow." },
],
},
];