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:
@@ -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();
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import { ClawHubService } from '@electron/gateway/clawhub';
|
||||
import {
|
||||
SkillInstallService,
|
||||
SkillInstallServiceError,
|
||||
type SkillInstallRequest,
|
||||
} from '@electron/service/skill-install-service';
|
||||
import { getAllSkillConfigs, updateSkillConfig } from '@electron/utils/skill-config';
|
||||
import type { GatewayRpcReturns } from '../types';
|
||||
import type { GatewayEvent, GatewayRpcParams, GatewayRpcReturns } from '../types';
|
||||
|
||||
type GatewayBroadcast = (event: GatewayEvent) => void;
|
||||
|
||||
function broadcastSkillsRuntimeChanged(broadcast?: GatewayBroadcast, reason = 'skills:changed'): void {
|
||||
broadcast?.({
|
||||
type: 'runtime:changed',
|
||||
topics: ['skills'],
|
||||
reason,
|
||||
syncedAt: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleSkillsStatus(): Promise<GatewayRpcReturns['skills.status']> {
|
||||
const configs = await getAllSkillConfigs();
|
||||
@@ -43,3 +60,28 @@ export async function handleSkillsUpdate(
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function handleSkillsInstall(
|
||||
params: GatewayRpcParams['skills.install'],
|
||||
broadcast?: GatewayBroadcast,
|
||||
): Promise<GatewayRpcReturns['skills.install']> {
|
||||
const request = params as SkillInstallRequest;
|
||||
const installService = new SkillInstallService({
|
||||
clawHubService: new ClawHubService(),
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await installService.install(request);
|
||||
broadcastSkillsRuntimeChanged(
|
||||
broadcast,
|
||||
`skills:install:${result.source}:${result.slug}`,
|
||||
);
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof SkillInstallServiceError) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
||||
throw error instanceof Error ? error : new Error(String(error));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user