fix: 死锁修复、管理员权限集中管控、群聊频率控制、表情映射修复
thinker.go: 移除所有 defer t.muUnlock() 持锁跨阻塞调用的模式,消除8处死锁点 - performThink: defer→立即解锁,LLM调用不再持锁 - lightThinkLoop: defer在for循环内→第2次迭代自死锁 - resetSilenceTimer: defer持锁调performThink - UpdatePresence: defer持锁调time.Sleep+performThink - storeThought: defer+panic→锁泄露; 移除extractProactiveMessage嵌套锁 is_admin三层防御: - synthesizer: 系统提示词注入管理员/非管理员身份标签 - iot_provider: 非管理员直接拒绝IoT操作 - plugin-manager: ToolDefinition.AdminOnly自动拦截,集中管控 群聊优化: - group_ambient: 强化审查指令,【不发送】自审查标签 - 群聊间隔4s→3s,最多2条/轮 - 工具失败也推跟进消息,避免沉默 平台桥接: - 日志文件名使用适配器唯一标识符(ConfigName) - QQ表情映射替换为官方116条目数据 - CQ表情保留名称/ID
This commit is contained in:
@@ -3584,8 +3584,7 @@ function handleChatLog(entry) {
|
||||
if (STATE.activePanel === 'chatPlatforms' && STATE.chatActivePlatform) {
|
||||
// Check if the entry's platform matches the active config's platform type.
|
||||
var activeCfg = (STATE.chatConfigs || []).find(function(c) { return c.name === STATE.chatActivePlatform; }) || null;
|
||||
var activePtype = (activeCfg && activeCfg.platform) || STATE.chatActivePlatform;
|
||||
if (entry.platform === activePtype) {
|
||||
if (entry.platform === STATE.chatActivePlatform || (activeCfg && entry.platform === activeCfg.platform)) {
|
||||
prependChatLogEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-18
@@ -1934,27 +1934,31 @@ server.listen(ETHEND_PORT, () => {
|
||||
});
|
||||
|
||||
// readBridgeLogs 从磁盘读取 platform-bridge 的 JSONL 日志文件
|
||||
// 动态扫描目录中的所有 .log 文件,合并后按时间排序取最近 limit 条
|
||||
function readBridgeLogs(limit) {
|
||||
const bridgeLogDir = path.join(ROOT, 'backend', 'platform-bridge', 'logs');
|
||||
const logFiles = ['obv11-main.log', 'obv11.log']; // 新旧平台名都试试
|
||||
const entries = [];
|
||||
for (const name of logFiles) {
|
||||
const logPath = path.join(bridgeLogDir, name);
|
||||
if (!fs.existsSync(logPath)) continue;
|
||||
try {
|
||||
const content = fs.readFileSync(logPath, 'utf-8');
|
||||
const lines = content.trim().split('\n');
|
||||
// 取最后 limit 行
|
||||
const recent = lines.slice(-limit);
|
||||
for (const line of recent) {
|
||||
try {
|
||||
entries.push(JSON.parse(line));
|
||||
} catch { /* skip bad lines */ }
|
||||
}
|
||||
break; // 优先用第一个找到的文件
|
||||
} catch { /* skip */ }
|
||||
}
|
||||
return entries.slice(-limit);
|
||||
if (!fs.existsSync(bridgeLogDir)) return entries;
|
||||
try {
|
||||
const allFiles = fs.readdirSync(bridgeLogDir);
|
||||
const logFiles = allFiles.filter(f => f.endsWith('.log')).sort();
|
||||
// 从每个日志文件取最近的部分行
|
||||
const perFile = Math.max(1, Math.ceil(limit / Math.max(1, logFiles.length)));
|
||||
for (const name of logFiles) {
|
||||
const logPath = path.join(bridgeLogDir, name);
|
||||
try {
|
||||
const content = fs.readFileSync(logPath, 'utf-8');
|
||||
const lines = content.trim().split('\n');
|
||||
const recent = lines.slice(-perFile);
|
||||
for (const line of recent) {
|
||||
try { entries.push(JSON.parse(line)); } catch { /* skip bad lines */ }
|
||||
}
|
||||
} catch { /* skip */ }
|
||||
}
|
||||
} catch { /* skip */ }
|
||||
// 按时间戳降序排列,取最近 limit 条
|
||||
entries.sort((a, b) => new Date(b.timestamp || 0) - new Date(a.timestamp || 0));
|
||||
return entries.slice(0, limit);
|
||||
}
|
||||
|
||||
// 时间格式化:使用系统本地时区
|
||||
|
||||
Reference in New Issue
Block a user