feat: add Chinese UI, Responses models, and mirrored Windows setup
CI / Python 3.12 / ubuntu-latest (push) Canceled after 0s
CI / Python 3.12 / windows-latest (push) Canceled after 0s
CI / Frontend tests and production build (push) Canceled after 0s
CI / Build and smoke-test Python packages (push) Canceled after 0s

Localize the web console and persist custom model and capability settings.
Use OpenAI Responses exclusively and move runtime limits into environment configuration.
Bootstrap uv and the virtual environment on Windows, support optional China mirrors,
and package source distributions without environments, build output, or credentials.

Update regression tests and repository quality checks for the new behavior.
This commit is contained in:
chenmingxuan committed 2026-09-22 19:16:17 +08:00
1 parent 371aa6df56
commit 2ddec53954
183 files changed
+3427 -11857

No files matched your search

@@ -138,7 +138,7 @@ function parseVideoResultText(text: string): Partial<VideoAnalysisView> {
return { outcome: 'failed', summary: value };
}
if (value.includes('Analysis is already in progress in another video agent')) {
return { outcome: 'waiting', summary: 'Another video agent is already analyzing this evidence.' };
return { outcome: 'waiting', summary: "另一个视频智能体正在分析此段内容。" };
}
return { outcome: 'complete', summary: value };
}
@@ -175,14 +175,14 @@ export function getVideoAnalysisView(tool: any): VideoAnalysisView | null {
const completedCount = Number(structured.completed_count ?? completedRanges.length ?? 0);
const totalCount = Number(structured.total_count ?? (completedRanges.length + failedRanges.length));
const titleByOutcome: Record<VideoAnalysisOutcome, string> = {
running: 'Analyzing screen recording',
recovering: 'Analyzing unfinished recording segment',
waiting: 'Waiting for existing video analysis',
running: "正在分析屏幕录像",
recovering: "正在分析尚未结束的录像片段",
waiting: "正在等待已有的视频分析",
complete: structured.reuse === 'full' || parsed.reuse === 'full'
? 'Reused video analysis'
: 'Analyzed screen recording',
partial: 'Video analysis partially completed',
failed: 'Video analysis returned no result'
? "已复用视频分析"
: "已分析屏幕录像",
partial: "视频分析部分完成",
failed: "视频分析未返回结果"
};
return {
@@ -236,7 +236,7 @@ export function getVideoToolTarget(tool: any): string | null {
* Canonical list of mobile device automation and ADB command tool names.
*/
export const DEVICE_ACTION_TOOL_NAMES = new Set([
'click', 'click_sequence', 'long_press', 'long_press_on', 'input', 'input_text',
'click', 'click_sequence', 'long_press', 'long_press_on', 'input', 'input_text',
'swipe', 'scroll', 'press_key', 'press_home', 'press_back', 'launch_app', 'manage_app', 'open_app',
'focus_and_input_text', 'focus_and_clear_text', 'tap', 'wait_for_delay', 'wait',
'run_adb_command', 'run_short_adb_command'
@@ -558,9 +558,9 @@ export function getToolDisplayLabel(tool: any, isFirstSaveNote: boolean = false)
case 'manage_app':
case 'launch_app': {
const rawApp = args.app_name || args.package_name || args.app || '';
const app = rawApp ? (rawApp.charAt(0).toUpperCase() + rawApp.slice(1)) : 'Application';
const app = rawApp ? (rawApp.charAt(0).toUpperCase() + rawApp.slice(1)) : "应用";
const rawAction = args.action ? String(args.action).toLowerCase() : '';
const verb = rawAction === 'launch' ? 'Launching' : (rawAction === 'stop' || rawAction === 'close' ? 'Stopping' : 'Managing');
const verb = rawAction === 'launch' ? "启动中" : (rawAction === 'stop' || rawAction === 'close' ? "停止中" : "管理中");
return `${verb} "${app}"`;
}
@@ -568,100 +568,100 @@ export function getToolDisplayLabel(tool: any, isFirstSaveNote: boolean = false)
case 'wait_delay':
case 'wait': {
const delay = args.delay_seconds || args.seconds || args.delay || args.duration;
return delay ? `Waiting for ${delay} second${Number(delay) > 1 ? 's' : ''}...` : 'Waiting for delay...';
return delay ? `等待 ${delay} second${Number(delay) > 1 ? 's' : ''}...` : "正在等待…";
}
case 'wait_for_text': {
const text = args.text || args.target_text || '';
return text ? `Waiting for text "${text}" to appear on screen` : 'Waiting for text on screen';
return text ? `等待文本 "${text}" 出现在屏幕上` : "正在等待屏幕出现文本";
}
case 'input_text':
case 'input': {
const text = args.text || args.input_text || '';
return text ? `Entering text "${text}" into field` : 'Entering text into input field';
return text ? `输入文本 "${text}" 至输入框` : "正在向输入框输入文本";
}
case 'focus_and_input_text': {
const text = args.text || args.input_text || '';
return text ? `Focusing field and typing "${text}"` : 'Focusing field and entering text';
return text ? `聚焦输入框并输入 "${text}"` : "正在聚焦输入框并输入文本";
}
case 'focus_and_clear_text':
return 'Focusing and clearing field text';
return "正在聚焦输入框并清空文本";
case 'click':
case 'tap': {
const target = args.target_text || args.text || args.query || '';
return target ? `Tapping on "${target}"` : 'Tapping on screen element';
return target ? `点击 "${target}"` : "正在点击屏幕元素";
}
case 'click_sequence':
return 'Executing click sequence';
return "正在执行连续点击";
case 'long_press': {
const target = args.target_text || args.text || '';
return target ? `Long pressing on "${target}"` : 'Long pressing screen element';
return target ? `长按 "${target}"` : "正在长按屏幕元素";
}
case 'swipe': {
const dir = args.action || args.direction || '';
return dir ? `Swiping ${String(dir).toUpperCase()} on screen` : 'Swiping screen';
return dir ? `滑动 ${String(dir).toUpperCase()} 在屏幕上` : "正在滑动屏幕";
}
case 'press_key': {
const key = args.key || args.keycode || '';
return key ? `Pressing key ${String(key).toUpperCase()}` : 'Pressing hardware key';
return key ? `按键 ${String(key).toUpperCase()}` : "正在按下实体按键";
}
case 'save_note':
return isFirstSaveNote ? 'Creating note' : 'Saving note';
return isFirstSaveNote ? "正在创建笔记" : "正在保存笔记";
case 'read_note':
return 'Reading note';
return "正在读取笔记";
case 'list_notes':
return 'Browsing all saved notes';
return "正在浏览已保存笔记";
case 'update_note':
return 'Updating note';
return "正在更新笔记";
case 'append_note':
return 'Updating note';
return "正在更新笔记";
case 'object_detection': {
const q = Array.isArray(args.queries) ? args.queries.join(', ') : (args.queries || '');
return q ? `Locating on screen: "${q}"` : 'Locating elements on screen';
return q ? `Locating 在屏幕上: "${q}"` : "正在定位屏幕元素";
}
case 'ask_explorer': {
const query = args.query || args.prompt || '';
return query ? `Searching on screen: "${query}"` : 'Searching on screen';
return query ? `Searching 在屏幕上: "${query}"` : "正在搜索屏幕";
}
case 'report_failure_analysis': {
const reason = args.reason || args.analysis || '';
return reason ? `Investigating issue: ${reason}` : 'Investigating execution issue';
return reason ? `排查问题:${reason}` : "正在排查执行问题";
}
case 'run_adb_command':
case 'run_short_adb_command': {
const cmd = args.command || args.cmd || '';
return cmd ? `Running command: ${cmd}` : 'Running system command';
return cmd ? `运行命令:${cmd}` : "正在运行系统命令";
}
case 'search_logs':
case 'read_logs': {
const q = args.query || args.filter || '';
return q ? `Searching logs for "${q}"` : 'Analyzing system logs';
return q ? `搜索日志:"${q}"` : "正在分析系统日志";
}
case 'log_analyzer':
case 'output_analyzer':
return 'Analyzing logs';
return "正在分析日志";
case 'diagnoser':
case 'diagnose':
return 'Diagnosing issue';
return "正在诊断问题";
case 'video_analyzer':
case 'video_analyzer_pure':
return 'Analyzing screen recording';
return "正在分析屏幕录像";
case 'extract_segment_metadata': {
const start = args.start_time !== undefined ? `${args.start_time}s` : '';
const end = args.end_time !== undefined ? `${args.end_time}s` : '';
const range = (start && end) ? ` (${start} - ${end})` : (start ? ` (from ${start})` : '');
return `Cropping screen recording segment${range}`;
return `截取录屏片段${range}`;
}
case 'spawn_sub_agent': {
const q = args.specific_query || args.query || args.prompt || '';
return q ? `Analyzing recording with sub-agent: "${q}"` : 'Analyzing recording with sub-agent';
return q ? `分析录屏:"${q}"` : "正在调用子智能体分析录像";
}
case 'analyze_audio_only': {
const q = args.specific_query || args.query || '';
return q ? `Analyzing audio track: "${q}"` : 'Analyzing recording audio track';
return q ? `Analyzing audio track: "${q}"` : "正在分析录像音轨";
}
case 'search_history': {
const q = args.query || '';
@@ -676,30 +676,30 @@ export function getToolDisplayLabel(tool: any, isFirstSaveNote: boolean = false)
if (n !== undefined && n !== '' && end !== undefined && end !== null && end !== '' && String(end) !== String(n)) {
return `Reviewing steps ${n}–${end}`;
}
return n !== undefined && n !== '' ? `Reviewing step ${n}` : 'Reviewing step details';
return n !== undefined && n !== '' ? `Reviewing step ${n}` : "正在查看步骤详情";
}
case 'get_step_screenshot': {
const n = args.step_number;
const variant = String(args.which || '').toLowerCase();
if (variant === 'overlay') {
return n !== undefined && n !== '' ? `Looking at where step ${n}'s action landed` : 'Looking at where an action landed';
return n !== undefined && n !== '' ? `Looking at where step ${n}'s action landed` : "正在查看操作位置";
}
const which = variant === 'post' ? 'after' : 'before';
return n !== undefined && n !== '' ? `Looking at the screen ${which} step ${n}` : 'Looking at a step screenshot';
return n !== undefined && n !== '' ? `Looking at the screen ${which} step ${n}` : "正在查看步骤截图";
}
case 'probe_device': {
const kind = args.kind ? String(args.kind).replace(/_/g, ' ') : '';
return kind ? `Reading ${kind} from the device` : 'Reading device state';
return kind ? `Reading ${kind} from the device` : "正在读取设备状态";
}
case 'outputter':
case 'output_synthesis':
return 'Synthesizing output report';
return "正在生成输出报告";
case 'web_search': {
const q = args.query || '';
return q ? `Searching web for "${q}"` : 'Searching the web';
return q ? `Searching web for "${q}"` : "正在搜索网页";
}
case 'read_url':
return 'Fetching web page';
return "正在读取网页";
case 'compress_history':
return getCompressionLabel(tool);
@@ -723,7 +723,7 @@ export function getCompressionLabel(tool: any): string {
const hasRange = Number.isFinite(start) && Number.isFinite(end) && start > 0 && end > 0;
const range = hasRange
? (start === end ? `step ${start}` : `steps ${start}–${end}`)
: 'earlier steps';
: "之前的步骤";
const rangeCapitalized = range.charAt(0).toUpperCase() + range.slice(1);
const status = String(tool?.status || '').toLowerCase();
const phase = getCompressionPhase(tool);
@@ -802,13 +802,13 @@ export function getCompressionPhase(tool: any): CompressionPhase {
export function getCompressionPhaseLabel(tool: any): string {
switch (getCompressionPhase(tool)) {
case 'ready':
return 'Summary ready; kept in reserve until the context fills up';
return "摘要已就绪,上下文接近容量上限时替换原文";
case 'applied':
return 'Replaced this stretch with its summary';
return "已使用摘要替换此段记录";
case 'failed':
return 'Summary failed; full record kept';
return "摘要失败,保留完整记录";
default:
return 'Summarizing this stretch';
return "正在总结此段记录";
}
}
@@ -898,76 +898,76 @@ export function getToolIcon(tool: any): string {
* Get formatted title for a tool call card
*/
export function getToolTitle(tool: any): string {
if (!tool || !tool.name) return 'Tool Call';
if (!tool || !tool.name) return "工具调用";
const cleanName = tool.name.replace(/^(_)?exec_/, '');
const name = cleanName.toLowerCase();
switch (name) {
case 'click':
case 'tap':
return 'Tapping Element';
return "点击元素";
case 'click_sequence':
return 'Executing Click Sequence';
return "执行连续点击";
case 'long_press':
return 'Long Pressing Element';
return "长按元素";
case 'input_text':
case 'input':
return 'Entering Text';
return "输入文本";
case 'swipe':
case 'scroll': {
const args = getToolArgs(tool);
const dir = args.direction || args.gesture || (typeof args.action === 'string' ? args.action : '');
if (dir && isPureDirectionString(dir)) return `Swiping Screen (${String(dir).toUpperCase()})`;
return 'Swiping Screen';
if (dir && isPureDirectionString(dir)) return `滑动屏幕(${String(dir).toUpperCase()})`;
return "滑动屏幕";
}
case 'drag':
case 'drag_and_drop':
return 'Dragging Screen';
return "拖动屏幕";
case 'press_key':
return 'Pressing Hardware Key';
return "按下实体按键";
case 'manage_app':
case 'launch_app': {
const args = getToolArgs(tool);
const rawAction = args.action ? String(args.action).toLowerCase() : '';
if (rawAction === 'launch') return 'Launching Application';
if (rawAction === 'stop' || rawAction === 'close') return 'Stopping Application';
return 'Managing Application';
if (rawAction === 'launch') return "启动应用";
if (rawAction === 'stop' || rawAction === 'close') return "停止应用";
return "管理应用";
}
case 'wait_for_delay':
case 'wait_delay':
return 'Waiting for Delay';
return "等待";
case 'wait_for_text':
return 'Waiting for Text';
return "等待文本";
case 'object_detection':
return 'Locating Elements';
return "定位元素";
case 'ask_explorer':
return 'Searching on Screen';
return "搜索屏幕";
case 'report_failure_analysis':
return 'Investigating Issue';
return "排查问题";
case 'run_adb_command':
case 'run_short_adb_command':
return 'Running System Command';
return "运行系统命令";
case 'web_search':
return 'Web Search';
return "网页搜索";
case 'read_url':
return 'Fetching Web Page';
return "读取网页";
case 'search_logs':
case 'read_logs':
return 'Searching Logs';
return "搜索日志";
case 'log_analyzer':
case 'output_analyzer':
return 'Analyzing Logs';
return "分析日志";
case 'diagnoser':
case 'diagnose':
return 'Diagnosing Issue';
return "诊断问题";
case 'video_analyzer':
case 'video_analyzer_pure':
return 'Analyzing Screen Recording';
return "分析屏幕录像";
case 'extract_segment_metadata':
return 'Cropping Screen Recording';
return "裁剪屏幕录像";
case 'spawn_sub_agent':
return 'Delegating Video Analysis';
return "委派视频分析";
case 'analyze_audio_only':
return 'Analyzing Audio Track';
return "分析音轨";
default:
return cleanName.replace(/_/g, ' ').replace(/\b\w/g, (c: string) => c.toUpperCase());
}
@@ -1046,26 +1046,26 @@ export function getToolTargetText(tool: any): string {
* Get input label for tools
*/
export function getToolInputLabel(tool: any): string {
if (!tool || !tool.name) return 'Input';
if (!tool || !tool.name) return "输入";
const name = tool.name.toLowerCase().replace(/^(_)?exec_/, '');
if (name === 'wait_for_delay' || name === 'wait_delay' || name === 'delay' || name === 'wait') {
return 'Duration';
return "耗时";
}
if (name === 'swipe' || name === 'scroll' || name === 'drag' || name === 'drag_and_drop') {
const args = getToolArgs(tool);
const dir = args.direction || args.gesture || (typeof args.action === 'string' ? args.action : '');
if (dir && isPureDirectionString(dir)) {
return 'Direction';
return "方向";
}
return 'Input';
return "输入";
}
if (name === 'press_key' || name === 'press_home' || name === 'press_back') {
return 'Key';
return "按键";
}
if (name === 'input_text' || name === 'input') {
return 'Input Text';
return "输入文本";
}
return 'Input';
return "输入";
}
/**
@@ -1220,14 +1220,14 @@ export function isToolFailed(tool: any): boolean {
* Get error message for a failed tool
*/
export function getToolErrorMessage(tool: any): string {
if (!tool) return 'Tool Failed';
if (!tool) return "工具失败";
const args = getToolArgs(tool);
if (args.status === 'cannot_fix') return 'Status: cannot_fix';
if (args.error || args.message || args.failure_reason) {
return args.error || args.message || args.failure_reason;
}
if (tool.error || tool.message) return tool.error || tool.message;
return 'Action Failed';
return "操作失败";
}
/**
@@ -1286,7 +1286,7 @@ export function extractToolExtraParams(toolData: any, cache?: WeakMap<any, Actio
}
let prettyKey = k.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
if (lowerK === 'time_in_ms' || lowerK === 'delay_ms') prettyKey = 'Delay';
if (lowerK === 'time_in_ms' || lowerK === 'delay_ms') prettyKey = "延迟";
result.push({ key: prettyKey, value: valStr });
}
@@ -1363,7 +1363,7 @@ export function cleanErrorMessage(rawError: any): string {
}
const errorStr = String(rawError).trim();
if (!errorStr) return 'Unknown error';
if (!errorStr) return "未知错误";
// 1. Try regex extraction for "message": "..."
const doubleQuoteMsgMatch = errorStr.match(/"message"\s*:\s*"((?:[^"\\]|\\.)*)"/i);
@@ -1415,5 +1415,5 @@ export function cleanErrorMessage(rawError: any): string {
.replace(/^(?:LLM\s+(?:Request\s+)?Error\s*:\s*)+/i, '')
.trim();
return fallback || 'Unknown error';
return fallback || "未知错误";
}