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:
duanshuwen
2026-04-23 20:27:54 +08:00
parent 979fb0a0f6
commit df600272d6
29 changed files with 2041 additions and 384 deletions

View File

@@ -1,4 +1,3 @@
import { randomUUID } from 'node:crypto';
import { createProvider } from '@electron/providers';
import type { BaseProvider } from '@electron/providers/BaseProvider';
import { providerApiService } from '@electron/service/provider-api-service';
@@ -9,21 +8,57 @@ import { sessionStore } from '../session-store';
import type { GatewayEvent, GatewayRpcParams, GatewayRpcReturns } from '../types';
import { appendTranscriptLine } from '@electron/utils/token-usage-writer';
import { maybeHandleBrowserOpenMessage } from '../browser-shortcut';
import { maybeHandleSkillInstallMessage } from '../skill-install-shortcut';
import { buildRuntimeContextMessages } from '../runtime-context';
import { createRandomId } from '../random-id';
export interface GatewayChatMessage {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
}
function flattenMessageContent(content: RawMessage['content']): string {
if (typeof content === 'string') {
return content;
}
return content
.map((block) => {
if (!block || typeof block !== 'object') {
return '';
}
if (block.type === 'text' && typeof block.text === 'string') {
return block.text;
}
if ((block.type === 'tool_result' || block.type === 'toolResult') && typeof block.content === 'string') {
return block.content;
}
if ((block.type === 'tool_result' || block.type === 'toolResult') && Array.isArray(block.content)) {
return flattenMessageContent(block.content as RawMessage['content']);
}
return '';
})
.filter(Boolean)
.join('\n');
}
function buildChatMessages(sessionMessages: RawMessage[]): GatewayChatMessage[] {
return sessionMessages
.map((msg): GatewayChatMessage | null => {
if (!msg.role || !msg.content) return null;
const role = msg.role;
const content = flattenMessageContent(msg.content).trim();
if (!content) {
return null;
}
if (role === 'user' || role === 'assistant' || role === 'system') {
return {
role,
content: typeof msg.content === 'string' ? msg.content : '',
content,
};
}
// Skip toolresult and unsupported roles for now
@@ -114,7 +149,7 @@ export function handleChatSend(
): GatewayRpcReturns['chat.send'] {
const sessionKey = normalizeAgentSessionKey(params.sessionKey);
const { message, options } = params;
const runId = randomUUID();
const runId = createRandomId();
// 1. Append user message
const userMessage: RawMessage = {
@@ -136,6 +171,10 @@ export function handleChatSend(
return { runId };
}
if (maybeHandleSkillInstallMessage(sessionKey, runId, userMessage, broadcast)) {
return { runId };
}
// 2. Resolve provider account
const accountId = options?.providerAccountId || providerApiService.getDefault().accountId;
if (!accountId) {
@@ -154,7 +193,10 @@ export function handleChatSend(
// 3. Build messages array from session history
const session = sessionStore.getOrCreate(sessionKey);
const messages = buildChatMessages(session.messages);
const messages = [
...buildRuntimeContextMessages(sessionKey),
...buildChatMessages(session.messages),
];
// 4. Start streaming
const abortController = new AbortController();