mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-19 22:53:08 +00:00
Merge main into massive-terminal-upgrade
Resolves merge conflicts: - apps/server/src/routes/terminal/common.ts: Keep randomBytes import, use @automaker/utils for createLogger - apps/ui/eslint.config.mjs: Use main's explicit globals list with XMLHttpRequest and MediaQueryListEvent additions - apps/ui/src/components/views/terminal-view.tsx: Keep our terminal improvements (killAllSessions, beforeunload, better error handling) - apps/ui/src/config/terminal-themes.ts: Keep our search highlight colors for all themes - apps/ui/src/store/app-store.ts: Keep our terminal settings persistence improvements (merge function) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,146 +1,146 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
extractTextFromContent,
|
||||
normalizeContentBlocks,
|
||||
formatHistoryAsText,
|
||||
convertHistoryToMessages,
|
||||
} from "@/lib/conversation-utils.js";
|
||||
import { conversationHistoryFixture } from "../../fixtures/messages.js";
|
||||
} from '@automaker/utils';
|
||||
import { conversationHistoryFixture } from '../../fixtures/messages.js';
|
||||
|
||||
describe("conversation-utils.ts", () => {
|
||||
describe("extractTextFromContent", () => {
|
||||
it("should return string content as-is", () => {
|
||||
const result = extractTextFromContent("Hello world");
|
||||
expect(result).toBe("Hello world");
|
||||
describe('conversation-utils.ts', () => {
|
||||
describe('extractTextFromContent', () => {
|
||||
it('should return string content as-is', () => {
|
||||
const result = extractTextFromContent('Hello world');
|
||||
expect(result).toBe('Hello world');
|
||||
});
|
||||
|
||||
it("should extract text from single text block", () => {
|
||||
const content = [{ type: "text", text: "Hello" }];
|
||||
it('should extract text from single text block', () => {
|
||||
const content = [{ type: 'text', text: 'Hello' }];
|
||||
const result = extractTextFromContent(content);
|
||||
expect(result).toBe("Hello");
|
||||
expect(result).toBe('Hello');
|
||||
});
|
||||
|
||||
it("should extract and join multiple text blocks with newlines", () => {
|
||||
it('should extract and join multiple text blocks with newlines', () => {
|
||||
const content = [
|
||||
{ type: "text", text: "First block" },
|
||||
{ type: "text", text: "Second block" },
|
||||
{ type: "text", text: "Third block" },
|
||||
{ type: 'text', text: 'First block' },
|
||||
{ type: 'text', text: 'Second block' },
|
||||
{ type: 'text', text: 'Third block' },
|
||||
];
|
||||
const result = extractTextFromContent(content);
|
||||
expect(result).toBe("First block\nSecond block\nThird block");
|
||||
expect(result).toBe('First block\nSecond block\nThird block');
|
||||
});
|
||||
|
||||
it("should ignore non-text blocks", () => {
|
||||
it('should ignore non-text blocks', () => {
|
||||
const content = [
|
||||
{ type: "text", text: "Text content" },
|
||||
{ type: "image", source: { type: "base64", data: "abc" } },
|
||||
{ type: "text", text: "More text" },
|
||||
{ type: "tool_use", name: "bash", input: {} },
|
||||
{ type: 'text', text: 'Text content' },
|
||||
{ type: 'image', source: { type: 'base64', data: 'abc' } },
|
||||
{ type: 'text', text: 'More text' },
|
||||
{ type: 'tool_use', name: 'bash', input: {} },
|
||||
];
|
||||
const result = extractTextFromContent(content);
|
||||
expect(result).toBe("Text content\nMore text");
|
||||
expect(result).toBe('Text content\nMore text');
|
||||
});
|
||||
|
||||
it("should handle blocks without text property", () => {
|
||||
it('should handle blocks without text property', () => {
|
||||
const content = [
|
||||
{ type: "text", text: "Valid" },
|
||||
{ type: "text" } as any,
|
||||
{ type: "text", text: "Also valid" },
|
||||
{ type: 'text', text: 'Valid' },
|
||||
{ type: 'text' } as any,
|
||||
{ type: 'text', text: 'Also valid' },
|
||||
];
|
||||
const result = extractTextFromContent(content);
|
||||
expect(result).toBe("Valid\n\nAlso valid");
|
||||
expect(result).toBe('Valid\n\nAlso valid');
|
||||
});
|
||||
|
||||
it("should handle empty array", () => {
|
||||
it('should handle empty array', () => {
|
||||
const result = extractTextFromContent([]);
|
||||
expect(result).toBe("");
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it("should handle array with only non-text blocks", () => {
|
||||
it('should handle array with only non-text blocks', () => {
|
||||
const content = [
|
||||
{ type: "image", source: {} },
|
||||
{ type: "tool_use", name: "test" },
|
||||
{ type: 'image', source: {} },
|
||||
{ type: 'tool_use', name: 'test' },
|
||||
];
|
||||
const result = extractTextFromContent(content);
|
||||
expect(result).toBe("");
|
||||
expect(result).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeContentBlocks", () => {
|
||||
it("should convert string to content block array", () => {
|
||||
const result = normalizeContentBlocks("Hello");
|
||||
expect(result).toEqual([{ type: "text", text: "Hello" }]);
|
||||
describe('normalizeContentBlocks', () => {
|
||||
it('should convert string to content block array', () => {
|
||||
const result = normalizeContentBlocks('Hello');
|
||||
expect(result).toEqual([{ type: 'text', text: 'Hello' }]);
|
||||
});
|
||||
|
||||
it("should return array content as-is", () => {
|
||||
it('should return array content as-is', () => {
|
||||
const content = [
|
||||
{ type: "text", text: "Hello" },
|
||||
{ type: "image", source: {} },
|
||||
{ type: 'text', text: 'Hello' },
|
||||
{ type: 'image', source: {} },
|
||||
];
|
||||
const result = normalizeContentBlocks(content);
|
||||
expect(result).toBe(content);
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should handle empty string", () => {
|
||||
const result = normalizeContentBlocks("");
|
||||
expect(result).toEqual([{ type: "text", text: "" }]);
|
||||
it('should handle empty string', () => {
|
||||
const result = normalizeContentBlocks('');
|
||||
expect(result).toEqual([{ type: 'text', text: '' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatHistoryAsText", () => {
|
||||
it("should return empty string for empty history", () => {
|
||||
describe('formatHistoryAsText', () => {
|
||||
it('should return empty string for empty history', () => {
|
||||
const result = formatHistoryAsText([]);
|
||||
expect(result).toBe("");
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it("should format single user message", () => {
|
||||
const history = [{ role: "user" as const, content: "Hello" }];
|
||||
it('should format single user message', () => {
|
||||
const history = [{ role: 'user' as const, content: 'Hello' }];
|
||||
const result = formatHistoryAsText(history);
|
||||
|
||||
expect(result).toContain("Previous conversation:");
|
||||
expect(result).toContain("User: Hello");
|
||||
expect(result).toContain("---");
|
||||
expect(result).toContain('Previous conversation:');
|
||||
expect(result).toContain('User: Hello');
|
||||
expect(result).toContain('---');
|
||||
});
|
||||
|
||||
it("should format single assistant message", () => {
|
||||
const history = [{ role: "assistant" as const, content: "Hi there" }];
|
||||
it('should format single assistant message', () => {
|
||||
const history = [{ role: 'assistant' as const, content: 'Hi there' }];
|
||||
const result = formatHistoryAsText(history);
|
||||
|
||||
expect(result).toContain("Assistant: Hi there");
|
||||
expect(result).toContain('Assistant: Hi there');
|
||||
});
|
||||
|
||||
it("should format multiple messages with correct roles", () => {
|
||||
it('should format multiple messages with correct roles', () => {
|
||||
const history = conversationHistoryFixture.slice(0, 2);
|
||||
const result = formatHistoryAsText(history);
|
||||
|
||||
expect(result).toContain("User: Hello, can you help me?");
|
||||
expect(result).toContain("Assistant: Of course! How can I assist you today?");
|
||||
expect(result).toContain("---");
|
||||
expect(result).toContain('User: Hello, can you help me?');
|
||||
expect(result).toContain('Assistant: Of course! How can I assist you today?');
|
||||
expect(result).toContain('---');
|
||||
});
|
||||
|
||||
it("should handle messages with array content (multipart)", () => {
|
||||
it('should handle messages with array content (multipart)', () => {
|
||||
const history = [conversationHistoryFixture[2]]; // Has text + image
|
||||
const result = formatHistoryAsText(history);
|
||||
|
||||
expect(result).toContain("What is in this image?");
|
||||
expect(result).not.toContain("base64"); // Should not include image data
|
||||
expect(result).toContain('What is in this image?');
|
||||
expect(result).not.toContain('base64'); // Should not include image data
|
||||
});
|
||||
|
||||
it("should format all messages from fixture", () => {
|
||||
it('should format all messages from fixture', () => {
|
||||
const result = formatHistoryAsText(conversationHistoryFixture);
|
||||
|
||||
expect(result).toContain("Previous conversation:");
|
||||
expect(result).toContain("User: Hello, can you help me?");
|
||||
expect(result).toContain("Assistant: Of course!");
|
||||
expect(result).toContain("User: What is in this image?");
|
||||
expect(result).toContain("---");
|
||||
expect(result).toContain('Previous conversation:');
|
||||
expect(result).toContain('User: Hello, can you help me?');
|
||||
expect(result).toContain('Assistant: Of course!');
|
||||
expect(result).toContain('User: What is in this image?');
|
||||
expect(result).toContain('---');
|
||||
});
|
||||
|
||||
it("should separate messages with double newlines", () => {
|
||||
it('should separate messages with double newlines', () => {
|
||||
const history = [
|
||||
{ role: "user" as const, content: "First" },
|
||||
{ role: "assistant" as const, content: "Second" },
|
||||
{ role: 'user' as const, content: 'First' },
|
||||
{ role: 'assistant' as const, content: 'Second' },
|
||||
];
|
||||
const result = formatHistoryAsText(history);
|
||||
|
||||
@@ -148,73 +148,71 @@ describe("conversation-utils.ts", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("convertHistoryToMessages", () => {
|
||||
it("should convert empty history", () => {
|
||||
describe('convertHistoryToMessages', () => {
|
||||
it('should convert empty history', () => {
|
||||
const result = convertHistoryToMessages([]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should convert single message to SDK format", () => {
|
||||
const history = [{ role: "user" as const, content: "Hello" }];
|
||||
it('should convert single message to SDK format', () => {
|
||||
const history = [{ role: 'user' as const, content: 'Hello' }];
|
||||
const result = convertHistoryToMessages(history);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchObject({
|
||||
type: "user",
|
||||
session_id: "",
|
||||
type: 'user',
|
||||
session_id: '',
|
||||
message: {
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "Hello" }],
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: 'Hello' }],
|
||||
},
|
||||
parent_tool_use_id: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("should normalize string content to array", () => {
|
||||
const history = [{ role: "assistant" as const, content: "Response" }];
|
||||
it('should normalize string content to array', () => {
|
||||
const history = [{ role: 'assistant' as const, content: 'Response' }];
|
||||
const result = convertHistoryToMessages(history);
|
||||
|
||||
expect(result[0].message.content).toEqual([
|
||||
{ type: "text", text: "Response" },
|
||||
]);
|
||||
expect(result[0].message.content).toEqual([{ type: 'text', text: 'Response' }]);
|
||||
});
|
||||
|
||||
it("should preserve array content", () => {
|
||||
it('should preserve array content', () => {
|
||||
const history = [
|
||||
{
|
||||
role: "user" as const,
|
||||
role: 'user' as const,
|
||||
content: [
|
||||
{ type: "text", text: "Hello" },
|
||||
{ type: "image", source: {} },
|
||||
{ type: 'text', text: 'Hello' },
|
||||
{ type: 'image', source: {} },
|
||||
],
|
||||
},
|
||||
];
|
||||
const result = convertHistoryToMessages(history);
|
||||
|
||||
expect(result[0].message.content).toHaveLength(2);
|
||||
expect(result[0].message.content[0]).toEqual({ type: "text", text: "Hello" });
|
||||
expect(result[0].message.content[0]).toEqual({ type: 'text', text: 'Hello' });
|
||||
});
|
||||
|
||||
it("should convert multiple messages", () => {
|
||||
it('should convert multiple messages', () => {
|
||||
const history = conversationHistoryFixture.slice(0, 2);
|
||||
const result = convertHistoryToMessages(history);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].type).toBe("user");
|
||||
expect(result[1].type).toBe("assistant");
|
||||
expect(result[0].type).toBe('user');
|
||||
expect(result[1].type).toBe('assistant');
|
||||
});
|
||||
|
||||
it("should set correct fields for SDK format", () => {
|
||||
const history = [{ role: "user" as const, content: "Test" }];
|
||||
it('should set correct fields for SDK format', () => {
|
||||
const history = [{ role: 'user' as const, content: 'Test' }];
|
||||
const result = convertHistoryToMessages(history);
|
||||
|
||||
expect(result[0].session_id).toBe("");
|
||||
expect(result[0].session_id).toBe('');
|
||||
expect(result[0].parent_tool_use_id).toBeNull();
|
||||
expect(result[0].type).toBe("user");
|
||||
expect(result[0].message.role).toBe("user");
|
||||
expect(result[0].type).toBe('user');
|
||||
expect(result[0].message.role).toBe('user');
|
||||
});
|
||||
|
||||
it("should handle all messages from fixture", () => {
|
||||
it('should handle all messages from fixture', () => {
|
||||
const result = convertHistoryToMessages(conversationHistoryFixture);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
Reference in New Issue
Block a user