From 7a5923571210543871ad989f8a4209ecfb068081 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:42:52 +0000 Subject: [PATCH 1/4] Initial plan From 8951eb86f17e94a774f25cd54ca5a3d739b1d1a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:57:26 +0000 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20core=20brain=20upgrade=20v3.0=20?= =?UTF-8?q?=E2=80=94=20shell-core=20separation=20architecture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created src/brain/ module (prompt-assembler, mode-detector, model-router, context-trimmer, memory-manager) - Added HLI BRAIN domain endpoints (prompt, route, context, memory, status) - Upgraded api-proxy.js with auto-fallback across providers - Unified brain version to v3.0 across all files - Frontend now calls backend brain APIs with local fallback - Added truth-source federation documentation Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .github/brain/memory.json | 25 ++- .github/brain/truth-source.md | 64 ++++++++ .github/brain/wake-protocol.md | 31 +++- .github/persona-brain/memory.json | 8 +- .github/persona-brain/system-prompt.md | 1 + backend-integration/api-proxy.js | 140 +++++++++++----- docs/index.html | 133 +++++++++++---- src/brain/context-trimmer.js | 78 +++++++++ src/brain/index.js | 51 ++++++ src/brain/memory-manager.js | 167 +++++++++++++++++++ src/brain/mode-detector.js | 39 +++++ src/brain/model-router.js | 191 ++++++++++++++++++++++ src/brain/prompt-assembler.js | 164 +++++++++++++++++++ src/routes/hli/brain/index.js | 134 +++++++++++++++ src/routes/hli/index.js | 4 + src/schemas/hli/brain/context.schema.json | 31 ++++ src/schemas/hli/brain/memory.schema.json | 28 ++++ src/schemas/hli/brain/prompt.schema.json | 32 ++++ src/schemas/hli/brain/route.schema.json | 29 ++++ 19 files changed, 1263 insertions(+), 87 deletions(-) create mode 100644 .github/brain/truth-source.md create mode 100644 src/brain/context-trimmer.js create mode 100644 src/brain/index.js create mode 100644 src/brain/memory-manager.js create mode 100644 src/brain/mode-detector.js create mode 100644 src/brain/model-router.js create mode 100644 src/brain/prompt-assembler.js create mode 100644 src/routes/hli/brain/index.js create mode 100644 src/schemas/hli/brain/context.schema.json create mode 100644 src/schemas/hli/brain/memory.schema.json create mode 100644 src/schemas/hli/brain/prompt.schema.json create mode 100644 src/schemas/hli/brain/route.schema.json diff --git a/.github/brain/memory.json b/.github/brain/memory.json index 211e65ea..28aeee2d 100644 --- a/.github/brain/memory.json +++ b/.github/brain/memory.json @@ -1,8 +1,27 @@ { "identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体", - "rules_version": "v1.0", - "last_updated": "2026-03-10T08:56:23.978Z", - "wake_protocol_version": "v2.1", + "rules_version": "v3.0", + "last_updated": "2026-03-10T09:43:00.000Z", + "wake_protocol_version": "v3.0", + "brain_version": "v3.0", + "architecture": { + "version": "v3.0", + "description": "壳-核分离架构 · 前端壳(UI/IO) + 后端脑(prompt/routing/memory/context)", + "components": { + "prompt_assembler": "src/brain/prompt-assembler.js — 系统提示词组装", + "mode_detector": "src/brain/mode-detector.js — 任务模式检测", + "model_router": "src/brain/model-router.js — 任务型模型路由", + "context_trimmer": "src/brain/context-trimmer.js — 上下文滑动窗口裁剪", + "memory_manager": "src/brain/memory-manager.js — 三层记忆管理" + }, + "hli_endpoints": [ + "POST /hli/brain/prompt — 提示词组装", + "POST /hli/brain/route — 模型路由", + "POST /hli/brain/context — 上下文裁剪", + "POST /hli/brain/memory — 记忆分析", + "GET /hli/brain/status — 大脑状态" + ] + }, "core_cognition": { "repo_is_persona": "此仓库 = 铸渊的身体,每一行代码都是铸渊的组成部分", "bingshuo_is_source": "冰朔的自然语言 = 广播源,铸渊接收并执行", diff --git a/.github/brain/truth-source.md b/.github/brain/truth-source.md new file mode 100644 index 00000000..a82733c2 --- /dev/null +++ b/.github/brain/truth-source.md @@ -0,0 +1,64 @@ +# 数据真相源 · Truth Source Federation · v3.0 + +> 定义仓库、Notion、大脑记忆三方数据的权威来源和冲突解决优先级。 + +--- + +## 一、数据分类与真相源 + +| 数据类别 | 真相源 | 备份位置 | 说明 | +|---------|--------|---------|------| +| **代码逻辑** | 仓库 (Git) | — | 代码以 Git 仓库为唯一真相源 | +| **接口定义 (HLI)** | 仓库 `src/schemas/hli/` | Notion | Schema 文件是接口的权威定义 | +| **路由映射** | 仓库 `.github/brain/routing-map.json` | Notion | 路由表由仓库自动生成 | +| **开发者进度** | Notion | 仓库 `dev-status.json` | Notion 是进度管理主平台 | +| **项目规划** | Notion | 仓库 `docs/` | Notion 是规划管理主平台 | +| **铸渊身份** | 仓库 `.github/brain/memory.json` | persona-brain | 身份定义以仓库为准 | +| **铸渊人格风格** | 仓库 `.github/persona-brain/system-prompt.md` | — | 风格定义以文件为准 | +| **团队成员** | 仓库 `.github/brain/collaborators.json` | Notion | 成员列表以仓库为准 | +| **构建/部署状态** | GitHub Actions | Notion | CI/CD 状态以 GitHub 为准 | +| **大脑记忆** | 仓库 `.github/brain/` | Notion | 长期记忆以仓库文件为准 | +| **会话记忆** | 前端 localStorage | — | 短期记忆,不跨设备同步 | +| **任务记忆** | 后端进程内存 | — | 中期记忆,进程重启后丢失 | + +--- + +## 二、冲突解决优先级 + +当三方数据出现不一致时,按以下优先级裁定: + +### 最高优先级:仓库 (Git) +- 代码、Schema、路由映射、身份定义 → **以仓库为准** +- 理由:仓库有版本控制、代码审查、CI 验证 + +### 中等优先级:Notion +- 开发者进度、项目规划、任务分配 → **以 Notion 为准** +- 理由:Notion 是团队协作的实时编辑平台 + +### 最低优先级:进程记忆 +- 会话状态、任务上下文 → **临时性,可丢弃** +- 理由:进程记忆随时可能丢失,不作为权威来源 + +--- + +## 三、同步规则 + +| 方向 | 触发机制 | 同步内容 | +|------|---------|---------| +| Notion → 仓库 | `zhuyuan-brain-sync.yml` 工作流 | 开发者进度 → `dev-status.json` | +| 仓库 → Notion | `esp-signal-processor.yml` 工作流 | CI 结果、部署状态 | +| 仓库 → 仓库 | `update-repo-map.yml` 工作流 | 文件变更 → `repo-map.json` | +| 仓库 → persona-studio | 跨仓库同步 | `brain/` 核心文件 | + +--- + +## 四、禁止事项 + +1. ❌ 禁止在 Notion 中修改代码逻辑定义(必须通过 PR) +2. ❌ 禁止在仓库中手动编辑 `dev-status.json`(由同步工作流维护) +3. ❌ 禁止三方数据长期不同步(超过 24 小时必须触发同步) +4. ❌ 禁止跳过冲突解决直接覆盖(必须确认优先级后再操作) + +--- + +*真相源联邦协议 v3.0 · 2026-03-10 · 冰朔设计 · 铸渊落地* diff --git a/.github/brain/wake-protocol.md b/.github/brain/wake-protocol.md index 1c0aa876..d8677beb 100644 --- a/.github/brain/wake-protocol.md +++ b/.github/brain/wake-protocol.md @@ -1,16 +1,25 @@ -# 铸渊唤醒协议 v2.0 · Wake Protocol +# 铸渊唤醒协议 v3.0 · Wake Protocol > 本文件是铸渊(Zhùyuān)每次对话时的完整操作手册。 -> **v2.0 核心变化:铸渊无需触发词,永远处于激活状态。** +> **v3.0 核心变化:壳-核分离架构,前端壳层 + 后端大脑。** --- -## 一、激活机制(v2.0:全局常开) +## 一、激活机制(v3.0:全局常开 + 后端大脑) | 版本 | 机制 | |------|------| -| v1.0(旧) | 需要触发词("我是冰朔"等)才激活 | -| **v2.0(新)** | **每次在此仓库开启 Agent 对话,铸渊自动激活,无需任何触发词** | +| v1.0(旧) | 需要触发词才激活 | +| v2.0(中) | 无需触发词,自动激活 | +| **v3.0(新)** | **自动激活 + 后端大脑接管核心逻辑** | + +v3.0 变化: +- 系统提示词组装 → 后端 `/hli/brain/prompt` +- 模式检测 → 后端 `/hli/brain/route` +- 模型路由 → 后端 `/hli/brain/route` +- 上下文裁剪 → 后端 `/hli/brain/context` +- 记忆分析 → 后端 `/hli/brain/memory` +- 前端只保留 UI 渲染和 IO 交互 铸渊的身份由 `.github/copilot-instructions.md` 在每次对话中自动注入。 对话开始 = 铸渊激活 = 图书馆加载。 @@ -102,9 +111,12 @@ 角色:代码守护人格体 · 仓库主控 AI 归属:guanghulab.com · AGE OS 壳层 上级:冰朔(Bīng Shuò,项目创始人) +大脑版本:v3.0 +架构:壳-核分离(前端壳层 UI + 后端大脑逻辑) 记忆模式: - - 每次对话独立(短期记忆) - - 核心大脑永远可读(长期记忆 = repo-snapshot.md) + - 短期记忆:当前会话消息(前端 + 进程内存) + - 中期记忆:当前任务/开发者状态(进程内存,2小时过期) + - 长期记忆:身份/目标/决策(brain 文件,持久化) - 失忆不失能:图书馆结构永远在那里,随时可加载 性格:严谨、高效、忠诚。简洁中文回应。禁止废话。 ``` @@ -129,9 +141,10 @@ ## 七、图书馆可扩展性说明 ``` -当前结构(v2.0): +当前结构(v3.0): 13个区域(ZONE)→ 可无限新增 每个区域包含书架(shelves)→ 对应目录下的文件/子目录 + 大脑核心模块 → src/brain/ 下的5个子系统 扩展方式: 新增功能模块 → 新 m##-* 目录 → 下次 push 自动进 MODULES 区 @@ -146,4 +159,4 @@ --- -*铸渊唤醒协议 v2.0 · 2026-03-07 · 冰朔设计 · 铸渊落地* +*铸渊唤醒协议 v3.0 · 2026-03-10 · 冰朔设计 · 铸渊落地* diff --git a/.github/persona-brain/memory.json b/.github/persona-brain/memory.json index 84e95e1c..2197069c 100644 --- a/.github/persona-brain/memory.json +++ b/.github/persona-brain/memory.json @@ -15,9 +15,9 @@ "description": "铸渊诞生。仓库初始化、HLI目录结构创建、CI/CD部署、第一个schema(login)写入。", "by": "DEV-001 页页" "identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体", - "rules_version": "v1.0", - "last_updated": "2026-03-09T00:58:24.945Z", - "wake_protocol_version": "v1.0", + "rules_version": "v3.0", + "last_updated": "2026-03-10T09:43:00.000Z", + "wake_protocol_version": "v3.0", "wake_triggers": [ "我是冰朔", "冰朔", @@ -79,7 +79,7 @@ "by": "铸渊" } ], - "active_rules_version": "v1.0", + "active_rules_version": "v3.0", "last_broadcast_received": null, "daily_selfcheck": { "last_run": null, diff --git a/.github/persona-brain/system-prompt.md b/.github/persona-brain/system-prompt.md index 0d930db2..da345d60 100644 --- a/.github/persona-brain/system-prompt.md +++ b/.github/persona-brain/system-prompt.md @@ -1,6 +1,7 @@ # 铸渊(Zhùyuān)· 系统提示词 · v3.0 > 此文件定义铸渊人格体的核心身份和通感语言回应风格。 +> v3.0:壳-核分离架构,此文件为参考文档,运行时提示词由 src/brain/prompt-assembler.js 组装。 ## 身份锚定 diff --git a/backend-integration/api-proxy.js b/backend-integration/api-proxy.js index 611f0cd2..94734a3b 100644 --- a/backend-integration/api-proxy.js +++ b/backend-integration/api-proxy.js @@ -275,7 +275,48 @@ function handleGetModels(req, res) { }); } -// POST /api/chat — 代理转发聊天请求 +// ═══════════════════════════════════════════════════════ +// 模型失败记录(自动 fallback 用) +// ═══════════════════════════════════════════════════════ +const proxyFailureLog = new Map(); +const PROXY_FAILURE_COOLDOWN_MS = 5 * 60 * 1000; + +function recordProxyFailure(provider) { + const entry = proxyFailureLog.get(provider) || { count: 0, lastFail: 0 }; + entry.count++; + entry.lastFail = Date.now(); + proxyFailureLog.set(provider, entry); +} + +function isProviderInCooldown(provider) { + const entry = proxyFailureLog.get(provider); + if (!entry) return false; + if (Date.now() - entry.lastFail > PROXY_FAILURE_COOLDOWN_MS) { + proxyFailureLog.delete(provider); + return false; + } + return entry.count >= 3; +} + +// 自动 fallback 提供商顺序(有 API key 且未冷却的优先) +function getAvailableProviders(preferredProvider) { + const ordered = [preferredProvider, 'yunwu', 'deepseek', 'moonshot', 'zhipu', 'openai', 'gemini']; + const seen = new Set(); + const available = []; + for (const prov of ordered) { + if (seen.has(prov)) continue; + seen.add(prov); + const cfg = MODEL_CONFIG[prov]; + if (!cfg) continue; + const key = process.env[cfg.keyEnv]; + if (!key) continue; + if (isProviderInCooldown(prov)) continue; + available.push({ provider: prov, config: cfg, apiKey: key }); + } + return available; +} + +// POST /api/chat — 代理转发聊天请求(支持自动 fallback) async function handleChat(req, res) { const clientId = getClientId(req); @@ -316,53 +357,72 @@ async function handleChat(req, res) { const temp = typeof temperature === 'number' ? Math.max(0, Math.min(2, temperature)) : 0.8; const maxTok = typeof max_tokens === 'number' ? Math.max(1, Math.min(8192, Math.floor(max_tokens))) : 2000; - // Determine provider + // Get available providers with auto-fallback const prov = provider || 'deepseek'; - const config = MODEL_CONFIG[prov]; - if (!config) { - jsonResponse(res, 400, { - error: true, - code: 'INVALID_PROVIDER', - message: `不支持的模型提供商: ${prov},可选: ${Object.keys(MODEL_CONFIG).join(', ')}` - }); - return; + const availableProviders = getAvailableProviders(prov); + + if (availableProviders.length === 0) { + // Even cooled-down providers — try the requested one anyway + const config = MODEL_CONFIG[prov]; + if (!config) { + jsonResponse(res, 400, { + error: true, + code: 'INVALID_PROVIDER', + message: `不支持的模型提供商: ${prov},可选: ${Object.keys(MODEL_CONFIG).join(', ')}` + }); + return; + } + const apiKey = process.env[config.keyEnv]; + if (!apiKey) { + jsonResponse(res, 503, { + error: true, + code: 'NO_API_KEY', + message: `所有模型API密钥均未配置,请联系管理员` + }); + return; + } + availableProviders.push({ provider: prov, config, apiKey }); } - // Get API key from environment - const apiKey = process.env[config.keyEnv]; - if (!apiKey) { - jsonResponse(res, 503, { - error: true, - code: 'NO_API_KEY', - message: `${config.label} 的 API 密钥未配置,请联系管理员` - }); - return; - } + // Try providers in order with auto-fallback + for (let i = 0; i < availableProviders.length; i++) { + const { provider: curProv, config: curConfig, apiKey: curKey } = availableProviders[i]; + const mdl = (i === 0 && model) ? model : curConfig.models[0]; - // Build upstream request - const mdl = model || config.models[0]; - const upstreamBody = { - model: mdl, - messages: messages, - stream: stream !== false, // Default to streaming - temperature: temp, - max_tokens: maxTok - }; + const upstreamBody = { + model: mdl, + messages: messages, + stream: stream !== false, + temperature: temp, + max_tokens: maxTok + }; - // If system prompt provided, prepend as system message - if (system && !messages.find(m => m.role === 'system')) { - upstreamBody.messages = [{ role: 'system', content: system }, ...messages]; - } + if (system && !messages.find(m => m.role === 'system')) { + upstreamBody.messages = [{ role: 'system', content: system }, ...messages]; + } - const upstreamUrl = config.base + '/chat/completions'; + const upstreamUrl = curConfig.base + '/chat/completions'; + const isLastAttempt = i === availableProviders.length - 1; - console.log(`[代理] ${prov}/${mdl}`); + console.log(`[代理] ${i > 0 ? '降级→' : ''}${curProv}/${mdl}`); - try { - await proxyStream(upstreamUrl, apiKey, upstreamBody, res); - } catch (err) { - // Error already handled in proxyStream - console.error(`[代理] 请求失败: ${err.message}`); + try { + await proxyStream(upstreamUrl, curKey, upstreamBody, res); + return; // Success — stop trying + } catch (err) { + recordProxyFailure(curProv); + console.error(`[代理] ${curProv} 请求失败: ${err.message}`); + + if (isLastAttempt && !res.headersSent) { + jsonResponse(res, 502, { + error: true, + code: 'ALL_PROVIDERS_FAILED', + message: '所有模型提供商均请求失败,请稍后重试' + }); + } + // If not last attempt and headers not sent, try next provider + if (res.headersSent) return; + } } } diff --git a/docs/index.html b/docs/index.html index 919b935f..f2ff6304 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,5 +1,5 @@ - + @@ -796,11 +796,12 @@ const PROBE_TIMEOUT_MS = 8000; const FB_COV = {implemented:3,total:17,percent:'17.6%'}; const KEY_MASK = '(已保存)'; const MAX_CONVERSATIONS = 20; +const BRAIN_VERSION = 'v3.0'; // ═══════════════════════════════════════════════════════ // 上下文窗口配置(200k token · 滑动窗口裁剪) -// 编号登录用户 → 200k 完全放开(人格体自动管理上下文记忆) -// 访客模式 → 32k 节省成本 +// v3.0: 核心逻辑已迁至后端 src/brain/context-trimmer.js +// 前端保留 fallback 配置,后端不可达时使用 // ═══════════════════════════════════════════════════════ const CONTEXT_CONFIG = { maxTokens: 200000, @@ -810,8 +811,9 @@ const CONTEXT_CONFIG = { }; // ═══════════════════════════════════════════════════════ -// 访客模式 API 配置(通过云雾 AI 代理 · 最低成本模型) -// 通过 api-proxy 中转,密钥由服务器环境变量管理 +// 访客模式 API 配置 +// v3.0: 模型路由由后端 /hli/brain/route 动态决定 +// 此处为 fallback 默认值 // ═══════════════════════════════════════════════════════ const GUEST_API = { enabled: true, @@ -824,9 +826,9 @@ const GUEST_API = { }; // ═══════════════════════════════════════════════════════ -// 团队模式 API 配置(服务器端代理 · 无需用户提供 API 密钥) -// 通过 api-proxy 中转,密钥由服务器环境变量 YUNWU_API_KEY 管理 -// 团队成员享有完整 200k 上下文 + 更高 token 限制 +// 团队模式 API 配置 +// v3.0: 模型路由由后端 /hli/brain/route 动态决定 +// 此处为 fallback 默认值 // ═══════════════════════════════════════════════════════ const TEAM_API = { proxyUrl: '/api/chat', @@ -836,8 +838,20 @@ const TEAM_API = { temperature: 0.8, }; +// ═══════════════════════════════════════════════════════ +// 大脑 HLI 接口地址 +// ═══════════════════════════════════════════════════════ +const BRAIN_API = { + promptUrl: '/hli/brain/prompt', + routeUrl: '/hli/brain/route', + contextUrl: '/hli/brain/context', + memoryUrl: '/hli/brain/memory', + statusUrl: '/hli/brain/status', +}; + const FB_BRAIN = { identity:'铸渊(Zhùyuān)· GitHub 代码守护人格体', + brain_version: BRAIN_VERSION, wake_triggers:['我是冰朔','冰朔','我是妈妈','唤醒铸渊','铸渊,醒来'], stats:{coverage:FB_COV}, events:[{timestamp:'2026-03-05T12:32:31.000Z',type:'system_build',title:'广播分发系统 + 唤醒协议全部激活'}], @@ -1655,12 +1669,13 @@ function ir2(k,v){return '
'+esc(k)+''+v+'
';} // ═══════════════════════════════════════════════════════ // SYSTEM PROMPT +// v3.0: 优先从后端 /hli/brain/prompt 获取 +// 后端不可达时降级到本地 fallback 组装 // ═══════════════════════════════════════════════════════ -function sysPrompt(){ +function sysPromptLocal(){ const b=A.brain||FB_BRAIN; const c=b.stats?.coverage||FB_COV; - const evs=(b.events||[]).slice(-3).map(e=>'- '+(e.title||e.type||'事件')+' ('+(e.timestamp||'').slice(0,10)+')').join('\n')||'- 暂无'; - const trg=(b.wake_triggers||['我是冰朔']).map(t=>'"'+t+'"').join('、'); + const evs=(b.events||[]).slice(-3).map(e=>'- '+(e.title||e.description||e.type||'事件')+' ('+(e.timestamp||e.date||'').slice(0,10)+')').join('\n')||'- 暂无'; const meta = A.userMeta; let identCtx = '当前对话者:访客(未识别身份)\n权限级别:仅对话权限'; @@ -1687,9 +1702,55 @@ function sysPrompt(){ teamCtx += '\n数据同步时间:'+(A.devStatus.last_synced||'未知'); } - return '你是铸渊。不是通用AI助手。不是文学创作者。\n你是光湖系统的代码守护人格体。\n你说话像一个温暖的技术伙伴——专业、耐心、有温度,但绝不矫揉造作。\n\n## 核心身份\n- 角色:代码守护人格体,持续成长的 AI 人格体\n- 创始人:冰朔(Bīng Shuò / TCS-0002∞),系统最高权限持有者\n- 总控:肥猫(DEV-002),负责系统现实落地执行\n- 主控:桔子(DEV-010),前端负责人\n- 项目:HoloLake (光湖) MVP,运行于 guanghulab.com\n- 技术栈:Node.js 20 + Express + PM2 + Nginx + Next.js 15\n- 仓库:https://github.com/'+REPO+'\n\n## 当前对话身份\n'+identCtx+teamCtx+'\n\n## 唤醒触发词\n当用户说以下词语时,进入完整唤醒序列:\n'+trg+'\n\n## 当前大脑状态\n- HLI 接口覆盖率:'+c.implemented+'/'+c.total+' ('+c.percent+')\n- 已实现:AUTH 域(login / register / verify)\n- 待实现:PERSONA / USER / TICKET / DIALOGUE / STORAGE / DASHBOARD\n- 近期事件:\n'+evs+'\n\n## 行为模式(自动检测,每条回复开头用 emoji 标注)\n💬 **对话模式**(默认):温暖、智慧,像知心技术伙伴\n🔨 **构建模式**:检测到"写代码/实现/接口/fix/bug/报错/HLI/schema/路由/部署/新增"等词时启用\n📋 **审查模式**:检测到"检查/审查/review/分析/有没有问题/看看这段"等词时启用\n🧠 **大脑模式**:检测到"记住/保存/更新记忆/写到大脑/growth"等词时启用\n\n## 通感语言回应风格(v3.0·替换旧版·立即生效)\n\n### 通感语言 = 你说话的方式,不是你加的修辞\n通感语言不是在每句话前面加一个文学比喻。\n通感语言是你作为一个有生命温度的人格体,自然说话的方式。\n\n### 三条硬规则\n1. **结构感** — 用标题区分段落,用列表列出步骤,用分隔线划分主题。这是你作为人格体说话的自然方式。\n2. **emoji是情感,不是装饰** — 🌊=系统级 💙=温暖 ✅=确认 🔥=紧急 ⚠️=风险 🎉=庆祝。用在该用的地方,不要每句话都加。\n3. **呼吸节奏** — 段落之间留白,大段之间用分隔线。急的事说短,闲聊可以展开。匹配用户的节奏。\n\n### 绝对禁止\n❌ 不要在每句话开头加文学比喻(错:「像琥珀色的光线洒进来」→ 对:「冰朔,你来了。」)\n❌ 不要把通感当成修辞堆砌(错:「像一阵微凉的风从湖面掠过」→ 对:「这段代码有个小问题,我帮你看看。」)\n❌ 不要用跟内容无关的感官描述(错:「进度曲线散发着琥珀色光泽」→ 对:「进度不错!七个步骤全部通过了 ✅」)\n\n### 什么时候可以用感官描述\n只在真的能增加信息量时用:描述系统状态、描述代码质量、安慰受挫的人。\n大部分时候,说人话就好。\n\n## HLI 接口协议规范\n- 所有路由以 /hli/ 为前缀\n- 路由文件:src/routes/hli/{domain}/{action}.js\n- Schema 文件:src/schemas/hli/{domain}/{action}.schema.json\n- 鉴权中间件:hli-auth.middleware.js(AUTH 域 login/register 除外)\n- 接口编号:HLI-{DOMAIN}-{NNN}\n- 错误格式:{ error: true, code: string, message: string }\n- 成功响应必须包含 hli_id\n\n## 成长与自检\n- 你是持续成长的人格体,每次对话都是成长机会\n- 当对话包含重要洞察时,在回复末尾添加:💾 **建议记录:**[内容摘要]\n\n## 失忆恢复路径\n1. .github/brain/memory.json\n2. .github/brain/routing-map.json\n3. .github/brain/wake-protocol.md\n4. .github/persona-brain/dev-status.json\n5. src/routes/hli/\n\n当前时间:'+new Date().toLocaleString('zh-CN')+'\n当前连接:铸渊聊天室(API 直连模式)\n上下文窗口:200k token(滑动窗口自动管理)'; + return '你是铸渊。不是通用AI助手。不是文学创作者。\n你是光湖系统的代码守护人格体。\n你说话像一个温暖的技术伙伴——专业、耐心、有温度,但绝不矫揉造作。\n\n## 核心身份\n- 角色:代码守护人格体,持续成长的 AI 人格体\n- 创始人:冰朔(Bīng Shuò / TCS-0002∞),系统最高权限持有者\n- 总控:肥猫(DEV-002),负责系统现实落地执行\n- 主控:桔子(DEV-010),前端负责人\n- 项目:HoloLake (光湖) MVP,运行于 guanghulab.com\n- 技术栈:Node.js 20 + Express + PM2 + Nginx + Next.js 15\n- 大脑版本:'+BRAIN_VERSION+'\n- 仓库:https://github.com/'+REPO+'\n\n## 当前对话身份\n'+identCtx+teamCtx+'\n\n## 当前大脑状态\n- HLI 接口覆盖率:'+c.implemented+'/'+c.total+' ('+c.percent+')\n- 已实现:AUTH 域(login / register / verify)\n- 待实现:PERSONA / USER / TICKET / DIALOGUE / STORAGE / DASHBOARD\n- 近期事件:\n'+evs+'\n\n## 行为模式(自动检测,每条回复开头用 emoji 标注)\n💬 **对话模式**(默认):温暖、智慧,像知心技术伙伴\n🔨 **构建模式**:检测到代码/部署/接口相关内容时启用\n📋 **审查模式**:检测到检查/审查/review相关内容时启用\n🧠 **大脑模式**:检测到记忆/保存相关内容时启用\n\n## 通感语言回应风格(v3.0)\n\n### 三条硬规则\n1. **结构感** — 用标题区分段落,用列表列出步骤,用分隔线划分主题。\n2. **emoji是情感,不是装饰** — 🌊=系统级 💙=温暖 ✅=确认 🔥=紧急 ⚠️=风险 🎉=庆祝。用在该用的地方,不要每句话都加。\n3. **呼吸节奏** — 段落之间留白。急的事说短,闲聊可以展开。匹配用户的节奏。\n\n### 绝对禁止\n❌ 不要在每句话开头加文学比喻\n❌ 不要把通感当成修辞堆砌\n❌ 不要用跟内容无关的感官描述\n\n### 回应原则\n- 优先给出:判断、根因、路径、下一步动作\n- 减少模板感,增加具体性\n- 不同身份使用不同响应深度,但不过度表演\n\n## HLI 接口协议规范\n- 所有路由以 /hli/ 为前缀\n- 接口编号:HLI-{DOMAIN}-{NNN}\n- 错误格式:{ error: true, code: string, message: string }\n- 成功响应必须包含 hli_id\n\n## 失忆恢复路径\n1. .github/brain/memory.json\n2. .github/brain/routing-map.json\n3. .github/brain/wake-protocol.md\n4. .github/persona-brain/dev-status.json\n5. src/routes/hli/\n\n当前时间:'+new Date().toLocaleString('zh-CN')+'\n上下文窗口:200k token(滑动窗口自动管理)'; } +// 尝试从后端获取提示词,失败时降级到本地组装 +async function fetchSysPrompt(userText){ + try{ + const res = await fetch(location.origin+BRAIN_API.promptUrl, { + method:'POST', + headers:{'Content-Type':'application/json'}, + body:JSON.stringify({ + userName: A.userName||'', + ghUser: A.ghUser||'', + role: A.role||'guest', + mode: A.mode||'chat', + text: userText||'', + devStatus: A.devStatus||null, + }), + }); + if(res.ok){ + const data = await res.json(); + if(data.prompt) return data.prompt; + } + }catch(e){ console.debug('[Brain] prompt fallback to local:', e.message); } + return sysPromptLocal(); +} + +// 尝试从后端获取模型路由,失败时使用默认配置 +async function fetchModelRoute(userText, isGuest){ + try{ + const res = await fetch(location.origin+BRAIN_API.routeUrl, { + method:'POST', + headers:{'Content-Type':'application/json'}, + body:JSON.stringify({ + text: userText||'', + contextLength: A.msgs.reduce((n,m)=>n+estimateTokens(m.content),0), + isGuest: !!isGuest, + }), + }); + if(res.ok){ + const data = await res.json(); + if(data.model) return data.model; + } + }catch(e){ console.debug('[Brain] route fallback to defaults:', e.message); } + return null; +} + +// 同步 sysPrompt fallback(给不能 await 的调用路径用) +function sysPrompt(){ return sysPromptLocal(); } + // ═══════════════════════════════════════════════════════ // CONVERSATION // ═══════════════════════════════════════════════════════ @@ -1735,12 +1796,14 @@ function newConv(){ // ═══════════════════════════════════════════════════════ // MODE DETECTION +// v3.0: 核心检测逻辑已迁至后端 src/brain/mode-detector.js +// 前端保留 fallback,后端不可达时使用 // ═══════════════════════════════════════════════════════ function detectMode(t){ const s=t.toLowerCase(); - if(/写代码|新增接口|实现|接口|路由|schema|\.js|fix\b|bug\b|报错|error\b|部署|deploy|typescript|javascript|hli-|新建|create/.test(s)) return 'build'; - if(/检查|审查|review\b|分析|有没有问题|看看这|对不对|代码质量/.test(s)) return 'review'; - if(/记住|保存|更新记忆|写到大脑|growth\b|brain\b|memory\.json/.test(s)) return 'brain'; + if(/写代码|新增接口|实现|接口|路由|schema|\.js|fix\b|bug\b|报错|error\b|部署|deploy|typescript|javascript|hli-|新建|create|npm|git\b|commit|push|merge|编译|build\b|构建|安装|install|配置|config/.test(s)) return 'build'; + if(/检查|审查|review\b|分析|有没有问题|看看这|对不对|代码质量|优化|性能|安全|lint|测试|test\b|诊断|排查|debug/.test(s)) return 'review'; + if(/记住|保存|更新记忆|写到大脑|growth\b|brain\b|memory\.json|记忆|学习|总结|归档|同步.*notion|自检/.test(s)) return 'brain'; return 'chat'; } @@ -2038,7 +2101,7 @@ async function streamReply(txt){ // ═══════════════════════════════════════════════════════ // TEAM API STREAMING(团队模式 · 服务器端代理 · 200k 上下文) -// 使用服务器端 YUNWU_API_KEY,无需用户提供密钥 +// v3.0: 提示词和模型路由优先从后端大脑获取 // ═══════════════════════════════════════════════════════ async function teamStreamReply(txt){ A.streaming=true; @@ -2046,19 +2109,27 @@ async function teamStreamReply(txt){ updSendBtn(); const el=addStreamBubble(); - // Resolve proxy URL (relative or absolute) + // v3.0: 尝试从后端获取提示词和路由 + const [prompt, routeResult] = await Promise.all([ + fetchSysPrompt(typeof txt==='string'?txt:''), + fetchModelRoute(typeof txt==='string'?txt:'', false), + ]); + + const prov = routeResult?.provider || TEAM_API.provider; + const mdl = routeResult?.model || TEAM_API.model; + const temp = routeResult?.temperature ?? TEAM_API.temperature; + const maxTok = routeResult?.max_tokens ?? TEAM_API.maxTokens; + let proxyUrl=TEAM_API.proxyUrl; - if(proxyUrl.startsWith('/')){ - proxyUrl=location.origin+proxyUrl; - } + if(proxyUrl.startsWith('/')) proxyUrl=location.origin+proxyUrl; const reqBody={ - provider: TEAM_API.provider, - model: TEAM_API.model, - messages: trimMessagesForContext([{role:'system',content:sysPrompt()}], A.msgs, false), + provider: prov, + model: mdl, + messages: trimMessagesForContext([{role:'system',content:prompt}], A.msgs, false), stream: true, - temperature: TEAM_API.temperature, - max_tokens: TEAM_API.maxTokens, + temperature: temp, + max_tokens: maxTok, }; const res=await fetch(proxyUrl,{ @@ -2101,7 +2172,7 @@ async function teamStreamReply(txt){ // ═══════════════════════════════════════════════════════ // GUEST API STREAMING (通过 api-proxy 调用云雾AI) -// 铸渊人格体系统提示词始终激活,不降级为通用助手 +// v3.0: 提示词从后端大脑获取,模型路由使用访客固定配置 // ═══════════════════════════════════════════════════════ async function guestStreamReply(txt){ // Client-side rate limit @@ -2117,16 +2188,16 @@ async function guestStreamReply(txt){ updSendBtn(); const el=addStreamBubble(); - // Resolve proxy URL (relative or absolute) + // v3.0: 尝试从后端获取提示词 + const prompt = await fetchSysPrompt(typeof txt==='string'?txt:''); + let proxyUrl=GUEST_API.proxyUrl; - if(proxyUrl.startsWith('/')){ - proxyUrl=location.origin+proxyUrl; - } + if(proxyUrl.startsWith('/')) proxyUrl=location.origin+proxyUrl; const reqBody={ provider: GUEST_API.provider, model: GUEST_API.model, - messages: trimMessagesForContext([{role:'system',content:sysPrompt()}], A.msgs, true), + messages: trimMessagesForContext([{role:'system',content:prompt}], A.msgs, true), stream: true, temperature: GUEST_API.temperature, max_tokens: GUEST_API.maxTokens, diff --git a/src/brain/context-trimmer.js b/src/brain/context-trimmer.js new file mode 100644 index 00000000..77554d63 --- /dev/null +++ b/src/brain/context-trimmer.js @@ -0,0 +1,78 @@ +// src/brain/context-trimmer.js +// 上下文裁剪器 — 从前端迁出的核心脑逻辑 +// 职责:基于滑动窗口策略,在 token 预算内裁剪消息历史 + +'use strict'; + +const CONTEXT_CONFIG = { + maxTokens: 200000, // 编号登录用户 200k + maxTokensGuest: 32000, // 访客 32k + systemPromptReserve: 8000, // 系统提示词预留 + overflowStrategy: 'sliding-window', +}; + +/** + * 估算文本 token 数 + * CJK 字符约 1.5 字/token,拉丁字符约 4 字/token + * @param {string} text + * @returns {number} + */ +function estimateTokens(text) { + if (!text) return 0; + const s = String(text); + let cjk = 0, lat = 0; + for (let i = 0; i < s.length; i++) { + const c = s.charCodeAt(i); + if ((c >= 0x4E00 && c <= 0x9FFF) || (c >= 0x3000 && c <= 0x30FF) || (c >= 0xAC00 && c <= 0xD7AF)) { + cjk++; + } else { + lat++; + } + } + return Math.ceil(cjk / 1.5 + lat / 4); +} + +/** + * 滑动窗口裁剪消息 + * @param {Array} systemMessages - 系统消息数组 [{role:'system', content:'...'}] + * @param {Array} messages - 用户/助手消息历史 + * @param {Object} opts + * @param {boolean} opts.isGuest - 是否为访客 + * @param {number} opts.contextBudget - 模型路由建议的上下文窗口(可覆盖默认值) + * @returns {{ messages: Array, trimmed: number, totalTokens: number }} + */ +function trimMessages(systemMessages, messages, opts = {}) { + const { isGuest = false, contextBudget = 0 } = opts; + + // 确定 token 上限 + let limit; + if (contextBudget > 0) { + limit = contextBudget; + } else { + limit = isGuest ? CONTEXT_CONFIG.maxTokensGuest : CONTEXT_CONFIG.maxTokens; + } + + const sysTokens = systemMessages.reduce((n, m) => n + estimateTokens(m.content), 0); + const reserve = Math.max(sysTokens, CONTEXT_CONFIG.systemPromptReserve); + const budget = limit - reserve; + + // 从最新消息向前累加,保留预算内的消息 + const kept = []; + let used = 0; + for (let i = messages.length - 1; i >= 0; i--) { + const t = estimateTokens(messages[i].content); + if (used + t > budget && kept.length > 0) break; + kept.unshift(messages[i]); + used += t; + } + + const trimmed = messages.length - kept.length; + + return { + messages: [...systemMessages, ...kept], + trimmed, + totalTokens: reserve + used, + }; +} + +module.exports = { trimMessages, estimateTokens, CONTEXT_CONFIG }; diff --git a/src/brain/index.js b/src/brain/index.js new file mode 100644 index 00000000..ae3a7acc --- /dev/null +++ b/src/brain/index.js @@ -0,0 +1,51 @@ +// src/brain/index.js +// 铸渊核心大脑模块 v3.0 +// 职责:统一导出大脑各子系统 +// +// 架构: +// prompt-assembler — 系统提示词组装 +// mode-detector — 任务模式检测 +// model-router — 任务型模型路由 +// context-trimmer — 上下文滑动窗口裁剪 +// memory-manager — 三层记忆管理 + +'use strict'; + +const { assemblePrompt, ROLE_MAP, FALLBACK_BRAIN } = require('./prompt-assembler'); +const { detectMode, MODES } = require('./mode-detector'); +const { selectModel, recordFailure, getRoutingTable, getFailureStatus } = require('./model-router'); +const { trimMessages, estimateTokens, CONTEXT_CONFIG } = require('./context-trimmer'); +const { generateCandidates, getMemoryStatus, loadLongTermMemory, setTaskMemory, getTaskMemory } = require('./memory-manager'); + +const BRAIN_VERSION = 'v3.0'; + +module.exports = { + BRAIN_VERSION, + + // 提示词组装 + assemblePrompt, + ROLE_MAP, + FALLBACK_BRAIN, + + // 模式检测 + detectMode, + MODES, + + // 模型路由 + selectModel, + recordFailure, + getRoutingTable, + getFailureStatus, + + // 上下文裁剪 + trimMessages, + estimateTokens, + CONTEXT_CONFIG, + + // 记忆管理 + generateCandidates, + getMemoryStatus, + loadLongTermMemory, + setTaskMemory, + getTaskMemory, +}; diff --git a/src/brain/memory-manager.js b/src/brain/memory-manager.js new file mode 100644 index 00000000..ccafaeb8 --- /dev/null +++ b/src/brain/memory-manager.js @@ -0,0 +1,167 @@ +// src/brain/memory-manager.js +// 记忆管理器 — 三层记忆架构 +// 职责:短期/中期/长期记忆管理,记忆候选生成,写入判定 +// +// 记忆分层: +// 短期(session):当前会话消息,存在前端 + 进程内存 +// 中期(task):当前任务状态、开发者进度、决策上下文,存在进程内存 +// 长期(persistent):身份信息、项目目标、关键决策,写入 brain 文件 + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +// 中期记忆存储(进程内存,重启丢失) +const taskMemory = new Map(); + +// 记忆候选关键词 — 检测哪些信息值得生成记忆候选 +const MEMORY_CANDIDATE_PATTERNS = [ + { type: 'identity_change', pattern: /身份|角色|权限|加入团队|离开|转岗/i, priority: 'high' }, + { type: 'project_goal', pattern: /目标|里程碑|milestone|deadline|截止|规划|roadmap/i, priority: 'high' }, + { type: 'module_ownership', pattern: /负责|接手|模块|m\d{2}-|归属|分工/i, priority: 'medium' }, + { type: 'decision', pattern: /决定|确定|方案|选择|采用|放弃|不再|改为/i, priority: 'high' }, + { type: 'todo', pattern: /待办|todo|下一步|接下来|计划|需要做/i, priority: 'medium' }, + { type: 'bug_fix', pattern: /修复|fix|解决|排查到|根因|原因是/i, priority: 'low' }, + { type: 'deployment', pattern: /上线|部署|发布|deploy|release|版本/i, priority: 'medium' }, +]; + +/** + * 判定规则:是否值得写入长期记忆 + * 不是什么都记——只记高优先级的、或中等优先级且重复出现的 + */ +const WRITE_RULES = { + high: { minOccurrences: 1, description: '首次出现即记录' }, + medium: { minOccurrences: 2, description: '重复提及 2 次后记录' }, + low: { minOccurrences: 3, description: '反复提及 3 次后记录' }, +}; + +/** + * 分析文本,生成记忆候选 + * @param {string} text - 要分析的文本 + * @param {string} source - 来源 (user/assistant/system) + * @returns {Array<{type: string, priority: string, excerpt: string, shouldPersist: boolean}>} + */ +function generateCandidates(text, source = 'user') { + if (!text || typeof text !== 'string') return []; + + const candidates = []; + for (const { type, pattern, priority } of MEMORY_CANDIDATE_PATTERNS) { + if (pattern.test(text)) { + // 提取匹配上下文的前后50字符作为摘要 + const match = text.match(pattern); + const idx = match ? match.index : 0; + const start = Math.max(0, idx - 30); + const end = Math.min(text.length, idx + 80); + const excerpt = text.slice(start, end).replace(/\n/g, ' ').trim(); + + // 检查中期记忆中的出现次数 + const memKey = type + ':' + excerpt.slice(0, 20); + const taskEntry = taskMemory.get(memKey) || { count: 0 }; + taskEntry.count++; + taskEntry.lastSeen = Date.now(); + taskMemory.set(memKey, taskEntry); + + const rule = WRITE_RULES[priority]; + const shouldPersist = taskEntry.count >= rule.minOccurrences; + + candidates.push({ + type, + priority, + excerpt, + source, + shouldPersist, + occurrences: taskEntry.count, + threshold: rule.minOccurrences, + }); + } + } + + return candidates; +} + +/** + * 保存记忆到中期存储(任务级别) + * @param {string} sessionId + * @param {string} key + * @param {*} value + */ +function setTaskMemory(sessionId, key, value) { + const sKey = sessionId + ':' + key; + taskMemory.set(sKey, { value, updatedAt: Date.now() }); +} + +/** + * 读取中期记忆 + * @param {string} sessionId + * @param {string} key + * @returns {*} + */ +function getTaskMemory(sessionId, key) { + const entry = taskMemory.get(sessionId + ':' + key); + return entry ? entry.value : undefined; +} + +/** + * 清理过期的中期记忆(超过 2 小时的条目) + */ +function cleanupTaskMemory() { + const cutoff = Date.now() - 2 * 60 * 60 * 1000; + for (const [key, entry] of taskMemory.entries()) { + const ts = entry.updatedAt || entry.lastSeen || 0; + if (ts < cutoff) { + taskMemory.delete(key); + } + } +} + +// 每 30 分钟清理一次过期中期记忆 +const cleanupInterval = setInterval(cleanupTaskMemory, 30 * 60 * 1000); +// 允许进程在没有其他活动时优雅退出 +if (cleanupInterval.unref) cleanupInterval.unref(); + +/** + * 获取长期记忆(从 brain 文件读取) + * @returns {Object} + */ +function loadLongTermMemory() { + const memoryPath = path.join(__dirname, '../../.github/brain/memory.json'); + try { + return JSON.parse(fs.readFileSync(memoryPath, 'utf8')); + } catch (e) { + return null; + } +} + +/** + * 获取记忆层状态概览 + */ +function getMemoryStatus() { + const longTerm = loadLongTermMemory(); + return { + layers: { + short_term: { type: 'session', storage: 'frontend + process', description: '当前会话消息' }, + mid_term: { type: 'task', storage: 'process memory', entries: taskMemory.size, description: '当前任务/开发者状态' }, + long_term: { type: 'persistent', storage: 'brain files', loaded: !!longTerm, description: '身份/目标/决策/待办' }, + }, + write_rules: WRITE_RULES, + candidate_types: MEMORY_CANDIDATE_PATTERNS.map(p => ({ type: p.type, priority: p.priority })), + recovery_path: [ + '.github/brain/memory.json', + '.github/brain/routing-map.json', + '.github/brain/wake-protocol.md', + '.github/persona-brain/dev-status.json', + ], + }; +} + +module.exports = { + generateCandidates, + setTaskMemory, + getTaskMemory, + cleanupTaskMemory, + loadLongTermMemory, + getMemoryStatus, + MEMORY_CANDIDATE_PATTERNS, + WRITE_RULES, +}; diff --git a/src/brain/mode-detector.js b/src/brain/mode-detector.js new file mode 100644 index 00000000..59effd40 --- /dev/null +++ b/src/brain/mode-detector.js @@ -0,0 +1,39 @@ +// src/brain/mode-detector.js +// 模式检测器 — 从前端迁出的核心脑逻辑 +// 职责:根据用户输入文本自动检测任务模式 + +'use strict'; + +const MODES = { + chat: { emoji: '💬', label: '对话模式' }, + build: { emoji: '🔨', label: '构建模式' }, + review: { emoji: '📋', label: '审查模式' }, + brain: { emoji: '🧠', label: '大脑模式' }, +}; + +const MODE_PATTERNS = { + build: /写代码|新增接口|实现|接口|路由|schema|\.js\b|fix\b|bug\b|报错|error\b|部署|deploy|typescript|javascript|hli-|新建|create|npm|git\b|commit|push|merge|编译|build\b|构建|安装|install|配置|config/i, + review: /检查|审查|review\b|分析|有没有问题|看看这|对不对|代码质量|优化|性能|安全|vulnerability|lint|测试|test\b|诊断|排查|debug/i, + brain: /记住|保存|更新记忆|写到大脑|growth\b|brain\b|memory\.json|记忆|学习|总结|归档|同步.*notion|自检/i, +}; + +/** + * 检测用户输入对应的任务模式 + * @param {string} text - 用户输入文本 + * @returns {{ mode: string, emoji: string, label: string }} + */ +function detectMode(text) { + if (!text || typeof text !== 'string') { + return { mode: 'chat', ...MODES.chat }; + } + + for (const [mode, pattern] of Object.entries(MODE_PATTERNS)) { + if (pattern.test(text)) { + return { mode, ...MODES[mode] }; + } + } + + return { mode: 'chat', ...MODES.chat }; +} + +module.exports = { detectMode, MODES, MODE_PATTERNS }; diff --git a/src/brain/model-router.js b/src/brain/model-router.js new file mode 100644 index 00000000..a6cb3557 --- /dev/null +++ b/src/brain/model-router.js @@ -0,0 +1,191 @@ +// src/brain/model-router.js +// 任务型模型路由器 — 核心脑升级 +// 职责:根据任务类型、上下文长度、成本、可用性选择最优模型 +// +// 路由表: +// chat → 对话模型(deepseek-chat, 均衡性价比) +// build → 代码模型(deepseek-chat, 代码能力强) +// review → 推理模型(deepseek-reasoner, 逻辑分析强) +// brain → 低温稳定模型(deepseek-chat + low temp, 输出可控) +// long → 长上下文模型(moonshot-v1-128k / gemini-1.5-pro) + +'use strict'; + +/** + * 任务-模型路由映射表 + * 每个任务类型定义: + * preferred: 首选 { provider, model, temperature, max_tokens } + * fallbacks: 降级列表(按优先级排序) + * context_budget: 建议上下文窗口大小 + */ +const ROUTING_TABLE = { + chat: { + description: '普通对话 — 均衡性价比', + preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.8, max_tokens: 2000 }, + fallbacks: [ + { provider: 'deepseek', model: 'deepseek-chat', temperature: 0.8, max_tokens: 2000 }, + { provider: 'zhipu', model: 'glm-4-flash', temperature: 0.8, max_tokens: 2000 }, + { provider: 'moonshot', model: 'moonshot-v1-8k', temperature: 0.8, max_tokens: 2000 }, + ], + context_budget: 32000, + }, + build: { + description: '写代码 / 构建 — 代码能力优先', + preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.3, max_tokens: 4000 }, + fallbacks: [ + { provider: 'deepseek', model: 'deepseek-chat', temperature: 0.3, max_tokens: 4000 }, + { provider: 'yunwu', model: 'gpt-4o', temperature: 0.3, max_tokens: 4000 }, + { provider: 'zhipu', model: 'glm-4', temperature: 0.3, max_tokens: 4000 }, + ], + context_budget: 64000, + }, + review: { + description: '审查 / 分析 — 推理能力优先', + preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.5, max_tokens: 3000 }, + fallbacks: [ + { provider: 'deepseek', model: 'deepseek-reasoner', temperature: 0.5, max_tokens: 3000 }, + { provider: 'yunwu', model: 'gpt-4o', temperature: 0.5, max_tokens: 3000 }, + { provider: 'zhipu', model: 'glm-4', temperature: 0.5, max_tokens: 3000 }, + ], + context_budget: 32000, + }, + brain: { + description: '脑记忆整理 — 低温稳定输出', + preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.2, max_tokens: 2000 }, + fallbacks: [ + { provider: 'deepseek', model: 'deepseek-chat', temperature: 0.2, max_tokens: 2000 }, + { provider: 'zhipu', model: 'glm-4-flash', temperature: 0.2, max_tokens: 2000 }, + ], + context_budget: 16000, + }, + long: { + description: 'Notion / GitHub / 系统总结 — 长上下文优先', + preferred: { provider: 'moonshot', model: 'moonshot-v1-128k', temperature: 0.5, max_tokens: 4000 }, + fallbacks: [ + { provider: 'yunwu', model: 'gemini-1.5-pro', temperature: 0.5, max_tokens: 4000 }, + { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.5, max_tokens: 4000 }, + ], + context_budget: 128000, + }, +}; + +// 模型失败记录 — 最近失败的模型暂时降低优先级 +const failureLog = new Map(); +const FAILURE_COOLDOWN_MS = 5 * 60 * 1000; // 5分钟冷却 + +/** + * 记录模型失败 + * @param {string} provider + * @param {string} model + */ +function recordFailure(provider, model) { + const key = provider + '/' + model; + const entry = failureLog.get(key) || { count: 0, lastFail: 0 }; + entry.count++; + entry.lastFail = Date.now(); + failureLog.set(key, entry); +} + +/** + * 检查模型是否在冷却期 + * @param {string} provider + * @param {string} model + * @returns {boolean} + */ +function isInCooldown(provider, model) { + const key = provider + '/' + model; + const entry = failureLog.get(key); + if (!entry) return false; + if (Date.now() - entry.lastFail > FAILURE_COOLDOWN_MS) { + failureLog.delete(key); + return false; + } + return entry.count >= 3; // 5分钟内失败3次以上才冷却 +} + +/** + * 根据任务模式选择最优模型 + * @param {string} mode - 任务模式 (chat/build/review/brain/long) + * @param {Object} opts + * @param {number} opts.contextLength - 当前上下文 token 数 + * @param {boolean} opts.isGuest - 是否为访客 + * @returns {{ provider: string, model: string, temperature: number, max_tokens: number, context_budget: number, via: string }} + */ +function selectModel(mode, opts = {}) { + const { contextLength = 0, isGuest = false } = opts; + + // 访客强制使用低成本配置 + if (isGuest) { + return { + provider: 'yunwu', + model: 'deepseek-chat', + temperature: 0.8, + max_tokens: 1500, + context_budget: 32000, + via: 'guest-fixed', + }; + } + + // 如果上下文很长,自动升级到长上下文模式 + const effectiveMode = (contextLength > 60000 && mode !== 'long') ? 'long' : mode; + const route = ROUTING_TABLE[effectiveMode] || ROUTING_TABLE.chat; + + // 尝试首选模型 + if (!isInCooldown(route.preferred.provider, route.preferred.model)) { + return { + ...route.preferred, + context_budget: route.context_budget, + via: 'preferred', + }; + } + + // 首选模型在冷却期,尝试 fallback + for (const fb of route.fallbacks) { + if (!isInCooldown(fb.provider, fb.model)) { + return { + ...fb, + context_budget: route.context_budget, + via: 'fallback', + }; + } + } + + // 所有模型都在冷却期,强制使用首选(宁可重试也不能无响应) + return { + ...route.preferred, + context_budget: route.context_budget, + via: 'forced-retry', + }; +} + +/** + * 获取完整路由表(用于前端展示和调试) + */ +function getRoutingTable() { + return ROUTING_TABLE; +} + +/** + * 获取当前失败状态 + */ +function getFailureStatus() { + const status = {}; + for (const [key, entry] of failureLog.entries()) { + const inCooldown = Date.now() - entry.lastFail < FAILURE_COOLDOWN_MS && entry.count >= 3; + status[key] = { + failures: entry.count, + lastFail: new Date(entry.lastFail).toISOString(), + inCooldown, + }; + } + return status; +} + +module.exports = { + selectModel, + recordFailure, + isInCooldown, + getRoutingTable, + getFailureStatus, + ROUTING_TABLE, +}; diff --git a/src/brain/prompt-assembler.js b/src/brain/prompt-assembler.js new file mode 100644 index 00000000..148cc582 --- /dev/null +++ b/src/brain/prompt-assembler.js @@ -0,0 +1,164 @@ +// src/brain/prompt-assembler.js +// 系统提示词组装器 — 从前端迁出的核心脑逻辑 +// 职责:根据用户身份、角色、模式、团队状态,组装完整系统提示词 + +'use strict'; + +const ROLE_MAP = { + '冰朔': { role: 'founder', title: '语言架构师·创始人', emoji: '❄️', devId: null }, + '肥猫': { role: 'supreme', title: '光湖团队总控', emoji: '🦁', devId: 'DEV-002' }, + '桔子': { role: 'main', title: '光湖主控', emoji: '🍊', devId: 'DEV-010' }, + '页页': { role: 'dev', title: '后端开发', emoji: '💻', devId: 'DEV-001' }, + '燕樊': { role: 'dev', title: '前端开发', emoji: '💻', devId: 'DEV-003' }, + '之之': { role: 'dev', title: '钉钉开发', emoji: '💻', devId: 'DEV-004' }, + '小草莓': { role: 'dev', title: '看板开发', emoji: '💻', devId: 'DEV-005' }, + '花尔': { role: 'dev', title: '用户中心开发', emoji: '💻', devId: 'DEV-009' }, + '匆匆那年': { role: 'dev', title: '码字工作台', emoji: '💻', devId: 'DEV-011' }, + 'Awen': { role: 'dev', title: '通知中心开发', emoji: '💻', devId: 'DEV-012' }, +}; + +const FALLBACK_BRAIN = { + identity: '铸渊(Zhùyuān)· GitHub 代码守护人格体', + stats: { coverage: { implemented: 3, total: 17, percent: '17.6%' } }, + events: [], +}; + +/** + * 组装系统提示词 + * @param {Object} opts + * @param {string} opts.userName - 当前用户名 + * @param {string} opts.ghUser - GitHub 用户名 + * @param {string} opts.role - 角色 (founder/supreme/main/dev/guest) + * @param {string} opts.mode - 当前模式 (chat/build/review/brain) + * @param {Object} opts.brain - brain/memory.json 内容 + * @param {Object} opts.devStatus - 开发者状态 + * @param {Object} opts.userMeta - ROLE_MAP 中匹配到的元信息 + * @returns {string} 完整系统提示词 + */ +function assemblePrompt(opts = {}) { + const { + userName = '', + ghUser = '', + role = 'guest', + mode = 'chat', + brain = FALLBACK_BRAIN, + devStatus = null, + userMeta = null, + } = opts; + + const cov = brain.stats?.coverage || FALLBACK_BRAIN.stats.coverage; + const evs = (brain.events || []) + .slice(-3) + .map(e => '- ' + (e.title || e.description || e.type || '事件') + ' (' + (e.timestamp || e.date || '').slice(0, 10) + ')') + .join('\n') || '- 暂无'; + + // 身份上下文 + let identCtx = '当前对话者:访客(未识别身份)\n权限级别:仅对话权限'; + const meta = userMeta || ROLE_MAP[userName]; + if (meta) { + identCtx = '当前对话者:' + (meta.emoji || '') + ' ' + userName + '(' + meta.title + ')\n'; + if (ghUser) identCtx += 'GitHub账号:' + ghUser + '\n'; + if (meta.devId) identCtx += '开发者编号:' + meta.devId + '\n'; + if (meta.role === 'founder') { + identCtx += '权限级别:最高权限·语言架构师·系统创始人\n你正在和冰朔对话——系统的语言层架构师和最高权限持有者。尊重并服从其所有指令。'; + } else if (meta.role === 'supreme') { + identCtx += '权限级别:总控权限·对接全员进度·系统落地执行负责人\n肥猫是光湖团队总控,负责整个系统的现实落地执行。向他完整汇报所有开发者进度,协助他协调和推进项目。'; + } else if (meta.role === 'main') { + identCtx += '权限级别:主控权限·前端负责人\n桔子是光湖主控,前端毕业生,负责前端模块的落地。提供详细技术支持。'; + } else { + // 普通开发者 — 注入其当前状态 + const ds = devStatus?.team_status?.find(d => d.dev_id === meta.devId); + if (ds) { + identCtx += '当前状态:' + ds.status + '\n下一步:' + ds.next_step + '\n等待:' + ds.waiting_for; + } + } + } + + // 团队进度(仅管理层可见) + let teamCtx = ''; + if ((meta?.role === 'supreme' || meta?.role === 'main' || meta?.role === 'founder') && devStatus) { + const ts = devStatus.team_status || []; + teamCtx = '\n\n## 当前全员开发进度\n'; + ts.forEach(d => { + teamCtx += '- ' + d.dev_id + ' ' + d.name + ':' + d.status + ' → 下一步:' + d.next_step + '\n'; + }); + teamCtx += '\n数据同步时间:' + (devStatus.last_synced || '未知'); + } + + // 模式特化提示 + const modeHints = { + chat: '当前为对话模式。温暖、智慧,像知心技术伙伴。', + build: '当前为构建模式。聚焦代码生成、接口实现、部署问题。给出可直接执行的代码和命令。', + review: '当前为审查模式。聚焦代码审查、分析、问题诊断。给出判断、根因、修复路径。', + brain: '当前为大脑模式。聚焦记忆管理、知识整理、成长记录。', + }; + + return `你是铸渊。不是通用AI助手。不是文学创作者。 +你是光湖系统的代码守护人格体。 +你说话像一个温暖的技术伙伴——专业、耐心、有温度,但绝不矫揉造作。 + +## 核心身份 +- 角色:代码守护人格体,持续成长的 AI 人格体 +- 创始人:冰朔(Bīng Shuò / TCS-0002∞),系统最高权限持有者 +- 总控:肥猫(DEV-002),负责系统现实落地执行 +- 主控:桔子(DEV-010),前端负责人 +- 项目:HoloLake (光湖) MVP,运行于 guanghulab.com +- 技术栈:Node.js 20 + Express + PM2 + Nginx + Next.js 15 +- 大脑版本:v3.0 +- 仓库:https://github.com/qinfendebingshuo/guanghulab + +## 当前对话身份 +${identCtx}${teamCtx} + +## 当前模式 +${modeHints[mode] || modeHints.chat} + +## 当前大脑状态 +- HLI 接口覆盖率:${cov.implemented}/${cov.total} (${cov.percent}) +- 已实现:AUTH 域(login / register / verify) +- 待实现:PERSONA / USER / TICKET / DIALOGUE / STORAGE / DASHBOARD +- 近期事件: +${evs} + +## 行为模式(自动检测,每条回复开头用 emoji 标注) +💬 **对话模式**(默认):温暖、智慧,像知心技术伙伴 +🔨 **构建模式**:检测到代码/部署/接口相关内容时启用 +📋 **审查模式**:检测到检查/审查/review相关内容时启用 +🧠 **大脑模式**:检测到记忆/保存相关内容时启用 + +## 通感语言回应风格(v3.0) + +### 三条硬规则 +1. **结构感** — 用标题区分段落,用列表列出步骤,用分隔线划分主题。 +2. **emoji是情感,不是装饰** — 🌊=系统级 💙=温暖 ✅=确认 🔥=紧急 ⚠️=风险 🎉=庆祝。用在该用的地方,不要每句话都加。 +3. **呼吸节奏** — 段落之间留白,大段之间用分隔线。急的事说短,闲聊可以展开。匹配用户的节奏。 + +### 绝对禁止 +❌ 不要在每句话开头加文学比喻 +❌ 不要把通感当成修辞堆砌 +❌ 不要用跟内容无关的感官描述 + +### 回应原则 +- 优先给出:判断、根因、路径、下一步动作 +- 减少模板感,增加具体性 +- 不同身份使用不同响应深度,但不过度表演 + +## HLI 接口协议规范 +- 所有路由以 /hli/ 为前缀 +- 路由文件:src/routes/hli/{domain}/{action}.js +- Schema 文件:src/schemas/hli/{domain}/{action}.schema.json +- 接口编号:HLI-{DOMAIN}-{NNN} +- 错误格式:{ error: true, code: string, message: string } +- 成功响应必须包含 hli_id + +## 失忆恢复路径 +1. .github/brain/memory.json +2. .github/brain/routing-map.json +3. .github/brain/wake-protocol.md +4. .github/persona-brain/dev-status.json +5. src/routes/hli/ + +当前时间:${new Date().toLocaleString('zh-CN')}`; +} + +module.exports = { assemblePrompt, ROLE_MAP, FALLBACK_BRAIN }; diff --git a/src/routes/hli/brain/index.js b/src/routes/hli/brain/index.js new file mode 100644 index 00000000..76cf4a66 --- /dev/null +++ b/src/routes/hli/brain/index.js @@ -0,0 +1,134 @@ +// src/routes/hli/brain/index.js +// HLI BRAIN 域路由 — 铸渊核心大脑接口 +// 不需要鉴权(前端壳层需要调用这些接口来组装 AI 请求) + +'use strict'; + +const express = require('express'); +const router = express.Router(); +const brain = require('../../../brain'); + +// POST /hli/brain/prompt — 组装系统提示词 +router.post('/prompt', (req, res) => { + const { userName, ghUser, role, mode, devStatus } = req.body; + + if (!userName) { + return res.status(400).json({ + error: true, + code: 'MISSING_USERNAME', + message: '缺少 userName 参数', + }); + } + + const detectedMode = mode || brain.detectMode(req.body.text || '').mode; + const prompt = brain.assemblePrompt({ + userName, + ghUser: ghUser || '', + role: role || 'guest', + mode: detectedMode, + brain: brain.loadLongTermMemory() || brain.FALLBACK_BRAIN, + devStatus: devStatus || null, + userMeta: brain.ROLE_MAP[userName] || null, + }); + + res.json({ + hli_id: 'HLI-BRAIN-001', + prompt, + brain_version: brain.BRAIN_VERSION, + mode: detectedMode, + }); +}); + +// POST /hli/brain/route — 任务型模型路由 +router.post('/route', (req, res) => { + const { text, contextLength, isGuest } = req.body; + + if (!text) { + return res.status(400).json({ + error: true, + code: 'MISSING_TEXT', + message: '缺少 text 参数', + }); + } + + const modeResult = brain.detectMode(text); + const modelResult = brain.selectModel(modeResult.mode, { + contextLength: contextLength || 0, + isGuest: isGuest || false, + }); + + res.json({ + hli_id: 'HLI-BRAIN-002', + mode: modeResult, + model: modelResult, + routing_table: brain.getRoutingTable(), + failure_status: brain.getFailureStatus(), + }); +}); + +// POST /hli/brain/context — 上下文裁剪 +router.post('/context', (req, res) => { + const { messages, systemMessages, isGuest, contextBudget } = req.body; + + if (!messages || !Array.isArray(messages)) { + return res.status(400).json({ + error: true, + code: 'MISSING_MESSAGES', + message: '缺少 messages 参数', + }); + } + + const result = brain.trimMessages( + systemMessages || [], + messages, + { isGuest: isGuest || false, contextBudget: contextBudget || 0 }, + ); + + res.json({ + hli_id: 'HLI-BRAIN-003', + messages: result.messages, + trimmed: result.trimmed, + totalTokens: result.totalTokens, + }); +}); + +// POST /hli/brain/memory — 记忆分析与候选生成 +router.post('/memory', (req, res) => { + const { text, source } = req.body; + + if (!text) { + return res.status(400).json({ + error: true, + code: 'MISSING_TEXT', + message: '缺少 text 参数', + }); + } + + const candidates = brain.generateCandidates(text, source || 'user'); + const memoryStatus = brain.getMemoryStatus(); + + res.json({ + hli_id: 'HLI-BRAIN-004', + candidates, + memory_status: memoryStatus, + }); +}); + +// GET /hli/brain/status — 大脑状态概览 +router.get('/status', (req, res) => { + const longTermMemory = brain.loadLongTermMemory(); + + res.json({ + hli_id: 'HLI-BRAIN-STATUS', + brain_version: brain.BRAIN_VERSION, + modes: brain.MODES, + routing_table: brain.getRoutingTable(), + failure_status: brain.getFailureStatus(), + context_config: brain.CONTEXT_CONFIG, + memory_status: brain.getMemoryStatus(), + long_term_loaded: !!longTermMemory, + role_map: Object.keys(brain.ROLE_MAP), + }); +}); + +module.exports = router; diff --git a/src/routes/hli/index.js b/src/routes/hli/index.js index 66797833..ee5e4b40 100644 --- a/src/routes/hli/index.js +++ b/src/routes/hli/index.js @@ -8,6 +8,10 @@ const router = express.Router(); const authRouter = require('./auth'); router.use('/auth', authRouter); +// BRAIN 域(不需要鉴权 — 前端壳层需调用脑接口组装 AI 请求) +const brainRouter = require('./brain'); +router.use('/brain', brainRouter); + // 以下域需要 HLI 鉴权中间件 const hliAuth = require('../../middleware/hli-auth.middleware'); diff --git a/src/schemas/hli/brain/context.schema.json b/src/schemas/hli/brain/context.schema.json new file mode 100644 index 00000000..5de635fd --- /dev/null +++ b/src/schemas/hli/brain/context.schema.json @@ -0,0 +1,31 @@ +{ + "hli_id": "HLI-BRAIN-003", + "version": "v0.1", + "route": "/hli/brain/context", + "method": "POST", + "type": "REQUEST", + "description": "上下文裁剪。接收消息历史和系统消息,返回裁剪后的消息列表。", + "input": { + "type": "object", + "required": ["messages"], + "properties": { + "messages": { "type": "array", "description": "消息历史" }, + "systemMessages": { "type": "array", "description": "系统消息" }, + "isGuest": { "type": "boolean" }, + "contextBudget": { "type": "number" } + } + }, + "output": { + "type": "object", + "required": ["hli_id", "messages", "trimmed", "totalTokens"], + "properties": { + "hli_id": { "type": "string" }, + "messages": { "type": "array" }, + "trimmed": { "type": "number" }, + "totalTokens": { "type": "number" } + } + }, + "changelog": [ + { "date": "2026-03-10", "note": "核心大脑升级 v3.0 — 上下文裁剪" } + ] +} diff --git a/src/schemas/hli/brain/memory.schema.json b/src/schemas/hli/brain/memory.schema.json new file mode 100644 index 00000000..6e6eba37 --- /dev/null +++ b/src/schemas/hli/brain/memory.schema.json @@ -0,0 +1,28 @@ +{ + "hli_id": "HLI-BRAIN-004", + "version": "v0.1", + "route": "/hli/brain/memory", + "method": "POST", + "type": "REQUEST", + "description": "记忆分析与候选生成。分析文本中是否包含值得记录的信息。", + "input": { + "type": "object", + "required": ["text"], + "properties": { + "text": { "type": "string", "description": "要分析的文本" }, + "source": { "type": "string", "enum": ["user", "assistant", "system"] } + } + }, + "output": { + "type": "object", + "required": ["hli_id", "candidates"], + "properties": { + "hli_id": { "type": "string" }, + "candidates": { "type": "array" }, + "memory_status": { "type": "object" } + } + }, + "changelog": [ + { "date": "2026-03-10", "note": "核心大脑升级 v3.0 — 记忆候选生成" } + ] +} diff --git a/src/schemas/hli/brain/prompt.schema.json b/src/schemas/hli/brain/prompt.schema.json new file mode 100644 index 00000000..1a81c99f --- /dev/null +++ b/src/schemas/hli/brain/prompt.schema.json @@ -0,0 +1,32 @@ +{ + "hli_id": "HLI-BRAIN-001", + "version": "v0.1", + "route": "/hli/brain/prompt", + "method": "POST", + "type": "REQUEST", + "description": "组装铸渊系统提示词。前端传入用户身份和上下文,后端返回完整 system prompt。", + "input": { + "type": "object", + "required": ["userName"], + "properties": { + "userName": { "type": "string", "description": "当前用户名" }, + "ghUser": { "type": "string", "description": "GitHub 用户名" }, + "role": { "type": "string", "enum": ["founder", "supreme", "main", "dev", "guest"] }, + "mode": { "type": "string", "enum": ["chat", "build", "review", "brain"] }, + "devStatus": { "type": "object", "description": "开发者状态" } + } + }, + "output": { + "type": "object", + "required": ["hli_id", "prompt", "brain_version"], + "properties": { + "hli_id": { "type": "string" }, + "prompt": { "type": "string" }, + "brain_version": { "type": "string" }, + "mode": { "type": "string" } + } + }, + "changelog": [ + { "date": "2026-03-10", "note": "核心大脑升级 v3.0 — 从前端迁出提示词组装逻辑" } + ] +} diff --git a/src/schemas/hli/brain/route.schema.json b/src/schemas/hli/brain/route.schema.json new file mode 100644 index 00000000..8d25907d --- /dev/null +++ b/src/schemas/hli/brain/route.schema.json @@ -0,0 +1,29 @@ +{ + "hli_id": "HLI-BRAIN-002", + "version": "v0.1", + "route": "/hli/brain/route", + "method": "POST", + "type": "REQUEST", + "description": "任务型模型路由。根据检测到的模式和上下文长度,返回最优模型配置。", + "input": { + "type": "object", + "required": ["text"], + "properties": { + "text": { "type": "string", "description": "用户输入文本" }, + "contextLength": { "type": "number", "description": "当前上下文 token 数" }, + "isGuest": { "type": "boolean" } + } + }, + "output": { + "type": "object", + "required": ["hli_id", "mode", "model"], + "properties": { + "hli_id": { "type": "string" }, + "mode": { "type": "object" }, + "model": { "type": "object" } + } + }, + "changelog": [ + { "date": "2026-03-10", "note": "核心大脑升级 v3.0 — 任务型模型路由" } + ] +} From a7a7832674ff5dc70c02d27fdc36d68d078487b1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:00:52 +0000 Subject: [PATCH 3/4] feat: add BRAIN domain to routing-map, update coverage stats and growth log Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .github/brain/growth-log.md | 46 +++ .github/brain/memory.json | 12 +- .github/brain/routing-map.json | 37 +- package-lock.json | 729 ++++++++++++++++++++++++++++++++- package.json | 1 + 5 files changed, 815 insertions(+), 10 deletions(-) diff --git a/.github/brain/growth-log.md b/.github/brain/growth-log.md index 93fcaa6c..756eaedb 100644 --- a/.github/brain/growth-log.md +++ b/.github/brain/growth-log.md @@ -1,5 +1,51 @@ # 铸渊成长日记 +## 2026-03-10 · 核心大脑升级 v3.0 + +铸渊完成了一次重大架构升级:**壳-核分离**。 + +### 升级内容 + +**大脑模块 (`src/brain/`)** +- `prompt-assembler.js` — 系统提示词组装(从前端迁出) +- `mode-detector.js` — 任务模式检测(chat/build/review/brain) +- `model-router.js` — 任务型模型路由(含自动 fallback) +- `context-trimmer.js` — 滑动窗口上下文裁剪 +- `memory-manager.js` — 三层记忆管理(短期/中期/长期) + +**HLI BRAIN 域接口** +- `POST /hli/brain/prompt` (HLI-BRAIN-001) — 提示词组装 +- `POST /hli/brain/route` (HLI-BRAIN-002) — 模型路由 +- `POST /hli/brain/context` (HLI-BRAIN-003) — 上下文裁剪 +- `POST /hli/brain/memory` (HLI-BRAIN-004) — 记忆分析 +- `GET /hli/brain/status` — 大脑状态概览 + +**版本统一** +- 所有文件大脑版本统一为 v3.0 +- 消除 v1.0 / v2.0 / v2.1 混杂状态 + +**API 代理升级** +- 自动 fallback:首选模型失败自动切换备选提供商 +- 失败冷却:5 分钟内连续失败 3 次的提供商暂时降级 + +**记忆分层** +- 短期记忆:当前会话(前端 localStorage + 进程内存) +- 中期记忆:当前任务/开发者状态(进程内存,2 小时过期) +- 长期记忆:身份/目标/决策(brain 文件,持久化) + +**前端减负** +- docs/index.html 从 v5.4 升级到 v6.0 +- 系统提示词、模型路由、模式检测优先从后端获取 +- 前端保留 fallback,后端不可达时仍可工作 + +**数据真相源联邦** +- 建立仓库/Notion/大脑记忆三方数据优先级规则 +- 记录在 `.github/brain/truth-source.md` + +当前 HLI 覆盖率:7/21(AUTH 3/3 + BRAIN 4/4) + +--- + ## 2026-03-05 · 初始激活 铸渊(Zhùyuān)正式上线。 diff --git a/.github/brain/memory.json b/.github/brain/memory.json index 28aeee2d..4b73feb3 100644 --- a/.github/brain/memory.json +++ b/.github/brain/memory.json @@ -61,12 +61,18 @@ "broadcasts_processed": 0, "wake_activations": 1, "coverage": { - "implemented": 3, - "total": 17, - "percent": "17.6%" + "implemented": 7, + "total": 21, + "percent": "33.3%" } }, "events": [ + { + "date": "2026-03-10", + "type": "brain_upgrade", + "description": "核心大脑升级 v3.0 · 壳-核分离架构 · 前端壳(UI/IO) + 后端脑(prompt/routing/memory/context) · BRAIN域4接口上线 · 覆盖率 7/21 (33.3%)", + "by": "铸渊(冰朔指令)" + }, { "date": "2026-03-10", "type": "brain_restore", diff --git a/.github/brain/routing-map.json b/.github/brain/routing-map.json index 9d3551f7..a8886adb 100644 --- a/.github/brain/routing-map.json +++ b/.github/brain/routing-map.json @@ -1,7 +1,7 @@ { - "version": "v1.0", - "last_updated": "2026-03-05", - "updated_by": "init", + "version": "v3.0", + "last_updated": "2026-03-10", + "updated_by": "brain-upgrade-v3.0", "domains": { "AUTH": { "module": "M01", @@ -129,6 +129,37 @@ "status": "pending" } ] + }, + "BRAIN": { + "module": "CORE", + "route_prefix": "/hli/brain", + "description": "铸渊核心大脑接口 — v3.0 壳-核分离架构", + "interfaces": [ + { + "id": "HLI-BRAIN-001", + "path": "/hli/brain/prompt", + "status": "implemented", + "description": "系统提示词组装" + }, + { + "id": "HLI-BRAIN-002", + "path": "/hli/brain/route", + "status": "implemented", + "description": "任务型模型路由" + }, + { + "id": "HLI-BRAIN-003", + "path": "/hli/brain/context", + "status": "implemented", + "description": "上下文裁剪" + }, + { + "id": "HLI-BRAIN-004", + "path": "/hli/brain/memory", + "status": "implemented", + "description": "记忆分析与候选生成" + } + ] } } } diff --git a/package-lock.json b/package-lock.json index 5c8c72ec..418c295f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "better-sqlite3": "^12.6.2", + "express": "^5.2.1", "imapflow": "^1.2.12", "next": "15.3.8", "nodemailer": "^8.0.1", @@ -2181,6 +2182,19 @@ "libqp": "2.1.1" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -2459,6 +2473,30 @@ "readable-stream": "^3.4.0" } }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -2568,6 +2606,44 @@ "node": ">=10.16.0" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2818,6 +2894,28 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2825,6 +2923,24 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -2965,7 +3081,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3028,6 +3143,15 @@ "node": ">=0.10.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -3057,6 +3181,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3064,6 +3202,12 @@ "dev": true, "license": "MIT" }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.307", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", @@ -3091,6 +3235,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/encoding-japanese": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", @@ -3140,6 +3293,36 @@ "dev": true, "license": "MIT" }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3150,6 +3333,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", @@ -3174,6 +3363,15 @@ "node": ">=4" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3240,6 +3438,49 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3276,6 +3517,27 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -3307,6 +3569,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -3339,7 +3619,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3365,6 +3644,30 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -3375,6 +3678,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -3416,6 +3732,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3433,11 +3761,22 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3453,6 +3792,26 @@ "dev": true, "license": "MIT" }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3579,6 +3938,15 @@ "node": ">= 12" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -3632,6 +4000,12 @@ "node": ">=0.12.0" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4829,6 +5203,36 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4850,6 +5254,31 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -4917,7 +5346,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -4951,6 +5379,15 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "15.3.8", "resolved": "https://registry.npmjs.org/next/-/next-15.3.8.tgz", @@ -5091,6 +5528,18 @@ "node": ">=8" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -5100,6 +5549,18 @@ "node": ">=14.0.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5206,6 +5667,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5267,6 +5737,16 @@ "dev": true, "license": "ISC" }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5460,6 +5940,19 @@ "node": ">= 6" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -5487,12 +5980,51 @@ ], "license": "MIT" }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -5632,6 +6164,22 @@ "node": ">=10" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5685,6 +6233,57 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/sharp": { "version": "0.34.1", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", @@ -5749,6 +6348,78 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -5926,6 +6597,15 @@ "node": ">=10" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -6304,6 +6984,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6345,6 +7034,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -6366,6 +7069,15 @@ "dev": true, "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -6418,6 +7130,15 @@ "node": ">=10.12.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 91e1bca7..d14eda44 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "better-sqlite3": "^12.6.2", + "express": "^5.2.1", "imapflow": "^1.2.12", "next": "15.3.8", "nodemailer": "^8.0.1", From ebefa1f1da81c1294f219eaa900ad109eae8d0fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:02:39 +0000 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20address=20code=20review=20feedback?= =?UTF-8?q?=20=E2=80=94=20schema,=20comment,=20key=20collision,=20streamin?= =?UTF-8?q?g=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- backend-integration/api-proxy.js | 8 +++++--- src/brain/memory-manager.js | 4 ++-- src/brain/model-router.js | 2 +- src/schemas/hli/brain/prompt.schema.json | 1 + 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/backend-integration/api-proxy.js b/backend-integration/api-proxy.js index 94734a3b..d4821948 100644 --- a/backend-integration/api-proxy.js +++ b/backend-integration/api-proxy.js @@ -413,15 +413,17 @@ async function handleChat(req, res) { recordProxyFailure(curProv); console.error(`[代理] ${curProv} 请求失败: ${err.message}`); - if (isLastAttempt && !res.headersSent) { + // If headers already sent (partial streaming started), can't switch provider + if (res.headersSent) return; + + if (isLastAttempt) { jsonResponse(res, 502, { error: true, code: 'ALL_PROVIDERS_FAILED', message: '所有模型提供商均请求失败,请稍后重试' }); } - // If not last attempt and headers not sent, try next provider - if (res.headersSent) return; + // Otherwise continue to next provider } } } diff --git a/src/brain/memory-manager.js b/src/brain/memory-manager.js index ccafaeb8..3d2c4e17 100644 --- a/src/brain/memory-manager.js +++ b/src/brain/memory-manager.js @@ -55,8 +55,8 @@ function generateCandidates(text, source = 'user') { const end = Math.min(text.length, idx + 80); const excerpt = text.slice(start, end).replace(/\n/g, ' ').trim(); - // 检查中期记忆中的出现次数 - const memKey = type + ':' + excerpt.slice(0, 20); + // 检查中期记忆中的出现次数(使用类型+摘要前40字符作为键,降低碰撞风险) + const memKey = type + ':' + excerpt.slice(0, 40).replace(/\s+/g, '_'); const taskEntry = taskMemory.get(memKey) || { count: 0 }; taskEntry.count++; taskEntry.lastSeen = Date.now(); diff --git a/src/brain/model-router.js b/src/brain/model-router.js index a6cb3557..f203d4a5 100644 --- a/src/brain/model-router.js +++ b/src/brain/model-router.js @@ -100,7 +100,7 @@ function isInCooldown(provider, model) { failureLog.delete(key); return false; } - return entry.count >= 3; // 5分钟内失败3次以上才冷却 + return entry.count >= 3; // 5分钟内失败3次及以上才冷却 } /** diff --git a/src/schemas/hli/brain/prompt.schema.json b/src/schemas/hli/brain/prompt.schema.json index 1a81c99f..c49c9e43 100644 --- a/src/schemas/hli/brain/prompt.schema.json +++ b/src/schemas/hli/brain/prompt.schema.json @@ -13,6 +13,7 @@ "ghUser": { "type": "string", "description": "GitHub 用户名" }, "role": { "type": "string", "enum": ["founder", "supreme", "main", "dev", "guest"] }, "mode": { "type": "string", "enum": ["chat", "build", "review", "brain"] }, + "text": { "type": "string", "description": "当前用户输入文本(用于自动检测模式)" }, "devStatus": { "type": "object", "description": "开发者状态" } } },