From 8f3b4fe60c879c55b6de66074bec27dd8f866ecc Mon Sep 17 00:00:00 2001 From: wizardchen Date: Fri, 28 Aug 2026 15:37:35 +0800 Subject: [PATCH] ui(chat): show skill names on tool cards and render sandbox file lists Skill steps were hard to tell apart in the stream, and list_sandbox_files fell through to raw output. Quote the skill or path in the title and add dedicated cards for read_skill and sandbox listings. --- frontend/src/i18n/embed.ts | 24 +++ frontend/src/i18n/locales/en-US.ts | 12 ++ frontend/src/i18n/locales/ko-KR.ts | 12 ++ frontend/src/i18n/locales/ru-RU.ts | 12 ++ frontend/src/i18n/locales/zh-CN.ts | 12 ++ frontend/src/types/tool-results.ts | 36 ++++- .../src/utils/agent-tool-display.test.mjs | 7 + frontend/src/utils/agent-tool-icons.ts | 8 +- frontend/src/utils/skillToolDisplay.test.ts | 79 ++++++++++ frontend/src/utils/skillToolDisplay.ts | 141 ++++++++++++++++++ .../AgentStreamDisplay.style.test.mjs | 4 + .../chat/components/AgentStreamDisplay.vue | 62 +++++++- .../chat/components/ToolResultRenderer.vue | 16 +- .../tool-results/ReadSkillResult.vue | 132 ++++++++++++++++ .../tool-results/SandboxFilesResult.vue | 89 +++++++++++ 15 files changed, 638 insertions(+), 8 deletions(-) create mode 100644 frontend/src/utils/skillToolDisplay.test.ts create mode 100644 frontend/src/utils/skillToolDisplay.ts create mode 100644 frontend/src/views/chat/components/tool-results/ReadSkillResult.vue create mode 100644 frontend/src/views/chat/components/tool-results/SandboxFilesResult.vue diff --git a/frontend/src/i18n/embed.ts b/frontend/src/i18n/embed.ts index 6edd8bd67..3a6bf168a 100644 --- a/frontend/src/i18n/embed.ts +++ b/frontend/src/i18n/embed.ts @@ -335,11 +335,23 @@ const messages = { "queryKnowledgeGraph": "知识图谱查询", "readSkill": "读取技能", "executeSkillScript": "执行技能脚本", + "listSandboxFiles": "列出沙箱文件", + "readSandboxFile": "读取沙箱文件", "shellExec": "执行沙箱命令", "dataAnalysis": "数据分析", "dataSchema": "数据结构", "databaseQuery": "数据库查询" }, + "skillFiles": { + "heading": "技能文件", + "script": "脚本", + "instructions": "技能说明" + }, + "sandboxFiles": { + "found": "找到 {count} 个文件", + "empty": "暂无文件", + "truncated": "列表已截断" + }, "shellExec": { "workDir": "目录", "exitCode": "退出码", @@ -844,11 +856,23 @@ const messages = { "queryKnowledgeGraph": "Knowledge Graph Query", "readSkill": "Read Skill", "executeSkillScript": "Execute Skill Script", + "listSandboxFiles": "List sandbox files", + "readSandboxFile": "Read sandbox file", "shellExec": "Run sandbox command", "dataAnalysis": "Data Analysis", "dataSchema": "Data Schema", "databaseQuery": "Database Query" }, + "skillFiles": { + "heading": "Skill files", + "script": "script", + "instructions": "Instructions" + }, + "sandboxFiles": { + "found": "Found {count} file(s)", + "empty": "No files", + "truncated": "List truncated" + }, "shellExec": { "workDir": "Directory", "exitCode": "Exit code", diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index 4ff206bb1..cfc2b751a 100755 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -5330,11 +5330,23 @@ export default { queryKnowledgeGraph: 'Knowledge Graph Query', readSkill: 'Read Skill', executeSkillScript: 'Execute Skill Script', + listSandboxFiles: 'List sandbox files', + readSandboxFile: 'Read sandbox file', shellExec: 'Run sandbox command', dataAnalysis: 'Data Analysis', dataSchema: 'Data Schema', databaseQuery: 'Database Query' }, + skillFiles: { + heading: 'Skill files', + script: 'script', + instructions: 'Instructions' + }, + sandboxFiles: { + found: 'Found {count} file(s)', + empty: 'No files', + truncated: 'List truncated' + }, shellExec: { workDir: 'Directory', exitCode: 'Exit code', diff --git a/frontend/src/i18n/locales/ko-KR.ts b/frontend/src/i18n/locales/ko-KR.ts index 6ba15a339..b94ecab31 100755 --- a/frontend/src/i18n/locales/ko-KR.ts +++ b/frontend/src/i18n/locales/ko-KR.ts @@ -1308,11 +1308,23 @@ export default { queryKnowledgeGraph: '지식 그래프 조회', readSkill: '스킬 읽기', executeSkillScript: '스킬 스크립트 실행', + listSandboxFiles: '샌드박스 파일 목록', + readSandboxFile: '샌드박스 파일 읽기', shellExec: '샌드박스 명령 실행', dataAnalysis: '데이터 분석', dataSchema: '데이터 구조', databaseQuery: '데이터베이스 조회' }, + skillFiles: { + heading: '스킬 파일', + script: '스크립트', + instructions: '스킬 안내' + }, + sandboxFiles: { + found: '파일 {count}개 발견', + empty: '파일 없음', + truncated: '목록이 잘림' + }, shellExec: { workDir: '디렉터리', exitCode: '종료 코드', diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts index 0433f96dc..8866bbff4 100755 --- a/frontend/src/i18n/locales/ru-RU.ts +++ b/frontend/src/i18n/locales/ru-RU.ts @@ -1308,11 +1308,23 @@ export default { queryKnowledgeGraph: 'Запрос графа знаний', readSkill: 'Чтение навыка', executeSkillScript: 'Выполнение скрипта навыка', + listSandboxFiles: 'Список файлов песочницы', + readSandboxFile: 'Чтение файла песочницы', shellExec: 'Выполнение команды в песочнице', dataAnalysis: 'Анализ данных', dataSchema: 'Структура данных', databaseQuery: 'Запрос к базе данных' }, + skillFiles: { + heading: 'Файлы навыка', + script: 'скрипт', + instructions: 'Инструкции навыка' + }, + sandboxFiles: { + found: 'Найдено файлов: {count}', + empty: 'Нет файлов', + truncated: 'Список обрезан' + }, shellExec: { workDir: 'Каталог', exitCode: 'Код выхода', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index b6a573e53..f77a819ab 100755 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -1310,11 +1310,23 @@ export default { queryKnowledgeGraph: '知识图谱查询', readSkill: '读取技能', executeSkillScript: '执行技能脚本', + listSandboxFiles: '列出沙箱文件', + readSandboxFile: '读取沙箱文件', shellExec: '执行沙箱命令', dataAnalysis: '数据分析', dataSchema: '数据结构', databaseQuery: '数据库查询' }, + skillFiles: { + heading: '技能文件', + script: '脚本', + instructions: '技能说明' + }, + sandboxFiles: { + found: '找到 {count} 个文件', + empty: '暂无文件', + truncated: '列表已截断' + }, shellExec: { workDir: '目录', exitCode: '退出码', diff --git a/frontend/src/types/tool-results.ts b/frontend/src/types/tool-results.ts index 7e58db6a5..74316d965 100644 --- a/frontend/src/types/tool-results.ts +++ b/frontend/src/types/tool-results.ts @@ -26,7 +26,9 @@ export type DisplayType = | 'wiki_replace_text' | 'wiki_rename_page' | 'wiki_delete_page' - | 'shell_exec'; + | 'shell_exec' + | 'list_sandbox_files' + | 'read_skill'; // Search result item export interface SearchResultItem { @@ -338,6 +340,34 @@ export interface ShellExecData { stderr_truncated?: boolean; } +export interface SandboxFileEntry { + name?: string; + path: string; + size?: number; + modified_at?: string; +} + +export interface ListSandboxFilesData { + display_type?: 'list_sandbox_files'; + session_id?: string; + path?: string; + root?: string; + entries?: SandboxFileEntry[]; + count?: number; + truncated?: boolean; +} + +export interface ReadSkillData { + display_type?: 'read_skill'; + skill_name?: string; + file_path?: string; + description?: string; + instructions?: string; + content?: string; + files?: string[]; + skill_dir?: string; +} + // Union type for all wiki edit data export type WikiEditData = WikiWritePageData | WikiReplaceTextData | WikiRenamePageData | WikiDeletePageData; @@ -360,7 +390,9 @@ export type ToolResultData = | WikiReplaceTextData | WikiRenamePageData | WikiDeletePageData - | ShellExecData; + | ShellExecData + | ListSandboxFilesData + | ReadSkillData; // Action data (from index.vue) export interface ActionData { diff --git a/frontend/src/utils/agent-tool-display.test.mjs b/frontend/src/utils/agent-tool-display.test.mjs index 5ded83b92..719324361 100644 --- a/frontend/src/utils/agent-tool-display.test.mjs +++ b/frontend/src/utils/agent-tool-display.test.mjs @@ -30,6 +30,13 @@ test('getAgentToolIconName maps sandbox shell tools to the terminal icon', () => assert.equal(getAgentToolIconName('shell_exec'), 'terminal') }) +test('getAgentToolIconName maps skill and sandbox file tools', () => { + assert.equal(getAgentToolIconName('read_skill'), 'file') + assert.equal(getAgentToolIconName('list_sandbox_files'), 'folder') + assert.equal(getAgentToolIconName('read_sandbox_file'), 'file') + assert.equal(getAgentToolIconName('execute_skill_script'), 'code') +}) + test('getAgentToolIconName maps Wiki tools to semantic search and reading icons', () => { assert.equal(getAgentToolIconName('wiki_search'), 'search') assert.equal(getAgentToolIconName('wiki_read_page'), 'file-search') diff --git a/frontend/src/utils/agent-tool-icons.ts b/frontend/src/utils/agent-tool-icons.ts index a7203e538..7fee1e20e 100644 --- a/frontend/src/utils/agent-tool-icons.ts +++ b/frontend/src/utils/agent-tool-icons.ts @@ -39,9 +39,15 @@ export function getAgentToolIconName( if (toolName.startsWith('mcp_')) { return 'terminal' } - if (toolName === 'shell_exec' || toolName === 'list_sandbox_files' || toolName === 'read_sandbox_file') { + if (toolName === 'shell_exec') { return 'terminal' } + if (toolName === 'list_sandbox_files') { + return 'folder' + } + if (toolName === 'read_sandbox_file' || toolName === 'read_skill') { + return 'file' + } if (toolName === 'execute_skill_script') { return 'code' } diff --git a/frontend/src/utils/skillToolDisplay.test.ts b/frontend/src/utils/skillToolDisplay.test.ts new file mode 100644 index 000000000..a69e9bbb8 --- /dev/null +++ b/frontend/src/utils/skillToolDisplay.test.ts @@ -0,0 +1,79 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { + formatToolTitleWithDetail, + getEventSkillName, + getReadSkillTarget, + getSandboxToolPath, + sandboxFileListItems, + skillFileListItems, + skillScriptTitleCommand, +} from './skillToolDisplay.ts' + +test('formatToolTitleWithDetail quotes a skill or path detail', () => { + assert.equal(formatToolTitleWithDetail('读取技能', 'pdf-processing'), '读取技能:「pdf-processing」') + assert.equal(formatToolTitleWithDetail('读取技能', ' '), '读取技能') +}) + +test('getReadSkillTarget prefers skill/file from tool_data over arguments', () => { + assert.equal( + getReadSkillTarget({ + arguments: { skill_name: 'old', file_path: 'SKILL.md' }, + tool_data: { skill_name: 'pdf-processing', file_path: 'scripts/run.py' }, + }), + 'pdf-processing/scripts/run.py', + ) + assert.equal( + getReadSkillTarget({ arguments: { skill_name: 'brandkit' } }), + 'brandkit', + ) +}) + +test('getEventSkillName parses JSON-encoded arguments', () => { + assert.equal( + getEventSkillName({ arguments: '{"skill_name":"smart-charts"}' }), + 'smart-charts', + ) +}) + +test('skillScriptTitleCommand drops a duplicated skill prefix', () => { + assert.equal( + skillScriptTitleCommand('pdf-processing', 'pdf-processing/scripts/run.py --fast'), + 'scripts/run.py --fast', + ) + assert.equal(skillScriptTitleCommand('pdf-processing', 'ls'), 'ls') +}) + +test('skillFileListItems skips SKILL.md and marks scripts', () => { + const items = skillFileListItems({ + files: ['SKILL.md', 'scripts/run.py', 'docs/guide.md', ''], + }) + assert.deepEqual( + items.map((item) => ({ path: item.path, isScript: item.isScript })), + [ + { path: 'scripts/run.py', isScript: true }, + { path: 'docs/guide.md', isScript: false }, + ], + ) +}) + +test('sandboxFileListItems uses paths relative to the artifact root', () => { + const items = sandboxFileListItems({ + root: '/workspace/output', + path: '/workspace/output', + entries: [ + { name: 'report.html', path: '/workspace/output/report.html', size: 12, modified_at: '2026-08-28T01:02:03Z' }, + { name: 'chart.png', path: '/workspace/output/charts/chart.png', size: 2048 }, + ], + }) + assert.equal(items[0].name, 'report.html') + assert.equal(items[1].name, 'charts/chart.png') + assert.equal(items[0].size, 12) +}) + +test('getSandboxToolPath reads the listed directory', () => { + assert.equal( + getSandboxToolPath({ tool_data: { path: '/workspace/output/charts' } }), + '/workspace/output/charts', + ) +}) diff --git a/frontend/src/utils/skillToolDisplay.ts b/frontend/src/utils/skillToolDisplay.ts new file mode 100644 index 000000000..b76843d10 --- /dev/null +++ b/frontend/src/utils/skillToolDisplay.ts @@ -0,0 +1,141 @@ +/** Display helpers for skill and sandbox file tools in the agent stream. */ + +const SKILL_SCRIPT_EXT = /\.(py|sh|bash|js|ts|rb|pl|php)$/i +const SKILL_MD = /(^|\/)SKILL\.md$/i + +type ToolEventLike = { + tool_data?: unknown + arguments?: unknown +} + +export type SkillFileListItem = { + name: string + path: string + size?: number + modifiedAt?: string + isScript?: boolean +} + +function asRecord(value: unknown): Record { + if (!value) return {} + if (typeof value === 'string') { + try { + const parsed = JSON.parse(value) + if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { + return parsed as Record + } + } catch { + return {} + } + } + if (typeof value === 'object' && !Array.isArray(value)) { + return value as Record + } + return {} +} + +function stringField(record: Record, key: string): string { + const value = record[key] + return typeof value === 'string' ? value.trim() : value == null ? '' : String(value).trim() +} + +function eventFields(event: ToolEventLike | null | undefined): Record { + return { + ...asRecord(event?.arguments), + ...asRecord(event?.tool_data), + } +} + +export function isSkillScriptPath(path: string): boolean { + return SKILL_SCRIPT_EXT.test(path) +} + +export function formatToolTitleWithDetail(baseTitle: string, detail: string): string { + const trimmed = detail.trim() + if (!trimmed) return baseTitle + return `${baseTitle}:「${trimmed}」` +} + +export function getEventSkillName(event: ToolEventLike | null | undefined): string { + return stringField(eventFields(event), 'skill_name') +} + +export function getReadSkillTarget(event: ToolEventLike | null | undefined): string { + const fields = eventFields(event) + const skill = stringField(fields, 'skill_name') + const file = stringField(fields, 'file_path') + if (skill && file) return `${skill}/${file}` + return skill || file +} + +export function getSandboxToolPath(event: ToolEventLike | null | undefined): string { + return stringField(eventFields(event), 'path') +} + +export function skillScriptTitleCommand(skillName: string, command: string): string { + const trimmed = command.trim() + if (!trimmed) return '' + if (skillName && trimmed.startsWith(`${skillName}/`)) { + return trimmed.slice(skillName.length + 1) + } + return trimmed +} + +function basename(path: string): string { + const trimmed = path.replace(/\/+$/, '') + const parts = trimmed.split('/').filter(Boolean) + return parts[parts.length - 1] || trimmed || path +} + +function relativeToRoot(path: string, root: string): string { + if (!root) return path + if (path === root) return '.' + const prefix = root.endsWith('/') ? root : `${root}/` + if (path.startsWith(prefix)) return path.slice(prefix.length) + return path +} + +export function skillFileListItems(data: unknown): SkillFileListItem[] { + const record = asRecord(data) + const files = record.files + if (!Array.isArray(files)) return [] + return files + .map((file) => String(file || '').trim()) + .filter(Boolean) + .filter((path) => !SKILL_MD.test(path)) + .map((path) => ({ + name: basename(path), + path, + isScript: isSkillScriptPath(path), + })) +} + +export function sandboxFileListItems(data: unknown): SkillFileListItem[] { + const record = asRecord(data) + const root = stringField(record, 'root') || stringField(record, 'path') + const entries = record.entries + if (!Array.isArray(entries)) return [] + return entries.map((entry) => { + const item = asRecord(entry) + const path = stringField(item, 'path') + const name = stringField(item, 'name') || basename(path) + const sizeRaw = item.size + const size = typeof sizeRaw === 'number' && Number.isFinite(sizeRaw) ? sizeRaw : undefined + return { + name: relativeToRoot(path, root) || name, + path, + size, + modifiedAt: stringField(item, 'modified_at'), + isScript: isSkillScriptPath(path || name), + } + }) +} + +export function formatSandboxModifiedAt(value: string): string { + const trimmed = value.trim() + if (!trimmed) return '' + const date = new Date(trimmed) + if (Number.isNaN(date.getTime())) return trimmed + const pad = (n: number) => String(n).padStart(2, '0') + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}` +} diff --git a/frontend/src/views/chat/components/AgentStreamDisplay.style.test.mjs b/frontend/src/views/chat/components/AgentStreamDisplay.style.test.mjs index 0b3359493..aeb0c5cd4 100644 --- a/frontend/src/views/chat/components/AgentStreamDisplay.style.test.mjs +++ b/frontend/src/views/chat/components/AgentStreamDisplay.style.test.mjs @@ -59,6 +59,10 @@ test('tool rows use line icon names instead of legacy asset masks', () => { assert.match(source, /wiki_search: 'agentEditor\.tools\.wikiSearch'/) assert.match(source, /wiki_read_page: 'agentEditor\.tools\.wikiReadPage'/) assert.match(source, /wiki_read_source_doc: 'agentStream\.tools\.wikiReadSourceDoc'/) + assert.match(source, /list_sandbox_files: 'agentStream\.tools\.listSandboxFiles'/) + assert.match(source, /read_sandbox_file: 'agentStream\.tools\.readSandboxFile'/) + assert.match(source, /getReadSkillTarget/) + assert.match(source, /tool_name === 'list_sandbox_files'/) assert.match(source, /toolName === 'get_document_content' \|\| toolName === 'wiki_read_source_doc'/) assert.doesNotMatch(source, /getToolIcon\(event\.tool_name\)/) }) diff --git a/frontend/src/views/chat/components/AgentStreamDisplay.vue b/frontend/src/views/chat/components/AgentStreamDisplay.vue index b6f0c82b3..18cd03811 100644 --- a/frontend/src/views/chat/components/AgentStreamDisplay.vue +++ b/frontend/src/views/chat/components/AgentStreamDisplay.vue @@ -564,6 +564,13 @@ import type { ProtectedFileAccessContext } from '@/utils/protectedFileAccess'; import { unwrapFinalAnswerWrappers, thinkingEqualsAnswer } from '@/utils/finalAnswer'; import { getAgentToolIconName } from '@/utils/agent-tool-icons'; import { getQueryText, getWikiPageText } from '@/utils/agent-tool-display'; +import { + formatToolTitleWithDetail, + getEventSkillName, + getReadSkillTarget, + getSandboxToolPath, + skillScriptTitleCommand, +} from '@/utils/skillToolDisplay'; import { previewShellCommand } from '@/utils/shellExecResult'; import type { DisplayType } from '@/types/tool-results'; import { parseWikiToolReferences } from '@/utils/wikiToolReferences'; @@ -631,6 +638,8 @@ const TOOL_NAME_KEYS: Record = { query_knowledge_graph: 'agentStream.tools.queryKnowledgeGraph', read_skill: 'agentStream.tools.readSkill', execute_skill_script: 'agentStream.tools.executeSkillScript', + list_sandbox_files: 'agentStream.tools.listSandboxFiles', + read_sandbox_file: 'agentStream.tools.readSandboxFile', shell_exec: 'agentStream.tools.shellExec', data_analysis: 'agentStream.tools.dataAnalysis', data_schema: 'agentStream.tools.dataSchema', @@ -1074,6 +1083,12 @@ const resolveToolDisplayType = (event: any): DisplayType | undefined => { if (event?.tool_name === 'shell_exec' || event?.tool_name === 'execute_skill_script') { return 'shell_exec' } + if (event?.tool_name === 'list_sandbox_files' && event?.success !== false) { + return 'list_sandbox_files' + } + if (event?.tool_name === 'read_skill' && event?.success !== false) { + return 'read_skill' + } return undefined }; @@ -2678,7 +2693,19 @@ const getToolTitle = (event: any): string => { if (event.tool_name === 'wiki_search' || event.tool_name === 'wiki_read_page') { return `${getLocalizedToolName(event.tool_name)}...`; } - if (event.tool_name === 'shell_exec' || event.tool_name === 'execute_skill_script') { + if (event.tool_name === 'read_skill') { + const name = getLocalizedToolName(event.tool_name); + return `${formatToolTitleWithDetail(name, getReadSkillTarget(event))}...`; + } + if (event.tool_name === 'execute_skill_script') { + const name = getLocalizedToolName(event.tool_name); + return `${formatToolTitleWithDetail(name, getEventSkillName(event))}...`; + } + if (event.tool_name === 'list_sandbox_files' || event.tool_name === 'read_sandbox_file') { + const name = getLocalizedToolName(event.tool_name); + return `${formatToolTitleWithDetail(name, getSandboxToolPath(event))}...`; + } + if (event.tool_name === 'shell_exec') { return t('agentStream.toolStatus.shellExecRunning'); } const localizedName = getLocalizedToolName(event.tool_name); @@ -2776,7 +2803,22 @@ const getToolTitle = (event: any): string => { return pageLabel ? `${baseTitle}:「${sanitizeForDisplay(pageLabel)}」` : baseTitle; } - if (toolName === 'shell_exec' || toolName === 'execute_skill_script') { + if (toolName === 'read_skill') { + return formatToolTitleWithDetail(getToolDescription(event), getReadSkillTarget(event)); + } + + if (toolName === 'list_sandbox_files' || toolName === 'read_sandbox_file') { + return formatToolTitleWithDetail(getToolDescription(event), getSandboxToolPath(event)); + } + + if (toolName === 'execute_skill_script') { + const command = previewShellCommand(skillScriptCommandLabel(event)) + const baseTitle = formatToolTitleWithDetail(getToolDescription(event), getEventSkillName(event)) + const rest = skillScriptTitleCommand(getEventSkillName(event), command) + return rest ? `${baseTitle}:${rest}` : baseTitle + } + + if (toolName === 'shell_exec') { const command = previewShellCommand(skillScriptCommandLabel(event)) const baseTitle = getToolDescription(event) return command ? `${baseTitle}:${command}` : baseTitle @@ -2812,7 +2854,19 @@ const getToolDescription = (event: any): string => { if (event.tool_name === 'query_understand') { return t('agentStream.toolStatus.queryUnderstanding'); } - if (event.tool_name === 'shell_exec' || event.tool_name === 'execute_skill_script') { + if (event.tool_name === 'read_skill') { + const name = getLocalizedToolName(event.tool_name); + return `${formatToolTitleWithDetail(name, getReadSkillTarget(event))}...`; + } + if (event.tool_name === 'execute_skill_script') { + const name = getLocalizedToolName(event.tool_name); + return `${formatToolTitleWithDetail(name, getEventSkillName(event))}...`; + } + if (event.tool_name === 'list_sandbox_files' || event.tool_name === 'read_sandbox_file') { + const name = getLocalizedToolName(event.tool_name); + return `${formatToolTitleWithDetail(name, getSandboxToolPath(event))}...`; + } + if (event.tool_name === 'shell_exec') { return t('agentStream.toolStatus.shellExecRunning'); } const localizedName = getLocalizedToolName(event.tool_name); @@ -2845,7 +2899,7 @@ const getToolDescription = (event: any): string => { return success ? t('agentStream.toolStatus.attachmentParsingDone') : t('agentStream.toolStatus.attachmentParsingFailed'); } else if (toolName === 'query_understand') { return success ? t('agentStream.toolStatus.queryUnderstandDone') : t('agentStream.toolStatus.calledFailed', { name: getLocalizedToolName(toolName) }); - } else if (toolName === 'shell_exec' || toolName === 'execute_skill_script') { + } else if (toolName === 'shell_exec' || toolName === 'execute_skill_script' || toolName === 'read_skill' || toolName === 'list_sandbox_files' || toolName === 'read_sandbox_file') { const localizedName = getLocalizedToolName(toolName); return success ? localizedName : t('agentStream.toolStatus.calledFailed', { name: localizedName }); } else { diff --git a/frontend/src/views/chat/components/ToolResultRenderer.vue b/frontend/src/views/chat/components/ToolResultRenderer.vue index da08bb6e9..8f7b0e2ae 100644 --- a/frontend/src/views/chat/components/ToolResultRenderer.vue +++ b/frontend/src/views/chat/components/ToolResultRenderer.vue @@ -53,6 +53,16 @@ :arguments="toolArguments" /> + + + +
@@ -83,7 +93,9 @@ import type { GrepResultsData, KnowledgeChunksListData, WikiEditData, - ShellExecData + ShellExecData, + ListSandboxFilesData, + ReadSkillData } from '@/types/tool-results'; import SearchResults from './tool-results/SearchResults.vue'; @@ -101,6 +113,8 @@ import GrepResults from './tool-results/GrepResults.vue'; import KnowledgeChunksList from './tool-results/KnowledgeChunksList.vue'; import WikiEditResult from './tool-results/WikiEditResult.vue'; import ShellExecResult from './tool-results/ShellExecResult.vue'; +import SandboxFilesResult from './tool-results/SandboxFilesResult.vue'; +import ReadSkillResult from './tool-results/ReadSkillResult.vue'; interface Props { displayType?: DisplayType; diff --git a/frontend/src/views/chat/components/tool-results/ReadSkillResult.vue b/frontend/src/views/chat/components/tool-results/ReadSkillResult.vue new file mode 100644 index 000000000..b5c70b6fe --- /dev/null +++ b/frontend/src/views/chat/components/tool-results/ReadSkillResult.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/frontend/src/views/chat/components/tool-results/SandboxFilesResult.vue b/frontend/src/views/chat/components/tool-results/SandboxFilesResult.vue new file mode 100644 index 000000000..fe9031a5e --- /dev/null +++ b/frontend/src/views/chat/components/tool-results/SandboxFilesResult.vue @@ -0,0 +1,89 @@ + + + + +