Files
BaiLongma/src/capabilities/tool-audit.js

86 lines
3.1 KiB
JavaScript

import { insertActionLog } from '../db.js'
import { emitEvent } from '../events.js'
import { classifyTool } from './tool-policy.js'
import { previewValue, safeJsonStringify } from './tool-utils.js'
function getExecutionSource(context = {}) {
return context.source || context.trigger || (context.autonomous ? 'autonomous' : 'llm')
}
function summarizeToolExecution(name, args = {}) {
switch (name) {
case 'read_file':
return `read_file(${args.path || args.filename || args.file_path || '?'})`
case 'list_dir':
return `list_dir(${args.path || args.dir || args.directory || '.'})`
case 'write_file':
return `write_file(${args.path || args.filename || args.file_path || '?'})`
case 'delete_file':
return `delete_file(${args.path || args.filename || args.file_path || '?'})`
case 'make_dir':
return `make_dir(${args.path || args.dir || args.directory || '?'})`
case 'exec_command':
return `exec_command(${String(args.command || args.cmd || '?').slice(0, 100)})`
case 'fetch_url':
case 'browser_read':
return `${name}(${String(args.url || args.link || args.href || '?').slice(0, 120)})`
case 'web_search':
return `web_search(${String(args.query || args.q || args.keyword || '?').slice(0, 120)})`
case 'send_message':
case 'express':
return `${name} -> ${args.target_id || '(unknown)'}`
case 'upsert_memory': {
const count = Array.isArray(args.memories) ? args.memories.length : 0
return `upsert_memory(${count})`
}
default:
return name
}
}
export function inferToolStatus(result) {
const text = String(result ?? '').trim()
if (!text) return 'ok'
try {
const parsed = JSON.parse(text)
return parsed?.ok === false ? 'error' : 'ok'
} catch {}
return /^(错误|请求失败|执行失败|命令超时|命令执行失败|閿欒|璇锋眰澶辫触|鎵ц澶辫触|鍛戒护瓒呮椂|鍛戒护鎵ц澶辫触)/.test(text) ? 'error' : 'ok'
}
export function writeToolAuditLog({ name, args, context, policy, status, result = '', error = '', startedAt }) {
const durationMs = Date.now() - startedAt
const detailParts = []
if (policy?.reason) detailParts.push(`policy=${policy.reason}`)
const argPreview = previewValue(args, 160)
if (argPreview && argPreview !== '{}') detailParts.push(`args=${argPreview}`)
const resultPreview = previewValue(result || error, 220)
if (resultPreview) detailParts.push(`result=${resultPreview}`)
try {
insertActionLog({
timestamp: new Date(startedAt).toISOString(),
tool: name,
summary: summarizeToolExecution(name, args),
detail: detailParts.join(' | '),
status,
risk: policy?.risk || classifyTool(name),
argsJson: safeJsonStringify(args),
resultPreview,
error,
durationMs,
source: getExecutionSource(context),
})
} catch (err) {
console.warn(`[audit] failed to persist tool audit log: ${err.message}`)
}
emitEvent('tool_audit', {
tool: name,
status,
risk: policy?.risk || classifyTool(name),
summary: summarizeToolExecution(name, args),
duration_ms: durationMs,
source: getExecutionSource(context),
})
}