- Updated chat message types to include tool statuses. - Enhanced localization files for English, Thai, and Chinese to support new tool status messages. - Modified HomePage and SkillsPage components to handle tool statuses in chat messages. - Implemented tool status merging and updating logic in the chat store. - Added handling for tool status events in the gateway event processing. - Created tests for chat message rendering with tool statuses and skill installation shortcuts. - Improved gateway event dispatching for tool lifecycle events.
175 lines
5.0 KiB
TypeScript
175 lines
5.0 KiB
TypeScript
// @vitest-environment node
|
||
|
||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||
|
||
const mocks = vi.hoisted(() => ({
|
||
appendMessage: vi.fn(),
|
||
setActiveRun: vi.fn(),
|
||
clearActiveRun: vi.fn(),
|
||
handleSkillsInstall: vi.fn(),
|
||
parseGitHubSkillUrl: vi.fn((url: string) => ({
|
||
owner: 'MiniMax-AI',
|
||
repo: 'skills',
|
||
ref: 'main',
|
||
skillPath: 'skills/minimax-xlsx',
|
||
defaultSlug: 'minimax-xlsx',
|
||
archiveUrl: 'https://api.github.com/repos/MiniMax-AI/skills/zipball/main',
|
||
repositoryUrl: 'https://github.com/MiniMax-AI/skills.git',
|
||
originalUrl: url,
|
||
})),
|
||
appendTranscriptLine: vi.fn(),
|
||
logger: {
|
||
error: vi.fn(),
|
||
},
|
||
}));
|
||
|
||
vi.mock('../electron/gateway/session-store', () => ({
|
||
sessionStore: {
|
||
appendMessage: mocks.appendMessage,
|
||
setActiveRun: mocks.setActiveRun,
|
||
clearActiveRun: mocks.clearActiveRun,
|
||
},
|
||
}));
|
||
|
||
vi.mock('../electron/gateway/handlers/skills', () => ({
|
||
handleSkillsInstall: mocks.handleSkillsInstall,
|
||
}));
|
||
|
||
vi.mock('@electron/service/skill-install-service', () => ({
|
||
parseGitHubSkillUrl: mocks.parseGitHubSkillUrl,
|
||
}));
|
||
|
||
vi.mock('@electron/utils/token-usage-writer', () => ({
|
||
appendTranscriptLine: mocks.appendTranscriptLine,
|
||
}));
|
||
|
||
vi.mock('@electron/service/logger', () => ({
|
||
default: mocks.logger,
|
||
}));
|
||
|
||
function flushAsyncTasks(): Promise<void> {
|
||
return new Promise((resolve) => setTimeout(resolve, 0));
|
||
}
|
||
|
||
describe('gateway skill install shortcut', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
it('extracts a github-url install intent from an explicit install request', async () => {
|
||
const { extractSkillInstallIntent } = await import('../electron/gateway/skill-install-shortcut');
|
||
|
||
expect(
|
||
extractSkillInstallIntent(
|
||
'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md,帮我安装这个skill',
|
||
),
|
||
).toEqual({
|
||
request: {
|
||
kind: 'github-url',
|
||
url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md',
|
||
},
|
||
description: 'minimax-xlsx',
|
||
});
|
||
});
|
||
|
||
it('extracts a marketplace install intent when the user explicitly names a skill slug', async () => {
|
||
const { extractSkillInstallIntent } = await import('../electron/gateway/skill-install-shortcut');
|
||
|
||
expect(
|
||
extractSkillInstallIntent('帮我安装 minimax-xlsx 这个 skill'),
|
||
).toEqual({
|
||
request: {
|
||
kind: 'marketplace',
|
||
slug: 'minimax-xlsx',
|
||
},
|
||
description: 'minimax-xlsx',
|
||
});
|
||
});
|
||
|
||
it('runs a skill install shortcut and emits tool status before the final assistant message', async () => {
|
||
mocks.handleSkillsInstall.mockResolvedValue({
|
||
success: true,
|
||
slug: 'minimax-xlsx',
|
||
baseDir: '/tmp/minimax-xlsx',
|
||
source: 'github-url',
|
||
enabled: true,
|
||
});
|
||
|
||
const { maybeHandleSkillInstallMessage } = await import('../electron/gateway/skill-install-shortcut');
|
||
const broadcast = vi.fn();
|
||
|
||
const handled = maybeHandleSkillInstallMessage(
|
||
'agent:test:main',
|
||
'run-1',
|
||
{
|
||
role: 'user',
|
||
content: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md,帮我安装这个skill',
|
||
},
|
||
broadcast,
|
||
);
|
||
|
||
expect(handled).toBe(true);
|
||
expect(mocks.setActiveRun).toHaveBeenCalledWith(
|
||
'agent:test:main',
|
||
'run-1',
|
||
expect.any(AbortController),
|
||
);
|
||
|
||
await flushAsyncTasks();
|
||
|
||
expect(mocks.handleSkillsInstall).toHaveBeenCalledWith(
|
||
{
|
||
kind: 'github-url',
|
||
url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md',
|
||
},
|
||
broadcast,
|
||
);
|
||
expect(broadcast).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
||
type: 'tool:status',
|
||
runId: 'run-1',
|
||
toolName: 'skills.install',
|
||
status: 'running',
|
||
}));
|
||
expect(broadcast).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
||
type: 'tool:status',
|
||
runId: 'run-1',
|
||
toolName: 'skills.install',
|
||
status: 'completed',
|
||
}));
|
||
expect(mocks.appendMessage).toHaveBeenCalledWith(
|
||
'agent:test:main',
|
||
expect.objectContaining({
|
||
role: 'assistant',
|
||
content: '已安装并启用 skill minimax-xlsx(github-url)。位置:/tmp/minimax-xlsx',
|
||
_toolStatuses: [
|
||
expect.objectContaining({
|
||
name: 'skills.install',
|
||
status: 'completed',
|
||
input: {
|
||
kind: 'github-url',
|
||
url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md',
|
||
},
|
||
result: expect.objectContaining({
|
||
slug: 'minimax-xlsx',
|
||
baseDir: '/tmp/minimax-xlsx',
|
||
source: 'github-url',
|
||
}),
|
||
}),
|
||
],
|
||
}),
|
||
);
|
||
expect(broadcast).toHaveBeenNthCalledWith(3, expect.objectContaining({
|
||
type: 'chat:final',
|
||
runId: 'run-1',
|
||
message: expect.objectContaining({
|
||
_toolStatuses: [
|
||
expect.objectContaining({
|
||
name: 'skills.install',
|
||
status: 'completed',
|
||
}),
|
||
],
|
||
}),
|
||
}));
|
||
});
|
||
});
|