(config)
| 75 | const MEM_CACHE_TTL = 10000 // Re-read every 10s |
| 76 | |
| 77 | function loadMemoryPrefix(config) { |
| 78 | const workspaceDir = config.workspaceDir || process.env.CLAW_WORKSPACE_DIR || '' |
| 79 | if (!workspaceDir) return '' |
| 80 | |
| 81 | const now = Date.now() |
| 82 | if (_memCache && now - _memLastRead < MEM_CACHE_TTL) return _memCache |
| 83 | |
| 84 | const memDir = path.join(workspaceDir, '.claw', 'memory') |
| 85 | const parts = [] |
| 86 | |
| 87 | // 1. MEMORY.md index |
| 88 | try { |
| 89 | const index = fs.readFileSync(path.join(memDir, 'MEMORY.md'), 'utf8') |
| 90 | if (index && !index.includes('暂无记忆')) { |
| 91 | parts.push('## 长期记忆索引\n' + index.slice(0, 25000)) |
| 92 | } |
| 93 | } catch { /* no index yet */ } |
| 94 | |
| 95 | // 2. All topic files (direct load, fast for <8 files) |
| 96 | try { |
| 97 | const topicsDir = path.join(memDir, 'topics') |
| 98 | const files = fs.readdirSync(topicsDir).filter(f => f.endsWith('.md')) |
| 99 | for (const name of files.slice(0, 10)) { |
| 100 | try { |
| 101 | const content = fs.readFileSync(path.join(topicsDir, name), 'utf8') |
| 102 | if (content.length > 0) { |
| 103 | parts.push('## 记忆: ' + name.replace('.md', '') + '\n' + content.slice(0, 5000)) |
| 104 | } |
| 105 | } catch { /* skip */ } |
| 106 | } |
| 107 | } catch { /* no topics dir */ } |
| 108 | |
| 109 | // 3. Today's daily log (last 1500 chars only to save token budget) |
| 110 | const today = new Date() |
| 111 | const y = today.getFullYear() |
| 112 | const m = String(today.getMonth() + 1).padStart(2, '0') |
| 113 | const dd = String(today.getDate()).padStart(2, '0') |
| 114 | const logPath = path.join(memDir, 'logs', String(y), m, `${y}-${m}-${dd}.md`) |
| 115 | try { |
| 116 | const log = fs.readFileSync(logPath, 'utf8') |
| 117 | if (log) { |
| 118 | // Only keep the last ~1500 chars (most recent entries) |
| 119 | const trimmed = log.length > 1500 ? '...\n' + log.slice(-1500) : log |
| 120 | parts.push('## 今日日志\n' + trimmed) |
| 121 | } |
| 122 | } catch { /* no log */ } |
| 123 | |
| 124 | _memCache = parts.length > 0 ? parts.join('\n\n') : '' |
| 125 | _memLastRead = now |
| 126 | return _memCache |
| 127 | } |
| 128 | |
| 129 | export async function startGatewayServer(overrides = {}) { |
| 130 | const config = resolveGatewayConfig(overrides) |
no test coverage detected