- Switch conversation agent to xiaomi/mimo-v2.5 (multimodal: text + image) - Render native reasoning parts as live 'Thinking trace' (streaming open, collapsed after completion); inline <think> extraction for streaming models - Image attachments: picker (up to 4, 10MB each), base64 to Flue AgentPromptImage, authenticated blob-URL replay for historical images - Mobile keyboard viewport fix: visual-viewport hook, fixed shell, interactive-widget=resizes-content, header pinned, composer follows keyboard - Conversation to Signal to proposed Work: Convex persistence, Effect validation in @code/work-os, Work cards with exact source provenance - Streamdown markdown + Mermaid chart rendering in chat messages - Flue tool turns hidden, reasoning-containing turns remain visible - Frontend regression tests: keyboard viewport, responsive shell, attachment overflow, authenticated images, reasoning traces, transforms - .env.example updated to xiaomi/mimo-v2.5 config
79 lines
1.9 KiB
TypeScript
79 lines
1.9 KiB
TypeScript
import type { FlueConversationMessage } from "@flue/react";
|
|
import { describe, expect, test } from "vitest";
|
|
|
|
import {
|
|
extractThinkingMarkup,
|
|
getReasoningText,
|
|
hasToolActivity,
|
|
isReasoningStreaming,
|
|
} from "./transforms";
|
|
|
|
const message = (
|
|
parts: FlueConversationMessage["parts"]
|
|
): FlueConversationMessage => ({
|
|
id: "message-1",
|
|
parts,
|
|
role: "assistant",
|
|
});
|
|
|
|
describe("hasToolActivity", () => {
|
|
test("detects an orchestration turn", () => {
|
|
expect(
|
|
hasToolActivity(
|
|
message([
|
|
{
|
|
input: { projectId: "project-1" },
|
|
state: "input-available",
|
|
toolCallId: "tool-1",
|
|
toolName: "list_proposed_work",
|
|
type: "dynamic-tool",
|
|
},
|
|
])
|
|
)
|
|
).toBe(true);
|
|
});
|
|
|
|
test("keeps a product-facing assistant response", () => {
|
|
expect(
|
|
hasToolActivity(
|
|
message([
|
|
{
|
|
state: "done",
|
|
text: "Captured the signal and proposed Work.",
|
|
type: "text",
|
|
},
|
|
])
|
|
)
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("reasoning traces", () => {
|
|
test("extracts completed and streaming MiniMax think blocks", () => {
|
|
expect(
|
|
extractThinkingMarkup(
|
|
"<think>First thought</think><think>Current thought"
|
|
)
|
|
).toBe("First thought\n\nCurrent thought");
|
|
});
|
|
|
|
test("combines native reasoning with inline thinking", () => {
|
|
expect(
|
|
getReasoningText(
|
|
message([
|
|
{ state: "streaming", text: "Native thought", type: "reasoning" },
|
|
{ state: "streaming", text: "<think>Inline thought", type: "text" },
|
|
])
|
|
)
|
|
).toBe("Native thought\n\nInline thought");
|
|
});
|
|
|
|
test("recognizes an open streamed think block as live reasoning", () => {
|
|
expect(
|
|
isReasoningStreaming(
|
|
message([{ state: "streaming", text: "<think>Working", type: "text" }])
|
|
)
|
|
).toBe(true);
|
|
});
|
|
});
|