小白龙 Bailongma - 初始提交

自主操作员与思考搭档系统。
包含 orchestrator-v2 多Agent编排层、后台意识引擎、记忆系统、ACUI 组件。
This commit is contained in:
chengjiaxi
2026-05-22 19:22:06 +08:00
commit 664ffb3834
64 changed files with 111533 additions and 0 deletions

View File

@@ -0,0 +1,205 @@
const initSqlJs = require('sql.js');
const path = require('path');
const fs = require('fs');
class SessionStore {
constructor(dbDir) {
this.dbDir = dbDir;
this.dbPath = path.join(dbDir, 'sessions.sqlite');
this.db = null;
}
async init() {
if (!fs.existsSync(this.dbDir)) fs.mkdirSync(this.dbDir, { recursive: true });
const SQL = await initSqlJs();
if (fs.existsSync(this.dbPath)) {
const buf = fs.readFileSync(this.dbPath);
this.db = new SQL.Database(buf);
} else {
this.db = new SQL.Database();
}
this.db.run('PRAGMA journal_mode=WAL');
this.db.run('PRAGMA foreign_keys=ON');
this.db.run("CREATE TABLE IF NOT EXISTS sessions (" +
"id TEXT PRIMARY KEY, parent_id TEXT, status TEXT DEFAULT 'created', " +
"task TEXT, context TEXT, result TEXT, summary TEXT, " +
"token_count INTEGER DEFAULT 0, is_compressed INTEGER DEFAULT 0, " +
"events TEXT DEFAULT '[]', " +
"created_at TEXT DEFAULT (datetime('now')), " +
"updated_at TEXT DEFAULT (datetime('now')), " +
"FOREIGN KEY (parent_id) REFERENCES sessions(id))");
this.db.run("CREATE TABLE IF NOT EXISTS messages (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, " +
"role TEXT NOT NULL, content TEXT, tool_calls TEXT, tool_call_id TEXT, " +
"token_count INTEGER DEFAULT 0, " +
"created_at TEXT DEFAULT (datetime('now')), " +
"FOREIGN KEY (session_id) REFERENCES sessions(id))");
this._save();
}
_save() {
const data = this.db.export();
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
fs.writeFileSync(this.dbPath, buf);
}
createSession(id, task, context, parentId) {
if (parentId === undefined) parentId = null;
this.db.run('INSERT OR REPLACE INTO sessions (id, parent_id, task, context, status) VALUES (?, ?, ?, ?, ?)',
[id, parentId, task, JSON.stringify(context || {}), 'created']);
this._save();
}
getSession(id) {
const stmt = this.db.exec('SELECT rowid, * FROM sessions WHERE id = ?', [id]);
if (!stmt.length || !stmt[0].values.length) return null;
const cols = stmt[0].columns;
const vals = stmt[0].values[0];
return cols.reduce(function(o, c, i) { o[c] = vals[i]; return o; }, {});
}
updateSession(id, updates) {
var allowed = ['status','task','context','result','summary','token_count','is_compressed','parent_id'];
var sets = [];
var params = [];
for (var key in updates) {
if (allowed.indexOf(key) >= 0) {
sets.push(key + ' = ?');
params.push(typeof updates[key] === 'object' ? JSON.stringify(updates[key]) : updates[key]);
}
}
if (!sets.length) return;
sets.push("updated_at = datetime('now')");
params.push(id);
this.db.run('UPDATE sessions SET ' + sets.join(', ') + ' WHERE id = ?', params);
this._save();
}
updateStatus(id, status, result) {
if (result) {
this.db.run("UPDATE sessions SET status = ?, result = ?, updated_at = datetime('now') WHERE id = ?",
[status, JSON.stringify(result), id]);
} else {
this.db.run("UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE id = ?",
[status, id]);
}
this._save();
}
deleteSession(id) {
this.db.run('DELETE FROM messages WHERE session_id = ?', [id]);
this.db.run('DELETE FROM sessions WHERE id = ?', [id]);
this._save();
}
listSessions(limit, offset) {
if (limit === undefined) limit = 20;
if (offset === undefined) offset = 0;
var stmt = this.db.exec('SELECT id, parent_id, status, task, summary, token_count, is_compressed, created_at, updated_at FROM sessions ORDER BY created_at DESC LIMIT ' + limit + ' OFFSET ' + offset);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
getSessionChain(id) {
var chain = [];
var current = this.getSession(id);
while (current) {
chain.unshift(current);
if (current.parent_id) current = this.getSession(current.parent_id);
else break;
}
return chain;
}
appendEvent(id, event) {
var row = this.db.exec('SELECT events FROM sessions WHERE id = ?', [id]);
if (row.length > 0) {
var events = JSON.parse(row[0].values[0][0] || '[]');
events.push(event);
this.db.run("UPDATE sessions SET events = ?, updated_at = datetime('now') WHERE id = ?", [JSON.stringify(events), id]);
this._save();
}
}
addMessage(sessionId, role, content, extras) {
if (!extras) extras = {};
this.db.run('INSERT INTO messages (session_id, role, content, tool_calls, tool_call_id, token_count) VALUES (?, ?, ?, ?, ?, ?)',
[sessionId, role, content, extras.toolCalls ? JSON.stringify(extras.toolCalls) : null, extras.toolCallId || null, extras.tokenCount || 0]);
this._save();
if (extras.tokenCount) {
this.db.run("UPDATE sessions SET token_count = token_count + ?, updated_at = datetime('now') WHERE id = ?", [extras.tokenCount, sessionId]);
this._save();
}
}
getMessages(sessionId, limit) {
if (limit === undefined) limit = 100;
var stmt = this.db.exec('SELECT id, role, content, tool_calls, token_count, created_at FROM messages WHERE session_id = ? ORDER BY id ASC LIMIT ?', [sessionId, limit]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
countMessages(sessionId) {
var stmt = this.db.exec('SELECT COUNT(*) as cnt FROM messages WHERE session_id = ?', [sessionId]);
return stmt.length ? stmt[0].values[0][0] : 0;
}
getTotalTokenCount(sessionId) {
var stmt = this.db.exec('SELECT SUM(token_count) as total FROM messages WHERE session_id = ?', [sessionId]);
return (stmt.length && stmt[0].values[0][0]) ? stmt[0].values[0][0] : 0;
}
searchSessions(query, limit) {
if (limit === undefined) limit = 10;
var likeQ = '%' + query + '%';
var stmt = this.db.exec('SELECT id, task, summary, status, created_at FROM sessions WHERE task LIKE ? OR summary LIKE ? ORDER BY created_at DESC LIMIT ?', [likeQ, likeQ, limit]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
searchMessages(query, limit) {
if (limit === undefined) limit = 20;
var likeQ = '%' + query + '%';
var stmt = this.db.exec('SELECT session_id, role, content FROM messages WHERE content LIKE ? ORDER BY id DESC LIMIT ?', [likeQ, limit]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
compressSession(id, summary) {
this.db.run("UPDATE sessions SET is_compressed = 1, summary = ?, status = 'compressed', updated_at = datetime('now') WHERE id = ?", [summary, id]);
this._save();
}
getCompressibleSessions(threshold) {
if (threshold === undefined) threshold = 100;
var stmt = this.db.exec("SELECT s.id, s.task, COUNT(m.id) as msg_count, s.created_at FROM sessions s LEFT JOIN messages m ON m.session_id = s.id WHERE s.is_compressed = 0 AND s.status = 'completed' GROUP BY s.id HAVING msg_count > ? ORDER BY msg_count DESC", [threshold]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
getStats() {
var stmt = this.db.exec("SELECT COUNT(*) as total_sessions, SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as failed, SUM(token_count) as total_tokens, SUM(CASE WHEN is_compressed=1 THEN 1 ELSE 0 END) as compressed FROM sessions");
if (!stmt.length) return {};
var vals = stmt[0].values[0];
var cols = stmt[0].columns;
return cols.reduce(function(o, c, i) { o[c] = vals[i]; return o; }, {});
}
searchEverything(query, limit) {
if (limit === undefined) limit = 10;
var results = { sessions: [], messages: [] };
try { results.sessions = this.searchSessions(query, limit); results.messages = this.searchMessages(query, limit * 2); } catch (e) {}
return results;
}
close() {
if (this.db) { this._save(); this.db.close(); }
}
}
module.exports = SessionStore;