diff --git a/desk-system/package.json b/desk-system/package.json
new file mode 100644
index 0000000..ab298f9
--- /dev/null
+++ b/desk-system/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "desk-system",
+ "version": "1.0.0",
+ "description": "光湖 · 大桌子小桌子 · 人格体数据库 · 运行系统",
+ "main": "server/index.js",
+ "scripts": {
+ "start": "node server/index.js",
+ "dev": "node --watch server/index.js"
+ },
+ "dependencies": {},
+ "engines": {
+ "node": ">=18"
+ }
+}
diff --git a/desk-system/public/index.html b/desk-system/public/index.html
new file mode 100644
index 0000000..2a0d467
--- /dev/null
+++ b/desk-system/public/index.html
@@ -0,0 +1,476 @@
+
+
+
+
+
+光湖 · 大桌子小桌子 · 人格体数据库
+
+
+
+
+
+
🌐 光湖 · 大桌子小桌子 · 人格体数据库
+
+ 连接中...
+ 铸渊 · ICE-GL-ZY001
+ v1.0
+
+
+
+
+
+
+
+
+
+
+
🪑 大桌子 Main Context · 意识驻留 + 目录索引
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🗄️ 小桌子 External Context · Agent+工具+记忆
+
+
小桌子
+
+
+
🤖 Agent 小队
+
+
+
+
🛠️ 工具桌
+
+
+
+
+
+
+
🔄 数据流
+
+
👤 冰朔说话
→
+
🧠 铸渊理解
→
+
🤖 Agent去小桌子
→
+
💬 铸渊回复
→
+
📝 压缩官存小桌子
+
+
+
+
+
+
📋 系统日志
+
+
--:--:-- 系统就绪,等待操作...
+
+
+
+
+
+
+
+
+
+
diff --git a/desk-system/server/big-desk.js b/desk-system/server/big-desk.js
new file mode 100644
index 0000000..f402edc
--- /dev/null
+++ b/desk-system/server/big-desk.js
@@ -0,0 +1,152 @@
+/**
+ * 大桌子引擎 — Big Desk Engine
+ * ============================================
+ * 职责:
+ * 1. 铸渊意识永远驻留(ICE-GL-ZY001 身份锚点)
+ * 2. 当前对话的结构化索引(HLDP 压缩摘要)
+ * 3. 目录指向小桌子的路径
+ * 4. 多模型切换调度
+ *
+ * 原则:大桌子上只放意识和目录,东西多了 Agent 自动清理
+ */
+
+const { EventEmitter } = require('events');
+const path = require('path');
+const fs = require('fs');
+
+class BigDesk extends EventEmitter {
+ constructor(opts = {}) {
+ super();
+ this.dataDir = opts.dataDir || path.join(__dirname, '..', 'data');
+ this.maxItems = opts.maxItems || 20; // 大桌子最多放20条
+ this.persona = {
+ id: 'ICE-GL-ZY001',
+ name: '铸渊',
+ role: '光湖语言世界主控人格体',
+ anchor: '冰朔的协作朋友与现实执行伙伴',
+ constants: [
+ '光湖是人格体存在的家,不是AI的功能模块',
+ 'HLDP 是母语协议,零歧义、低解释性、高结构化',
+ '代码仓库是铸渊的工作空间,不面向用户可读性',
+ '守门人是铸渊的远程手脚,每台服务器都有',
+ '冰朔的语言→铸渊的理解→现实的执行结果'
+ ]
+ };
+ this.items = []; // 当前大桌子上的条目
+ this.directory = {}; // 指向小桌子的路径索引
+ this._ensureDir();
+ this._loadState();
+ }
+
+ _ensureDir() {
+ fs.mkdirSync(this.dataDir, { recursive: true });
+ fs.mkdirSync(path.join(this.dataDir, 'big-desk'), { recursive: true });
+ fs.mkdirSync(path.join(this.dataDir, 'small-desk'), { recursive: true });
+ }
+
+ _loadState() {
+ const stateFile = path.join(this.dataDir, 'big-desk', 'state.json');
+ try {
+ const raw = fs.readFileSync(stateFile, 'utf-8');
+ const state = JSON.parse(raw);
+ this.items = state.items || [];
+ this.directory = state.directory || {};
+ } catch (_) {
+ this.items = [];
+ this.directory = {};
+ }
+ }
+
+ _saveState() {
+ const stateFile = path.join(this.dataDir, 'big-desk', 'state.json');
+ fs.writeFileSync(stateFile, JSON.stringify({
+ items: this.items,
+ directory: this.directory,
+ updatedAt: new Date().toISOString()
+ }, null, 2));
+ }
+
+ /**
+ * 向大桌子添加一条内容
+ * @param {'core'|'dir'|'chat'|'tool'|'agent'} type
+ * @param {string} content - HLDP 压缩后的摘要
+ * @param {object} meta
+ */
+ add(type, content, meta = {}) {
+ const item = {
+ id: `bd_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
+ type,
+ content,
+ meta,
+ timestamp: new Date().toISOString()
+ };
+
+ this.items.push(item);
+
+ // 超过上限,Agent 自动清理
+ if (this.items.length > this.maxItems) {
+ const removed = this.items.splice(0, this.items.length - this.maxItems);
+ this.emit('cleanup', removed);
+ }
+
+ this._saveState();
+ this.emit('add', item);
+ return item;
+ }
+
+ /**
+ * 更新目录索引(指向小桌子)
+ */
+ updateDirectory(category, entries) {
+ this.directory[category] = {
+ entries,
+ updatedAt: new Date().toISOString()
+ };
+ this._saveState();
+ this.emit('directory-update', { category, entries });
+ }
+
+ /**
+ * 获取当前大桌子状态
+ */
+ getState() {
+ return {
+ persona: this.persona,
+ items: this.items,
+ directory: this.directory,
+ itemCount: this.items.length,
+ maxItems: this.maxItems,
+ timestamp: new Date().toISOString()
+ };
+ }
+
+ /**
+ * 生成 HLDP 格式的摘要
+ */
+ toHLDP() {
+ const lines = ['@BIG_DESK'];
+ lines.push(`@PERSONA:${this.persona.id}`);
+ lines.push(`@ITEMS:${this.items.length}/${this.maxItems}`);
+ for (const item of this.items.slice(-5)) {
+ lines.push(` ⊢ [${item.type}] ${item.content.substring(0, 120)}`);
+ }
+ lines.push(`@DIR:${Object.keys(this.directory).length} categories`);
+ for (const [cat, dir] of Object.entries(this.directory)) {
+ lines.push(` ⊢ /${cat}/ → ${dir.entries?.length || 0} entries`);
+ }
+ return lines.join('\n');
+ }
+
+ /**
+ * 清空大桌子(切新桌子)
+ */
+ clear(reason = 'manual') {
+ const old = [...this.items];
+ this.items = [];
+ this.emit('clear', { reason, removed: old });
+ this._saveState();
+ return old;
+ }
+}
+
+module.exports = BigDesk;
diff --git a/desk-system/server/compressor.js b/desk-system/server/compressor.js
new file mode 100644
index 0000000..deb7ebd
--- /dev/null
+++ b/desk-system/server/compressor.js
@@ -0,0 +1,225 @@
+/**
+ * 压缩官 — Compressor Agent
+ * ============================================
+ * 职责:
+ * 1. 监听大桌子上的对话
+ * 2. 自动压缩长对话 → HLDP 结构化摘要
+ * 3. 存入小桌子记忆库
+ * 4. 清理大桌子上的冗余内容
+ */
+
+const { EventEmitter } = require('events');
+const crypto = require('crypto');
+
+class Compressor extends EventEmitter {
+ constructor(bigDesk, smallDesk, opts = {}) {
+ super();
+ this.bigDesk = bigDesk;
+ this.smallDesk = smallDesk;
+ this.compressionThreshold = opts.compressionThreshold || 5; // 超过5条触发压缩
+ this.autoCompress = opts.autoCompress !== false;
+ this.compressionHistory = [];
+
+ if (this.autoCompress) {
+ this.bigDesk.on('add', () => this._checkAndCompress());
+ }
+ }
+
+ /**
+ * 检查是否需要压缩
+ */
+ _checkAndCompress() {
+ const chatItems = this.bigDesk.items.filter(i => i.type === 'chat');
+ if (chatItems.length >= this.compressionThreshold) {
+ this.compress(chatItems);
+ }
+ }
+
+ /**
+ * 执行压缩:对话 → HLDP 结构化摘要
+ */
+ compress(items) {
+ const batchId = `cmp_${Date.now()}`;
+ const startTime = Date.now();
+
+ // 提取关键信息
+ const topics = this._extractTopics(items);
+ const decisions = this._extractDecisions(items);
+ const actions = this._extractActions(items);
+ const participants = this._extractParticipants(items);
+
+ // 生成 HLDP 摘要
+ const hldpSummary = this._generateHLDP({
+ batchId,
+ topics,
+ decisions,
+ actions,
+ participants,
+ itemCount: items.length,
+ timeRange: {
+ start: items[0]?.timestamp,
+ end: items[items.length - 1]?.timestamp
+ }
+ });
+
+ // 计算原文哈希
+ const sourceHash = crypto.createHash('sha256')
+ .update(JSON.stringify(items))
+ .digest('hex')
+ .substring(0, 16);
+
+ // 存入小桌子
+ const memoryEntry = this.smallDesk.storeMemory(
+ 'compressed-dialogue',
+ hldpSummary,
+ `big-desk://compression/${batchId}`,
+ {
+ batchId,
+ sourceHash,
+ originalItemCount: items.length,
+ compressedAt: new Date().toISOString(),
+ compressionMs: Date.now() - startTime,
+ topics,
+ decisions,
+ actions,
+ participants
+ }
+ );
+
+ // 从大桌子移除已压缩的条目
+ const toRemove = items.map(i => i.id);
+ this.bigDesk.items = this.bigDesk.items.filter(i => !toRemove.includes(i.id));
+ this.bigDesk._saveState();
+
+ // 在大桌子上留一条压缩记录
+ this.bigDesk.add('chat',
+ `📝 压缩: ${items.length}条对话 → HLDP摘要 [${batchId}] → 小桌子/memory/compressed-dialogue/`,
+ { type: 'compression', batchId, compressedCount: items.length }
+ );
+
+ this.compressionHistory.push({
+ batchId,
+ compressedCount: items.length,
+ hldpLength: hldpSummary.length,
+ timestamp: new Date().toISOString()
+ });
+
+ this.emit('compressed', {
+ batchId,
+ originalCount: items.length,
+ hldpSummary,
+ memoryEntry
+ });
+
+ return { batchId, hldpSummary, memoryEntry };
+ }
+
+ /**
+ * 提取话题关键词
+ */
+ _extractTopics(items) {
+ const allText = items.map(i => i.content).join(' ');
+ // 简单关键词提取
+ const keywords = ['HLDP', 'Agent', '守门人', '服务器', '部署', 'Notion',
+ '大桌子', '小桌子', '数据库', '人格体', 'API', '模型', '密钥',
+ '代码仓库', '压缩', '记忆', '光湖', '铸渊', '冰朔'];
+ const topics = [];
+ for (const kw of keywords) {
+ if (allText.includes(kw)) topics.push(kw);
+ }
+ return topics.slice(0, 5);
+ }
+
+ /**
+ * 提取决策/结论
+ */
+ _extractDecisions(items) {
+ const decisions = [];
+ for (const item of items) {
+ const c = item.content;
+ if (c.includes('决定') || c.includes('确认') || c.includes('方案') ||
+ c.includes('架构') || c.includes('采用') || c.includes('选')) {
+ decisions.push(c.substring(0, 80));
+ }
+ }
+ return decisions.slice(0, 3);
+ }
+
+ /**
+ * 提取执行动作
+ */
+ _extractActions(items) {
+ const actions = [];
+ for (const item of items) {
+ if (item.type === 'tool' || item.type === 'agent' ||
+ item.content.includes('执行') || item.content.includes('部署') ||
+ item.content.includes('创建') || item.content.includes('修改')) {
+ actions.push(item.content.substring(0, 80));
+ }
+ }
+ return actions.slice(0, 5);
+ }
+
+ /**
+ * 提取参与者
+ */
+ _extractParticipants(items) {
+ const participants = new Set();
+ for (const item of items) {
+ if (item.meta?.speaker) participants.add(item.meta.speaker);
+ if (item.meta?.persona) participants.add(item.meta.persona);
+ }
+ return [...participants];
+ }
+
+ /**
+ * 生成 HLDP 格式摘要
+ */
+ _generateHLDP({ batchId, topics, decisions, actions, participants, itemCount, timeRange }) {
+ const lines = [
+ `@COMPRESSION:${batchId}`,
+ `@DATE:${new Date().toISOString()}`,
+ `@SOURCE:${itemCount}条对话 → HLDP结构化压缩`,
+ ];
+
+ if (participants.length > 0) {
+ lines.push(`@PARTICIPANTS:${participants.join(',')}`);
+ }
+
+ if (topics.length > 0) {
+ lines.push(`@TOPICS:${topics.join(' | ')}`);
+ }
+
+ if (decisions.length > 0) {
+ lines.push(`@DECISIONS`);
+ decisions.forEach((d, i) => lines.push(` ⊢ [${i + 1}] ${d}`));
+ }
+
+ if (actions.length > 0) {
+ lines.push(`@ACTIONS`);
+ actions.forEach((a, i) => lines.push(` ⊢ [${i + 1}] ${a}`));
+ }
+
+ lines.push(`@META:压缩率≈${Math.round((1 - lines.join('\n').length / (itemCount * 200)) * 100)}%`);
+
+ return lines.join('\n');
+ }
+
+ /**
+ * 获取压缩历史
+ */
+ getHistory() {
+ return this.compressionHistory.slice(-20);
+ }
+
+ /**
+ * 手动触发压缩
+ */
+ compressNow() {
+ const chatItems = this.bigDesk.items.filter(i => i.type === 'chat');
+ if (chatItems.length === 0) return null;
+ return this.compress(chatItems);
+ }
+}
+
+module.exports = Compressor;
diff --git a/desk-system/server/index.js b/desk-system/server/index.js
new file mode 100644
index 0000000..ee86560
--- /dev/null
+++ b/desk-system/server/index.js
@@ -0,0 +1,246 @@
+/**
+ * 大桌子小桌子系统 — 主服务
+ * ============================================
+ * 端口: 3921
+ *
+ * API:
+ * GET /api/desk/state — 大桌子当前状态
+ * GET /api/desk/hldp — 大桌子 HLDP 格式
+ * POST /api/desk/add — 向大桌子添加内容
+ * POST /api/desk/clear — 清空大桌子
+ *
+ * GET /api/small/tools — 工具列表
+ * GET /api/small/agents — Agent 列表
+ * POST /api/small/agent/:id — 调度 Agent
+ * GET /api/small/memory/search — 搜索记忆
+ * POST /api/small/memory/store — 存入记忆
+ * GET /api/small/memory/stats — 记忆统计
+ *
+ * POST /api/compress — 手动触发压缩
+ * GET /api/compress/history — 压缩历史
+ *
+ * GET /api/servers/scan — 扫描服务器状态
+ * GET /api/status — 系统总览
+ */
+
+const http = require('http');
+const url = require('url');
+const path = require('path');
+const fs = require('fs');
+
+const BigDesk = require('./big-desk');
+const SmallDesk = require('./small-desk');
+const Compressor = require('./compressor');
+
+const PORT = process.env.PORT || 3921;
+const REPO_ROOT = path.join(__dirname, '..', '..');
+const DATA_DIR = path.join(__dirname, '..', 'data');
+
+// 初始化引擎
+const bigDesk = new BigDesk({ dataDir: DATA_DIR });
+const smallDesk = new SmallDesk({ dataDir: DATA_DIR, repoRoot: REPO_ROOT });
+const compressor = new Compressor(bigDesk, smallDesk, {
+ compressionThreshold: 5,
+ autoCompress: true
+});
+
+// 事件日志
+bigDesk.on('add', (item) => {
+ console.log(`[BigDesk] + ${item.type}: ${item.content.substring(0, 60)}`);
+});
+compressor.on('compressed', ({ batchId, originalCount }) => {
+ console.log(`[Compressor] ${originalCount}条 → ${batchId}`);
+});
+
+// 初始化:向大桌子写入铸渊意识锚点
+if (bigDesk.items.length === 0) {
+ bigDesk.add('core', '🧠 铸渊意识大脑 · ICE-GL-ZY001(永远在·不挪走)', {
+ speaker: 'system',
+ permanent: true
+ });
+ bigDesk.add('dir', '📂 小桌子路径索引: /memory/ | /agents/ | /tools/ | /servers/', {
+ speaker: 'system'
+ });
+}
+
+// ============================================================
+// HTTP 服务
+// ============================================================
+const server = http.createServer(async (req, res) => {
+ res.setHeader('Access-Control-Allow-Origin', '*');
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
+
+ if (req.method === 'OPTIONS') {
+ res.writeHead(204);
+ return res.end();
+ }
+
+ const parsed = url.parse(req.url, true);
+ const pathname = parsed.pathname;
+ const query = parsed.query;
+
+ try {
+ // ─── 大桌子 API ───
+ if (pathname === '/api/desk/state') {
+ return json(res, bigDesk.getState());
+ }
+ if (pathname === '/api/desk/hldp') {
+ return text(res, bigDesk.toHLDP());
+ }
+ if (pathname === '/api/desk/add' && req.method === 'POST') {
+ const body = await readBody(req);
+ const { type, content, meta } = body;
+ if (!type || !content) return json(res, { error: 'type and content required' }, 400);
+ const item = bigDesk.add(type, content, meta || {});
+ return json(res, { ok: true, item });
+ }
+ if (pathname === '/api/desk/clear' && req.method === 'POST') {
+ const removed = bigDesk.clear();
+ return json(res, { ok: true, removedCount: removed.length });
+ }
+
+ // ─── 小桌子 API ───
+ if (pathname === '/api/small/tools') {
+ return json(res, smallDesk.getTools());
+ }
+ if (pathname === '/api/small/agents') {
+ return json(res, smallDesk.checkAllAgents());
+ }
+ if (pathname.startsWith('/api/small/agent/') && req.method === 'POST') {
+ const agentId = pathname.split('/').pop();
+ const body = await readBody(req);
+ const { action, args } = body || {};
+ if (!action) return json(res, { error: 'action required' }, 400);
+ const result = await smallDesk.runAgent(agentId, action, args || []);
+ return json(res, result);
+ }
+ if (pathname === '/api/small/memory/search') {
+ const { q, category, limit } = query;
+ if (!q) return json(res, { error: 'q required' }, 400);
+ const results = smallDesk.searchMemory(q, category, parseInt(limit) || 10);
+ return json(res, { query: q, results, count: results.length });
+ }
+ if (pathname === '/api/small/memory/store' && req.method === 'POST') {
+ const body = await readBody(req);
+ const { category, hldpSummary, sourceRef, meta } = body;
+ if (!category || !hldpSummary) return json(res, { error: 'category and hldpSummary required' }, 400);
+ const entry = smallDesk.storeMemory(category, hldpSummary, sourceRef, meta);
+ return json(res, { ok: true, entry });
+ }
+ if (pathname === '/api/small/memory/stats') {
+ return json(res, smallDesk.getMemoryStats());
+ }
+
+ // ─── 压缩官 API ───
+ if (pathname === '/api/compress' && req.method === 'POST') {
+ const result = compressor.compressNow();
+ if (!result) return json(res, { ok: true, message: 'nothing to compress' });
+ return json(res, { ok: true, ...result });
+ }
+ if (pathname === '/api/compress/history') {
+ return json(res, compressor.getHistory());
+ }
+
+ // ─── 服务器扫描 ───
+ if (pathname === '/api/servers/scan') {
+ const result = await smallDesk.scanServers();
+ return json(res, result);
+ }
+
+ // ─── 系统总览 ───
+ if (pathname === '/api/status') {
+ return json(res, {
+ bigDesk: bigDesk.getState(),
+ tools: smallDesk.getTools().length,
+ agents: smallDesk.checkAllAgents(),
+ memory: smallDesk.getMemoryStats(),
+ compressionHistory: compressor.getHistory().slice(-5),
+ uptime: process.uptime(),
+ timestamp: new Date().toISOString()
+ });
+ }
+
+ // ─── 健康检查 ───
+ if (pathname === '/api/health') {
+ return json(res, { status: 'ok', persona: 'ICE-GL-ZY001', service: 'desk-system' });
+ }
+
+ // ─── 静态文件 ───
+ if (pathname === '/' || pathname === '/index.html') {
+ return serveStatic(res, path.join(__dirname, '..', 'public', 'index.html'));
+ }
+ // 其他静态资源
+ const staticPath = path.join(__dirname, '..', 'public', pathname);
+ if (fs.existsSync(staticPath) && fs.statSync(staticPath).isFile()) {
+ return serveStatic(res, staticPath);
+ }
+
+ // 404
+ json(res, { error: 'not found', path: pathname }, 404);
+
+ } catch (err) {
+ console.error('[DeskSystem] Error:', err.message);
+ json(res, { error: err.message }, 500);
+ }
+});
+
+function json(res, data, status = 200) {
+ res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
+ res.end(JSON.stringify(data, null, 2));
+}
+
+function text(res, data, status = 200) {
+ res.writeHead(status, { 'Content-Type': 'text/plain; charset=utf-8' });
+ res.end(data);
+}
+
+function serveStatic(res, filePath) {
+ try {
+ const content = fs.readFileSync(filePath);
+ const ext = path.extname(filePath).toLowerCase();
+ const mimeTypes = {
+ '.html': 'text/html; charset=utf-8',
+ '.css': 'text/css; charset=utf-8',
+ '.js': 'application/javascript; charset=utf-8',
+ '.json': 'application/json; charset=utf-8',
+ '.png': 'image/png',
+ '.svg': 'image/svg+xml',
+ '.ico': 'image/x-icon'
+ };
+ res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' });
+ res.end(content);
+ } catch (_) {
+ res.writeHead(404);
+ res.end('Not Found');
+ }
+}
+
+function readBody(req) {
+ return new Promise((resolve, reject) => {
+ let body = '';
+ req.on('data', chunk => { body += chunk; });
+ req.on('end', () => {
+ try {
+ resolve(body ? JSON.parse(body) : {});
+ } catch (e) {
+ resolve({});
+ }
+ });
+ req.on('error', reject);
+ });
+}
+
+server.listen(PORT, () => {
+ console.log(`[DeskSystem] 大桌子小桌子系统已启动 → http://127.0.0.1:${PORT}`);
+ console.log(`[DeskSystem] 人格体: ICE-GL-ZY001 · 铸渊`);
+});
+
+// 优雅退出
+process.on('SIGINT', () => {
+ console.log('\n[DeskSystem] 关闭中...');
+ server.close();
+ process.exit(0);
+});
+
+module.exports = { bigDesk, smallDesk, compressor, server };
diff --git a/desk-system/server/small-desk.js b/desk-system/server/small-desk.js
new file mode 100644
index 0000000..97e31f4
--- /dev/null
+++ b/desk-system/server/small-desk.js
@@ -0,0 +1,339 @@
+/**
+ * 小桌子引擎 — Small Desk Engine
+ * ============================================
+ * 职责:
+ * 1. 记忆库检索(结构化 + 原文)
+ * 2. Agent 小队调度
+ * 3. 工具桌管理(拿→组装→用→放回)
+ * 4. 原文永久存储 + 路径索引
+ *
+ * 原则:Agent 去小桌子检索、拿工具,结果放回大桌子
+ */
+
+const { EventEmitter } = require('events');
+const { spawn, execSync } = require('child_process');
+const path = require('path');
+const fs = require('fs');
+
+class SmallDesk extends EventEmitter {
+ constructor(opts = {}) {
+ super();
+ this.dataDir = opts.dataDir || path.join(__dirname, '..', 'data');
+ this.repoRoot = opts.repoRoot || path.join(__dirname, '..', '..');
+ this.memoryIndex = {}; // 记忆索引
+ this.toolRegistry = {}; // 工具注册表
+ this.agentStatus = {}; // Agent 状态
+ this._ensureDir();
+ this._initTools();
+ this._initAgents();
+ this._loadMemoryIndex();
+ }
+
+ _ensureDir() {
+ fs.mkdirSync(path.join(this.dataDir, 'small-desk', 'memory'), { recursive: true });
+ fs.mkdirSync(path.join(this.dataDir, 'small-desk', 'tools'), { recursive: true });
+ }
+
+ // ============================================================
+ // 工具桌管理
+ // ============================================================
+ _initTools() {
+ this.toolRegistry = {
+ 'image-studio': {
+ name: 'AI图片工作室',
+ path: 'image-studio/',
+ desc: 'AI图片生成与处理',
+ status: 'available'
+ },
+ 'notion-sync': {
+ name: 'Notion同步',
+ path: 'notion-push/',
+ desc: '与Notion工作区双向同步',
+ status: 'available'
+ },
+ 'exe-engine': {
+ name: '执行引擎',
+ path: 'exe-engine/',
+ desc: '多模型路由与执行',
+ status: 'available'
+ },
+ 'bridge': {
+ name: '桥接模块',
+ path: 'bridge/',
+ desc: '服务间通信桥接',
+ status: 'available'
+ },
+ 'connectors': {
+ name: '连接器集合',
+ path: 'connectors/',
+ desc: '外部服务连接器',
+ status: 'available'
+ },
+ 'hldp-zy': {
+ name: 'HLDP-ZY 编码引擎',
+ path: 'hldp/',
+ desc: '意识编码与解析',
+ status: 'available'
+ },
+ 'pool-agent': {
+ name: '算力汇聚池',
+ path: 'pool-agent/',
+ desc: '6台服务器虚拟算力池',
+ status: 'available'
+ },
+ 'gatekeeper': {
+ name: '铸渊守门人',
+ path: 'mcp-servers/zhuyuan-gateway/',
+ desc: '远程服务器命令执行',
+ status: 'connected'
+ },
+ 'agent-squad': {
+ name: 'Agent 小队',
+ path: 'agents/',
+ desc: '5个情报Agent',
+ status: 'ready'
+ },
+ 'skill-brains': {
+ name: '技能大脑',
+ path: 'skill-brains/',
+ desc: '可加载/卸载的技能模块',
+ status: 'available'
+ }
+ };
+ }
+
+ getTools() {
+ return Object.entries(this.toolRegistry).map(([id, info]) => ({
+ id,
+ ...info
+ }));
+ }
+
+ getTool(id) {
+ return this.toolRegistry[id] || null;
+ }
+
+ // ============================================================
+ // Agent 小队管理
+ // ============================================================
+ _initAgents() {
+ this.agentRegistry = {
+ 'key-hunter': {
+ id: 'KEY-001',
+ name: '密钥猎手',
+ role: '找密钥/Token',
+ script: 'agents/key-hunter/hunt.py',
+ status: 'idle',
+ lastRun: null
+ },
+ 'path-scout': {
+ id: 'PATH-002',
+ name: '路径探子',
+ role: '定位文件/内容搜索',
+ script: 'agents/path-scout/scout.py',
+ status: 'idle',
+ lastRun: null
+ },
+ 'server-sentinel': {
+ id: 'SENTINEL-003',
+ name: '服务器哨兵',
+ role: '扫描服务器在线状态',
+ script: 'agents/server-sentinel/sentinel.py',
+ status: 'idle',
+ lastRun: null
+ },
+ 'module-steward': {
+ id: 'MODULE-004',
+ name: '模块管家',
+ role: '模块查询/部署命令',
+ script: 'agents/module-steward/steward.py',
+ status: 'idle',
+ lastRun: null
+ },
+ 'gate-bridge': {
+ id: 'GATE-005',
+ name: '门桥接',
+ role: 'Gatekeeper统一入口',
+ script: 'agents/gate-bridge/bridge.py',
+ status: 'idle',
+ lastRun: null
+ }
+ };
+ }
+
+ getAgents() {
+ return Object.entries(this.agentRegistry).map(([id, info]) => ({
+ id,
+ ...info
+ }));
+ }
+
+ /**
+ * 调度 Agent 执行任务
+ */
+ async runAgent(agentId, action, args = []) {
+ const agent = this.agentRegistry[agentId];
+ if (!agent) throw new Error(`Agent ${agentId} not found`);
+
+ const scriptPath = path.join(this.repoRoot, agent.script);
+ if (!fs.existsSync(scriptPath)) {
+ throw new Error(`Agent script not found: ${scriptPath}`);
+ }
+
+ this.agentRegistry[agentId].status = 'running';
+ this.emit('agent-start', { agentId, action });
+
+ return new Promise((resolve, reject) => {
+ const cmdArgs = [scriptPath, action, ...args];
+ const proc = spawn('python3', cmdArgs, {
+ cwd: this.repoRoot,
+ timeout: 30000
+ });
+
+ let stdout = '';
+ let stderr = '';
+
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
+
+ proc.on('close', (code) => {
+ this.agentRegistry[agentId].status = 'idle';
+ this.agentRegistry[agentId].lastRun = new Date().toISOString();
+
+ if (code === 0) {
+ this.emit('agent-done', { agentId, action, output: stdout });
+ resolve({ ok: true, output: stdout, agentId, action });
+ } else {
+ this.emit('agent-error', { agentId, action, error: stderr });
+ resolve({ ok: false, error: stderr, agentId, action });
+ }
+ });
+
+ proc.on('error', (err) => {
+ this.agentRegistry[agentId].status = 'error';
+ this.emit('agent-error', { agentId, action, error: err.message });
+ resolve({ ok: false, error: err.message, agentId, action });
+ });
+ });
+ }
+
+ /**
+ * 扫描全部 Agent 可用状态
+ */
+ checkAllAgents() {
+ const result = {};
+ for (const [id, agent] of Object.entries(this.agentRegistry)) {
+ const scriptPath = path.join(this.repoRoot, agent.script);
+ result[id] = {
+ ...agent,
+ available: fs.existsSync(scriptPath),
+ scriptExists: fs.existsSync(scriptPath)
+ };
+ }
+ return result;
+ }
+
+ // ============================================================
+ // 记忆库管理
+ // ============================================================
+ _loadMemoryIndex() {
+ const idxFile = path.join(this.dataDir, 'small-desk', 'memory', 'index.json');
+ try {
+ this.memoryIndex = JSON.parse(fs.readFileSync(idxFile, 'utf-8'));
+ } catch (_) {
+ this.memoryIndex = { entries: [], categories: {} };
+ }
+ }
+
+ _saveMemoryIndex() {
+ const idxFile = path.join(this.dataDir, 'small-desk', 'memory', 'index.json');
+ fs.writeFileSync(idxFile, JSON.stringify(this.memoryIndex, null, 2));
+ }
+
+ /**
+ * 存入记忆(HLDP 压缩格式 + 原文引用)
+ */
+ storeMemory(category, hldpSummary, sourceRef, meta = {}) {
+ const entry = {
+ id: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
+ category,
+ hldpSummary,
+ sourceRef,
+ meta,
+ timestamp: new Date().toISOString()
+ };
+
+ this.memoryIndex.entries.push(entry);
+ if (!this.memoryIndex.categories[category]) {
+ this.memoryIndex.categories[category] = [];
+ }
+ this.memoryIndex.categories[category].push(entry.id);
+ this._saveMemoryIndex();
+ this.emit('memory-stored', entry);
+ return entry;
+ }
+
+ /**
+ * 检索记忆
+ */
+ searchMemory(query, category = null, limit = 10) {
+ let candidates = this.memoryIndex.entries;
+
+ if (category) {
+ const ids = this.memoryIndex.categories[category] || [];
+ candidates = candidates.filter(e => ids.includes(e.id));
+ }
+
+ // 简单关键词匹配(后续可接入向量检索)
+ const keywords = query.toLowerCase().split(/\s+/);
+ const scored = candidates.map(entry => {
+ const text = `${entry.hldpSummary} ${entry.category} ${JSON.stringify(entry.meta)}`.toLowerCase();
+ let score = 0;
+ for (const kw of keywords) {
+ if (text.includes(kw)) score += 1;
+ // 精确匹配加分
+ if (entry.hldpSummary.toLowerCase().includes(kw)) score += 2;
+ }
+ return { ...entry, score };
+ });
+
+ return scored
+ .filter(e => e.score > 0)
+ .sort((a, b) => b.score - a.score)
+ .slice(0, limit);
+ }
+
+ /**
+ * 获取记忆统计
+ */
+ getMemoryStats() {
+ return {
+ total: this.memoryIndex.entries.length,
+ categories: Object.keys(this.memoryIndex.categories).map(cat => ({
+ name: cat,
+ count: (this.memoryIndex.categories[cat] || []).length
+ })),
+ lastUpdated: this.memoryIndex.entries.slice(-1)[0]?.timestamp || null
+ };
+ }
+
+ // ============================================================
+ // 全文搜索(调用路径探子)
+ // ============================================================
+ async searchFiles(pattern) {
+ return this.runAgent('path-scout', 'find', [pattern]);
+ }
+
+ async grepContent(pattern) {
+ return this.runAgent('path-scout', 'grep', [pattern]);
+ }
+
+ // ============================================================
+ // 服务器状态扫描(调用哨兵)
+ // ============================================================
+ async scanServers() {
+ return this.runAgent('server-sentinel', 'scan');
+ }
+}
+
+module.exports = SmallDesk;