Files
zn-ai/electron/gateway/browser-shortcut.ts
duanshuwen df600272d6 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.
2026-04-23 20:27:54 +08:00

164 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logManager from '@electron/service/logger';
import { extractBrowserOpenIntent, openUrlInBrowser } from '@electron/service/browser-open-service';
import { appendTranscriptLine } from '@electron/utils/token-usage-writer';
import type { RawMessage, ToolStatus } from '@runtime/shared/chat-model';
import { sessionStore } from './session-store';
import type { GatewayEvent } from './types';
function buildBrowserOpenResponseText(result: { pageUrl: string; title?: string }): string {
const suffix = result.title ? `${result.title}` : '';
return `已为你打开 ${result.pageUrl}${suffix}`;
}
function buildBrowserOpenErrorText(error: unknown): string {
return `打开失败:${error instanceof Error ? error.message : String(error)}`;
}
async function processBrowserOpen(
sessionKey: string,
runId: string,
url: string,
signal: AbortSignal,
broadcast: (event: GatewayEvent) => void,
) {
let assistantText = '';
const toolCallId = `browser.open_url:${runId}`;
const startedAt = Date.now();
let finalToolStatus: ToolStatus | null = null;
broadcast({
type: 'tool:status',
sessionKey,
runId,
toolCallId,
toolName: 'browser.open_url',
status: 'running',
updatedAt: startedAt,
summary: `Opening ${url}`,
input: { url },
});
try {
const result = await openUrlInBrowser(url, { signal });
if (signal.aborted) {
return;
}
assistantText = buildBrowserOpenResponseText(result);
finalToolStatus = {
id: toolCallId,
toolCallId,
name: 'browser.open_url',
status: 'completed',
updatedAt: Date.now(),
durationMs: Date.now() - startedAt,
summary: assistantText,
input: { url },
result,
};
broadcast({
type: 'tool:status',
sessionKey,
runId,
toolCallId,
toolName: 'browser.open_url',
status: finalToolStatus.status,
updatedAt: finalToolStatus.updatedAt,
durationMs: finalToolStatus.durationMs,
summary: finalToolStatus.summary,
input: finalToolStatus.input,
result: finalToolStatus.result,
});
} catch (error) {
if (signal.aborted) {
return;
}
assistantText = buildBrowserOpenErrorText(error);
finalToolStatus = {
id: toolCallId,
toolCallId,
name: 'browser.open_url',
status: 'error',
updatedAt: Date.now(),
durationMs: Date.now() - startedAt,
summary: assistantText,
input: { url },
result: {
error: error instanceof Error ? error.message : String(error),
},
};
broadcast({
type: 'tool:status',
sessionKey,
runId,
toolCallId,
toolName: 'browser.open_url',
status: finalToolStatus.status,
updatedAt: finalToolStatus.updatedAt,
durationMs: finalToolStatus.durationMs,
summary: finalToolStatus.summary,
input: finalToolStatus.input,
result: finalToolStatus.result,
});
}
sessionStore.clearActiveRun(sessionKey);
const finalMessage: RawMessage = {
role: 'assistant',
content: assistantText,
timestamp: Date.now(),
_toolStatuses: finalToolStatus ? [finalToolStatus] : undefined,
};
sessionStore.appendMessage(sessionKey, finalMessage);
appendTranscriptLine(sessionKey, {
type: 'message',
timestamp: new Date().toISOString(),
message: {
role: 'assistant',
content: assistantText,
tool: 'browser.open_url',
},
});
broadcast({
type: 'chat:final',
sessionKey,
runId,
message: finalMessage,
});
}
export function maybeHandleBrowserOpenMessage(
sessionKey: string,
runId: string,
message: RawMessage,
broadcast: (event: GatewayEvent) => void,
): boolean {
const browserIntent = typeof message.content === 'string'
? extractBrowserOpenIntent(message.content)
: null;
if (!browserIntent) {
return false;
}
const abortController = new AbortController();
sessionStore.setActiveRun(sessionKey, runId, abortController);
processBrowserOpen(sessionKey, runId, browserIntent.url, abortController.signal, broadcast).catch(
(error) => {
logManager.error('Unexpected error in processBrowserOpen:', error);
sessionStore.clearActiveRun(sessionKey);
broadcast({
type: 'chat:error',
sessionKey,
runId,
error: error instanceof Error ? error.message : String(error),
});
},
);
return true;
}