feat: add tool status management and localization for skill installation
- 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.
This commit is contained in:
174
tests/skill-install-shortcut.test.ts
Normal file
174
tests/skill-install-shortcut.test.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
// @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',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user