Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions frontend/src/i18n/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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": "退出码",
Expand Down Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/i18n/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/i18n/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '종료 코드',
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/i18n/locales/ru-RU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: 'Код выхода',
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/i18n/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '退出码',
Expand Down
36 changes: 34 additions & 2 deletions frontend/src/types/tool-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand All @@ -360,7 +390,9 @@ export type ToolResultData =
| WikiReplaceTextData
| WikiRenamePageData
| WikiDeletePageData
| ShellExecData;
| ShellExecData
| ListSandboxFilesData
| ReadSkillData;

// Action data (from index.vue)
export interface ActionData {
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/utils/agent-tool-display.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/utils/agent-tool-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand Down
79 changes: 79 additions & 0 deletions frontend/src/utils/skillToolDisplay.test.ts
Original file line number Diff line number Diff line change
@@ -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',
)
})
Loading