重构: P0前端架构(useAiSend/useAiEvents/useToolCard拆分+status union全)
squash合并: - P0-1 useAiSend审批抽离(useAiApproval) - P0-2 useAiEvents switch拆3域handler - P0-3 useToolCard formatToolResult策略表 - P0-4 status union(Idea+Task+Project+Workflow+AiToolCall)
This commit is contained in:
+228
-156
@@ -329,7 +329,132 @@ export function formatFileInfoResult(r: ToolResult): string {
|
||||
* 通用工具结果格式化(非文件类工具)。
|
||||
* UX-260616-04:统一 switch 覆盖全部工具产出人类可读摘要,不再走裸 JSON 兜底。
|
||||
* read_file/list_directory/write_file 有模板特化分支,不进此函数。
|
||||
*
|
||||
* P0-3: switch 17 case 拆为策略表 TOOL_FORMATTERS(name → 纯函数),formatToolResult 查表分发。
|
||||
* 新增工具只在此表加一行,零改 formatToolResult 主体。逻辑与原 switch 等价(零行为变更)。
|
||||
*/
|
||||
|
||||
// list_* 系列(list_projects/list_tasks/list_ideas/list_trash)共享格式化:
|
||||
// 渲染结构化列表替代裸 JSON,头行按 name 选 i18n key,条目取 name/title/id + status/stack 标注。
|
||||
function formatListResult(r: ToolResult, name: string): string {
|
||||
// UX-260618-12: list_* 后端返 {items,total,has_more} 对象(非数组)
|
||||
const items = Array.isArray(r) ? r : (Array.isArray(r.items) ? r.items : [])
|
||||
const total = typeof r.total === 'number' ? r.total : items.length
|
||||
const head = name === 'list_projects' ? t('aiTool.projectCount', { n: total })
|
||||
: name === 'list_tasks' ? t('aiTool.taskCount', { n: total })
|
||||
: name === 'list_ideas' ? t('aiTool.ideaCount', { n: total })
|
||||
: t('aiTool.trashCount', { n: total })
|
||||
const lines = items.slice(0, 10).map((it: Record<string, unknown>) => {
|
||||
const label = typeof it.name === 'string' ? it.name
|
||||
: typeof it.title === 'string' ? it.title
|
||||
: typeof it.id === 'string' ? it.id
|
||||
: ''
|
||||
const status = typeof it.status === 'string' ? ` [${it.status}]` : ''
|
||||
const stack = typeof it.stack === 'string' ? ` (${it.stack})` : ''
|
||||
return label ? `- ${label}${status}${stack}` : ''
|
||||
}).filter(Boolean)
|
||||
const parts = [head, ...lines]
|
||||
if (r.has_more) parts.push(t('aiTool.listHasMore', { n: items.length }))
|
||||
return parts.join('\n')
|
||||
}
|
||||
|
||||
/** advance_task 共享格式化(body + summary 一致): review_rounds>0 显退回累加轮数,否则首推原文案 */
|
||||
function formatAdvanceTask(r: ToolResult): string {
|
||||
// UX-260618-09: review_rounds>0 显退回累加轮数,=0 回退原文案(首推无噪音)
|
||||
// UX-260618-14: 后端返 TaskRecord(含 title),优先显 title,无则回退 id(防裸 UUID)
|
||||
return typeof r.review_rounds === 'number' && r.review_rounds > 0
|
||||
? t('aiTool.advancedTaskWithRounds', { title: r.title || r.id || '', status: r.status || '', n: r.review_rounds })
|
||||
: t('aiTool.advancedTask', { title: r.title || r.id || '', status: r.status || '' })
|
||||
}
|
||||
|
||||
/** update_task 共享格式化: updated!==false 显字段更新,否则通用 updated */
|
||||
function formatUpdateTask(r: ToolResult): string {
|
||||
return r.updated !== false
|
||||
? t('aiTool.updatedTaskField', { id: r.id || '', field: r.field || '' })
|
||||
: t('aiTool.updated')
|
||||
}
|
||||
|
||||
/** delete_task/delete_project 共享格式化: deleted 标志 */
|
||||
function formatDelete(r: ToolResult): string {
|
||||
return r.deleted ? t('aiTool.deleted') : t('aiTool.deleteIneffective')
|
||||
}
|
||||
|
||||
/** delete_file 共享格式化: permanent 走永久删,否则软删带 backup_path */
|
||||
function formatDeleteFile(r: ToolResult): string {
|
||||
return r.permanent
|
||||
? t('aiTool.deleteFilePermanent', { path: r.path || '' })
|
||||
: t('aiTool.deleteFileSoft', { path: r.path || '', backup_path: r.backup_path || '' })
|
||||
}
|
||||
|
||||
/** patch_file 共享格式化: changed===false 显无变化,否则显 size_diff */
|
||||
function formatPatchFile(r: ToolResult): string {
|
||||
return r.changed === false
|
||||
? t('aiTool.patchedNoChange', { path: r.path || '' })
|
||||
: t('aiTool.patchedFile', { path: r.path || '', diff: formatSizeDiff(r.size_diff) })
|
||||
}
|
||||
|
||||
/** append_file 共享格式化: path + bytes_written */
|
||||
function formatAppendFile(r: ToolResult): string {
|
||||
return t('aiTool.appendedTo', { path: r.path || '', bytes: formatBytes(r.bytes_written) })
|
||||
}
|
||||
|
||||
/** search_files 共享格式化: total/results.length 取命中数 */
|
||||
function formatSearchFiles(r: ToolResult): string {
|
||||
const n = typeof r.total === 'number' ? r.total : (Array.isArray(r.results) ? r.results.length : 0)
|
||||
return t('aiTool.foundN', { n })
|
||||
}
|
||||
|
||||
/**
|
||||
* rename_file 格式化(body 版): renamed===false 走 formatJson 兜底(summary 版走空串)。
|
||||
* 故 body 与 summary 分别登记不同 formatter。
|
||||
*/
|
||||
function formatRenameFile(r: ToolResult): string {
|
||||
return r.renamed
|
||||
? (r.cross_volume
|
||||
? t('aiTool.renamedCrossVolume', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) })
|
||||
: t('aiTool.renamedWithBytes', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) }))
|
||||
: formatJson(r)
|
||||
}
|
||||
|
||||
/** bind_directory 格式化(body 版): bound===false 走 formatJson 兜底(summary 版走空串) */
|
||||
function formatBindDir(r: ToolResult): string {
|
||||
return r.bound
|
||||
? t('aiTool.boundDir', { path: r.path || '', stack: r.stack || '' })
|
||||
: formatJson(r)
|
||||
}
|
||||
|
||||
/** run_workflow 格式化(body 版):无 execution_id 走 formatJson,否则显完整三要素(task_id/target_status/exec) */
|
||||
function formatRunWorkflow(r: ToolResult): string {
|
||||
// UX-260618-05: 后端返 {task_id,target_status,execution_id,status,note},显 execution_id 对应执行实例
|
||||
const execId = typeof r.execution_id === 'string' && r.execution_id
|
||||
? r.execution_id
|
||||
: (typeof r.id === 'string' ? r.id : '')
|
||||
if (!execId) return formatJson(r)
|
||||
return t('aiTool.workflowTriggered', { id: r.task_id || '', status: r.target_status || '', exec: execId })
|
||||
}
|
||||
|
||||
/** 工具结果格式化策略表(name → 纯函数)。新增工具在此加一行,不动 formatToolResult 主体。 */
|
||||
const TOOL_FORMATTERS: Record<string, (r: ToolResult) => string> = {
|
||||
list_projects: r => formatListResult(r, 'list_projects'),
|
||||
list_tasks: r => formatListResult(r, 'list_tasks'),
|
||||
list_ideas: r => formatListResult(r, 'list_ideas'),
|
||||
list_trash: r => formatListResult(r, 'list_trash'),
|
||||
write_file: r => `${r.path} (${formatBytes(r.bytes_written)})`,
|
||||
update_task: formatUpdateTask,
|
||||
advance_task: formatAdvanceTask,
|
||||
delete_task: formatDelete,
|
||||
delete_file: formatDeleteFile,
|
||||
run_command: formatCommandResult,
|
||||
patch_file: formatPatchFile,
|
||||
append_file: formatAppendFile,
|
||||
rename_file: formatRenameFile,
|
||||
search_files: formatSearchFiles,
|
||||
file_info: formatFileInfoResult,
|
||||
bind_directory: formatBindDir,
|
||||
run_workflow: formatRunWorkflow,
|
||||
http_request: formatHttpResult,
|
||||
}
|
||||
|
||||
export function formatToolResult(tc: AiToolCallInfo): string {
|
||||
const r = parseResult(tc.result)
|
||||
if (!r) {
|
||||
@@ -345,86 +470,8 @@ export function formatToolResult(tc: AiToolCallInfo): string {
|
||||
}
|
||||
return raw
|
||||
}
|
||||
switch (tc.name) {
|
||||
case 'list_projects':
|
||||
case 'list_tasks':
|
||||
case 'list_ideas':
|
||||
case 'list_trash': {
|
||||
// UX-260618-12: list_* 后端返 {items,total,has_more} 对象(非数组),渲染结构化列表替代裸 JSON
|
||||
const items = Array.isArray(r) ? r : (Array.isArray(r.items) ? r.items : [])
|
||||
const total = typeof r.total === 'number' ? r.total : items.length
|
||||
const head = tc.name === 'list_projects' ? t('aiTool.projectCount', { n: total })
|
||||
: tc.name === 'list_tasks' ? t('aiTool.taskCount', { n: total })
|
||||
: tc.name === 'list_ideas' ? t('aiTool.ideaCount', { n: total })
|
||||
: t('aiTool.trashCount', { n: total })
|
||||
const lines = items.slice(0, 10).map((it: Record<string, unknown>) => {
|
||||
const label = typeof it.name === 'string' ? it.name
|
||||
: typeof it.title === 'string' ? it.title
|
||||
: typeof it.id === 'string' ? it.id
|
||||
: ''
|
||||
const status = typeof it.status === 'string' ? ` [${it.status}]` : ''
|
||||
const stack = typeof it.stack === 'string' ? ` (${it.stack})` : ''
|
||||
return label ? `- ${label}${status}${stack}` : ''
|
||||
}).filter(Boolean)
|
||||
const parts = [head, ...lines]
|
||||
if (r.has_more) parts.push(t('aiTool.listHasMore', { n: items.length }))
|
||||
return parts.join('\n')
|
||||
}
|
||||
case 'write_file':
|
||||
return `${r.path} (${formatBytes(r.bytes_written)})`
|
||||
case 'update_task':
|
||||
return r.updated !== false
|
||||
? t('aiTool.updatedTaskField', { id: r.id || '', field: r.field || '' })
|
||||
: t('aiTool.updated')
|
||||
case 'advance_task':
|
||||
// UX-260618-09: review_rounds>0 显退回累加轮数,=0 回退原文案(首推无噪音)
|
||||
// UX-260618-14: 后端返 TaskRecord(含 title),优先显 title,无则回退 id(防裸 UUID)
|
||||
return typeof r.review_rounds === 'number' && r.review_rounds > 0
|
||||
? t('aiTool.advancedTaskWithRounds', { title: r.title || r.id || '', status: r.status || '', n: r.review_rounds })
|
||||
: t('aiTool.advancedTask', { title: r.title || r.id || '', status: r.status || '' })
|
||||
case 'delete_task':
|
||||
return r.deleted ? t('aiTool.deleted') : t('aiTool.deleteIneffective')
|
||||
case 'delete_file':
|
||||
return r.permanent
|
||||
? t('aiTool.deleteFilePermanent', { path: r.path || '' })
|
||||
: t('aiTool.deleteFileSoft', { path: r.path || '', backup_path: r.backup_path || '' })
|
||||
case 'run_command':
|
||||
return formatCommandResult(r)
|
||||
case 'patch_file':
|
||||
return r.changed === false
|
||||
? t('aiTool.patchedNoChange', { path: r.path || '' })
|
||||
: t('aiTool.patchedFile', { path: r.path || '', diff: formatSizeDiff(r.size_diff) })
|
||||
case 'append_file':
|
||||
return t('aiTool.appendedTo', { path: r.path || '', bytes: formatBytes(r.bytes_written) })
|
||||
case 'rename_file':
|
||||
return r.renamed
|
||||
? (r.cross_volume
|
||||
? t('aiTool.renamedCrossVolume', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) })
|
||||
: t('aiTool.renamedWithBytes', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) }))
|
||||
: formatJson(r)
|
||||
case 'search_files': {
|
||||
const n = typeof r.total === 'number' ? r.total : (Array.isArray(r.results) ? r.results.length : 0)
|
||||
return t('aiTool.foundN', { n })
|
||||
}
|
||||
case 'file_info':
|
||||
return formatFileInfoResult(r)
|
||||
case 'bind_directory':
|
||||
return r.bound
|
||||
? t('aiTool.boundDir', { path: r.path || '', stack: r.stack || '' })
|
||||
: formatJson(r)
|
||||
case 'run_workflow': {
|
||||
// UX-260618-05: 后端返 {task_id,target_status,execution_id,status,note},显 execution_id 对应执行实例
|
||||
const execId = typeof r.execution_id === 'string' && r.execution_id
|
||||
? r.execution_id
|
||||
: (typeof r.id === 'string' ? r.id : '')
|
||||
if (!execId) return formatJson(r)
|
||||
return t('aiTool.workflowTriggered', { id: r.task_id || '', status: r.target_status || '', exec: execId })
|
||||
}
|
||||
case 'http_request':
|
||||
return formatHttpResult(r)
|
||||
default:
|
||||
return formatJson(r)
|
||||
}
|
||||
const fn = TOOL_FORMATTERS[tc.name]
|
||||
return fn ? fn(r) : formatJson(r)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,85 +512,110 @@ export function formatHttpResult(r: ToolResult): string {
|
||||
* completed 工具结果的一句话摘要(折叠态 header 也能看到,避免光秃秃)。
|
||||
* UX-260616-04:覆盖范围与 formatToolResult 对齐(header+body 一致);
|
||||
* read_file/list_directory/write_file 有模板特化分支,header 摘要仍显示关键计数。
|
||||
*
|
||||
* P0-3: switch 拆为策略表 TOOL_SUMMARIES(name → 纯函数),toolResultSummary 查表分发。
|
||||
* 与 TOOL_FORMATTERS 不共用:summary 版部分 case 输出更短(create/update_project/restore/purge 仅 summary 有;
|
||||
* rename/bind 的 false 分支返空串而非 formatJson;run_workflow 用 workflowTriggeredShort;list_* 多 has_more 判断差异)。
|
||||
* 共享语义的纯函数(formatAdvanceTask/formatUpdateTask/formatDelete/formatDeleteFile/formatPatchFile/
|
||||
* formatAppendFile/formatSearchFiles)两个表共用,DRY。零行为变更。
|
||||
*/
|
||||
|
||||
/** list_* summary 版:n=0 返空串(折叠态无噪音),按 name 选 i18n key */
|
||||
function summaryList(r: ToolResult, name: string): string {
|
||||
// UX-260618-10/12: 后端返 {items,total,has_more} 对象(非数组),原 Array.isArray(r) 恒 false→折叠态无计数
|
||||
const n = Array.isArray(r) ? r.length
|
||||
: (typeof r.total === 'number' ? r.total
|
||||
: (Array.isArray(r.items) ? r.items.length : 0))
|
||||
if (!n) return ''
|
||||
const key = name === 'list_projects' ? 'projectCount'
|
||||
: name === 'list_tasks' ? 'taskCount'
|
||||
: name === 'list_ideas' ? 'ideaCount'
|
||||
: 'trashCount'
|
||||
return t(`aiTool.${key}`, { n })
|
||||
}
|
||||
|
||||
/** run_command summary 版:仅 exit_code + duration_ms 一行(不带输出) */
|
||||
function summaryRunCommand(r: ToolResult): string {
|
||||
const code = typeof r.exit_code === 'number' ? r.exit_code : -1
|
||||
const ms = typeof r.duration_ms === 'number' ? r.duration_ms : 0
|
||||
return code === 0 ? t('aiTool.commandOk', { ms }) : t('aiTool.commandFailed', { code })
|
||||
}
|
||||
|
||||
/** file_info summary 版:存在性 + 大小(不带 modified/lines/binary extras) */
|
||||
function summaryFileInfo(r: ToolResult): string {
|
||||
return r.exists === false
|
||||
? t('aiTool.fileInfoMissing', { path: r.path || '' })
|
||||
: t('aiTool.fileInfoSize', { path: r.path || '', size: formatBytes(typeof r.size === 'number' ? r.size : 0) })
|
||||
}
|
||||
|
||||
/** rename_file summary 版:renamed===false 返空串(body 版走 formatJson) */
|
||||
function summaryRenameFile(r: ToolResult): string {
|
||||
return r.renamed
|
||||
? (r.cross_volume
|
||||
? t('aiTool.renamedCrossVolume', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) })
|
||||
: t('aiTool.renamedWithBytes', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) }))
|
||||
: ''
|
||||
}
|
||||
|
||||
/** bind_directory summary 版:bound===false 返空串(body 版走 formatJson) */
|
||||
function summaryBindDir(r: ToolResult): string {
|
||||
return r.bound
|
||||
? t('aiTool.boundDir', { path: r.path || '', stack: r.stack || '' })
|
||||
: ''
|
||||
}
|
||||
|
||||
/** run_workflow summary 版:无 execution_id 返空串,有则显 workflowTriggeredShort(body 版显完整三要素) */
|
||||
function summaryRunWorkflow(r: ToolResult): string {
|
||||
// UX-260618-05: header 副标题显 execution_id 概要(原 workflowHint「请到工作流页面运行」误导——工作流已触发)
|
||||
const execId = typeof r.execution_id === 'string' && r.execution_id
|
||||
? r.execution_id
|
||||
: (typeof r.id === 'string' ? r.id : '')
|
||||
return execId ? t('aiTool.workflowTriggeredShort', { exec: execId }) : ''
|
||||
}
|
||||
|
||||
/** http_request summary 版:仅状态码标题(method + status,不带 url/body) */
|
||||
function summaryHttpRequest(r: ToolResult): string {
|
||||
const status = typeof r.status === 'number' ? r.status : 0
|
||||
const method = typeof r.method === 'string' ? r.method : 'HTTP'
|
||||
const ms = typeof r.elapsed_ms === 'number' ? r.elapsed_ms : 0
|
||||
const ok = status >= 200 && status < 300
|
||||
return ok
|
||||
? t('aiTool.httpOk', { method, status, ms })
|
||||
: t('aiTool.httpFailed', { method, status })
|
||||
}
|
||||
|
||||
/** 工具结果摘要策略表(name → 纯函数)。仅 summary 独有的工具(create/update_project/restore/purge)在此登记。 */
|
||||
const TOOL_SUMMARIES: Record<string, (r: ToolResult) => string> = {
|
||||
list_projects: r => summaryList(r, 'list_projects'),
|
||||
list_tasks: r => summaryList(r, 'list_tasks'),
|
||||
list_ideas: r => summaryList(r, 'list_ideas'),
|
||||
list_trash: r => summaryList(r, 'list_trash'),
|
||||
create_project: r => r.name ? t('aiTool.createdWithName', { name: r.name }) : t('aiTool.created'),
|
||||
create_idea: r => r.title ? t('aiTool.createdWithName', { name: r.title }) : t('aiTool.created'),
|
||||
create_task: r => r.title ? t('aiTool.createdWithName', { name: r.title }) : t('aiTool.created'),
|
||||
update_project: r => r.field ? t('aiTool.updatedField', { field: r.field }) : t('aiTool.updated'),
|
||||
update_task: formatUpdateTask,
|
||||
advance_task: formatAdvanceTask,
|
||||
delete_project: formatDelete,
|
||||
delete_task: formatDelete,
|
||||
delete_file: formatDeleteFile,
|
||||
restore_project: r => r.restored ? t('aiTool.restored') : t('aiTool.restoreIneffective'),
|
||||
purge_project: r => r.purged ? t('aiTool.purged') : t('aiTool.purgeIneffective'),
|
||||
run_command: summaryRunCommand,
|
||||
patch_file: formatPatchFile,
|
||||
append_file: formatAppendFile,
|
||||
rename_file: summaryRenameFile,
|
||||
search_files: formatSearchFiles,
|
||||
file_info: summaryFileInfo,
|
||||
bind_directory: summaryBindDir,
|
||||
run_workflow: summaryRunWorkflow,
|
||||
http_request: summaryHttpRequest,
|
||||
}
|
||||
|
||||
export function toolResultSummary(tc: AiToolCallInfo): string {
|
||||
if (tc.status !== 'completed') return ''
|
||||
const r = parseResult(tc.result)
|
||||
if (!r) return ''
|
||||
switch (tc.name) {
|
||||
case 'list_tasks':
|
||||
case 'list_projects':
|
||||
case 'list_ideas':
|
||||
case 'list_trash': {
|
||||
// UX-260618-10/12: 后端返 {items,total,has_more} 对象(非数组),原 Array.isArray(r) 恒 false→折叠态无计数
|
||||
const n = Array.isArray(r) ? r.length
|
||||
: (typeof r.total === 'number' ? r.total
|
||||
: (Array.isArray(r.items) ? r.items.length : 0))
|
||||
if (!n) return ''
|
||||
const key = tc.name === 'list_projects' ? 'projectCount'
|
||||
: tc.name === 'list_tasks' ? 'taskCount'
|
||||
: tc.name === 'list_ideas' ? 'ideaCount'
|
||||
: 'trashCount'
|
||||
return t(`aiTool.${key}`, { n })
|
||||
}
|
||||
case 'create_project': return r.name ? t('aiTool.createdWithName', { name: r.name }) : t('aiTool.created')
|
||||
case 'create_idea':
|
||||
case 'create_task': return r.title ? t('aiTool.createdWithName', { name: r.title }) : t('aiTool.created')
|
||||
case 'update_project': return r.field ? t('aiTool.updatedField', { field: r.field }) : t('aiTool.updated')
|
||||
case 'update_task': return r.updated !== false
|
||||
? t('aiTool.updatedTaskField', { id: r.id || '', field: r.field || '' })
|
||||
: t('aiTool.updated')
|
||||
case 'advance_task': return typeof r.review_rounds === 'number' && r.review_rounds > 0
|
||||
// UX-260618-14: 优先显 title(后端返 TaskRecord),无则回退 id,防裸 UUID
|
||||
? t('aiTool.advancedTaskWithRounds', { title: r.title || r.id || '', status: r.status || '', n: r.review_rounds })
|
||||
: t('aiTool.advancedTask', { title: r.title || r.id || '', status: r.status || '' })
|
||||
case 'delete_project':
|
||||
case 'delete_task': return r.deleted ? t('aiTool.deleted') : t('aiTool.deleteIneffective')
|
||||
case 'delete_file': return r.permanent
|
||||
? t('aiTool.deleteFilePermanent', { path: r.path || '' })
|
||||
: t('aiTool.deleteFileSoft', { path: r.path || '', backup_path: r.backup_path || '' })
|
||||
case 'restore_project': return r.restored ? t('aiTool.restored') : t('aiTool.restoreIneffective')
|
||||
case 'purge_project': return r.purged ? t('aiTool.purged') : t('aiTool.purgeIneffective')
|
||||
case 'run_command': {
|
||||
const code = typeof r.exit_code === 'number' ? r.exit_code : -1
|
||||
const ms = typeof r.duration_ms === 'number' ? r.duration_ms : 0
|
||||
return code === 0 ? t('aiTool.commandOk', { ms }) : t('aiTool.commandFailed', { code })
|
||||
}
|
||||
case 'patch_file': return r.changed === false
|
||||
? t('aiTool.patchedNoChange', { path: r.path || '' })
|
||||
: t('aiTool.patchedFile', { path: r.path || '', diff: formatSizeDiff(r.size_diff) })
|
||||
case 'append_file': return t('aiTool.appendedTo', { path: r.path || '', bytes: formatBytes(r.bytes_written) })
|
||||
case 'rename_file': return r.renamed
|
||||
? (r.cross_volume
|
||||
? t('aiTool.renamedCrossVolume', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) })
|
||||
: t('aiTool.renamedWithBytes', { from: r.from || '', to: r.to || '', bytes: formatBytes(r.bytes_moved) }))
|
||||
: ''
|
||||
case 'search_files': {
|
||||
const n = typeof r.total === 'number' ? r.total : (Array.isArray(r.results) ? r.results.length : 0)
|
||||
return t('aiTool.foundN', { n })
|
||||
}
|
||||
case 'file_info': return r.exists === false
|
||||
? t('aiTool.fileInfoMissing', { path: r.path || '' })
|
||||
: t('aiTool.fileInfoSize', { path: r.path || '', size: formatBytes(typeof r.size === 'number' ? r.size : 0) })
|
||||
case 'bind_directory': return r.bound
|
||||
? t('aiTool.boundDir', { path: r.path || '', stack: r.stack || '' })
|
||||
: ''
|
||||
case 'run_workflow': {
|
||||
// UX-260618-05: header 副标题显 execution_id 概要(原 workflowHint「请到工作流页面运行」误导——工作流已触发)
|
||||
const execId = typeof r.execution_id === 'string' && r.execution_id
|
||||
? r.execution_id
|
||||
: (typeof r.id === 'string' ? r.id : '')
|
||||
return execId ? t('aiTool.workflowTriggeredShort', { exec: execId }) : ''
|
||||
}
|
||||
case 'http_request': {
|
||||
const status = typeof r.status === 'number' ? r.status : 0
|
||||
const method = typeof r.method === 'string' ? r.method : 'HTTP'
|
||||
const ms = typeof r.elapsed_ms === 'number' ? r.elapsed_ms : 0
|
||||
const ok = status >= 200 && status < 300
|
||||
return ok
|
||||
? t('aiTool.httpOk', { method, status, ms })
|
||||
: t('aiTool.httpFailed', { method, status })
|
||||
}
|
||||
default: return ''
|
||||
}
|
||||
const fn = TOOL_SUMMARIES[tc.name]
|
||||
return fn ? fn(r) : ''
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user