claude-mem Server Beta 路由对等地图:从 Worker 遗留 /api/* 迁移到 /v1/* 的完整对照指南
本文基于仓库中的 Server Beta Parity Map,逐一梳理 claude-mem 遗留 worker 运行时下所有 /api/* HTTP 路由在 Server beta 运行时中的对等状态(native / adapter / unsupported),并结合 兼容性适配层源码、v1 路由实现 与 运行时选择器,给出每个路由的迁移方式、不可迁移的原因及底层实现依据,帮助你在部署 claude-mem 时完成从 worker 运行时到 Postgres + BullMQ 服务端的客户端迁移。
1. 双运行时并存:Server beta 如何被选中
claude-mem 当前同时存在两套服务运行时:
- Worker 运行时(遗留):本地
claude-mem worker,SQLite 存储,提供全部/api/*遗留路由。它是当前默认运行时; - Server beta 运行时:Postgres 存储 + BullMQ 队列 + API key 鉴权的可部署服务端(架构细节见 docs/server.md),提供规范的
/v1/*API 和少量兼容路径。
Server beta 运行时通过环境变量 CLAUDE_MEM_RUNTIME=server-beta 选择。从源码结构看,钩子侧的运行时选择逻辑位于 runtime-selector.ts:
export function selectRuntime(): SelectedRuntime {
const settings = loadFromFileOnce();
const raw = (settings.CLAUDE_MEM_RUNTIME ?? 'worker').trim().toLowerCase();
// 接受规范值 'server'(Phase 1a)与遗留字面量 'server-beta' 以兼容已安装的 settings.json
if (raw === 'server' || raw === 'server-beta') return 'server';
return 'worker';
}
从源码注释可以看出,'server' 是 cmem-sdk 更名后的规范值,'server-beta' 作为遗留字面量仍被接受,因此 Parity Map 中使用的 CLAUDE_MEM_RUNTIME=server-beta 与现网 settings.json 中的 CLAUDE_MEM_RUNTIME=server 等价。选择 server 运行时后,客户端通过 ServerClient 携带三个配置项访问服务端:
| 设置键(新) | 回退键(遗留) | 用途 |
|---|---|---|
CLAUDE_MEM_SERVER_URL |
CLAUDE_MEM_SERVER_BETA_URL |
服务端 Base URL |
CLAUDE_MEM_SERVER_API_KEY |
CLAUDE_MEM_SERVER_BETA_API_KEY |
Bearer API key |
CLAUDE_MEM_SERVER_PROJECT_ID |
CLAUDE_MEM_SERVER_BETA_PROJECT_ID |
项目作用域 ID |
任一缺失都会触发 [server-fallback] 告警并回退到 worker 路径(见 buildServerContext() 中 reason=missing_base_url / missing_api_key / missing_project_id 三个分支),这是 Parity Map 中"客户端应迁移到新客户端(ServerBetaClient)"说法的具体落地。
2. 三种对等状态的语义
Parity Map 为每个遗留路由标注三种状态之一,这是整份地图的阅读基础:
native— Server beta 在/v1/*(或其他非遗留路径)下有自有的原生实现,客户端应当迁移过去;adapter— src/server/compat/* 下的兼容性适配器将遗留请求体翻译为/v1/*等价的代码路径;适配器的响应形状保留 worker 的形态,因此旧客户端无需任何改动即可继续工作;unsupported— Server beta 运行时故意不提供该路由,文档内注明原因。需要该能力的客户端必须继续使用 worker 运行时。
3. 会话生命周期路由(遗留 /api/sessions/*)
| 遗留路径 | Server beta 原生替代 | 适配器 | 状态 |
|---|---|---|---|
POST /api/sessions/init |
POST /v1/sessions/start |
(无适配器——客户端应直接调用 /v1/sessions/start) |
native* |
POST /api/sessions/observations |
POST /v1/events |
SessionsObservationsAdapter.ts | adapter |
POST /api/sessions/summarize |
POST /v1/sessions/:id/end |
SessionsSummarizeAdapter.ts | adapter |
* 标注为 native 但带星号的路由表示:规范替代已在 /v1/* 下存在,但不提供自动翻译。遗留钩子层被期望直接改用新客户端调用。旧的 worker 客户端若仍向 Server beta 端口 POST /api/sessions/init,会得到 404——这是有意设计,因为契约本身不同:init 隐式地创建一个会话 DB id,而 /v1/sessions/start 返回的是项目作用域的 server_session UUID(对应 ServerV1PostgresRoutes.ts 中 POST /v1/sessions/start 的 create-or-find 语义)。
3.1 为什么 init 没有适配器而 observations/summarize 有
POST /v1/sessions/start 在 ServerV1PostgresRoutes.ts 中是幂等的 create-or-find:同一 externalSessionId 的并发调用通过唯一约束兜底。而 init 的旧契约(隐式返回本地 DB 自增 id)无法在不改变客户端解析逻辑的前提下翻译,因此选择"硬切换 + 404 提示",把迁移压力留给钩子层;observations 与 summarize 的旧请求体则可以在服务端无损翻译,所以保留适配器。
4. 兼容性适配器的源码级实现
4.1 观察事件适配器:遗留 payload 到 agent_event 的翻译规则
SessionsObservationsAdapter.ts 头部注释完整声明了四条翻译规则:
contentSessionId(Claude Code 会话 UUID)成为 Server betaserver_sessions行的external_session_id,作用域限定在 API key 所属的 team 与 project 内,采用 create-or-found 语义;- 工具调用形状(
tool_name、tool_input、tool_response、tool_use_id)被映射为sourceAdapter='claude-code-compat'、eventType='tool_use'的agent_event,payload 逐字保留遗留字段; - API key 必须是项目作用域——跨项目(team 级)的兼容调用返回 400,绝不允许兼容流量绕过项目作用域;
- 适配器从不触碰 worker 代码,从不直接入队观察事件,也从不使用
src/services/worker/*的类型。
请求体通过 zod schema 校验(contentSessionId 与 tool_name 必填,tool_use_id/toolUseId 双命名兼容),随后交给 IngestEventsService.ingestOne() 走与 /v1/events 相同的 ingestion 管线。响应刻意沿用旧形状,让老客户端只检查 status 字段即可:
// 旧客户端只检查 `status`,故保留遗留响应形状
res.json({
status: 'queued',
observationCount: 1,
sessionId: session.id,
serverSessionId: session.id,
eventId: result.event.id,
generationJobId: result.outbox?.id ?? null,
transport: result.enqueueState,
});
4.2 resolveServerSession:幂等与会话竞争的兜底
两个适配器共用的 resolveServerSession 负责按平台作用域的 (project, team, externalSessionId) 查或建 server_session 行。源码注释专门处理了一个并发场景:多个兼容客户端同时观察到"不存在"并同时调用 repo.create,第二个会撞上唯一约束((project_id, idempotency_key) 或平台作用域的 external_session_id 索引)。实现上捕获 Postgres 唯一约束错误码 23505 后重新取行返回,保证遗留客户端永远不会看到 500。
4.3 摘要适配器:保留 subagent 跳过语义
SessionsSummarizeAdapter.ts 把遗留 /api/sessions/summarize 翻译为 EndSessionService.end() 调用。它额外保留了一条遗留语义:旧 worker 对 subagent 上下文发出的 summarize 会跳过,适配器中对应保留为:
if (parsed.data.agentId) {
res.json({ status: 'skipped', reason: 'subagent_context' });
return;
}
再次摘要同一会话会收敛到同一条 outbox 行,因为 (team_id, project_id, source_type='session_summary', source_id) 的 UNIQUE 约束始终生效——与 /v1/sessions/:id/end 的幂等保证完全一致。
4.4 测试证据
tests/compat/sessions-observations-adapter.test.ts 在真实 Postgres 测试库(CLAUDE_MEM_TEST_POSTGRES_URL)上验证了两层行为:单元层(HTTP 触发适配器的 legacy → AgentEvent 翻译)与集成层(compat → IngestEventsService → Postgres 的 outbox 行 + BullMQ 入队)。测试用例同时构造了 team 作用域 key 与 project 作用域 key,断言前者可以走 /v1/events 但被兼容路由拒绝(400),后者被允许——与第 4.1 节第 3 条规则一一对应。
5. 健康检查与运行时信息
| 遗留路径 | Server beta 原生替代 | 适配器 | 状态 |
|---|---|---|---|
GET /api/health |
GET /api/health(同路径) |
(无——同路径) | native |
GET /api/info |
GET /v1/info |
(无) | native |
GET /healthz |
GET /healthz(同路径) |
(无) | native |
/api/health 由两种运行时共享的 Server 类提供;当 Server beta 运行时激活时,JSON 负载包含 runtime: "server-beta" 字段。这一行为可在 Server.ts 中得到印证:健康检查响应在 options.runtime 存在时展开 runtime 字段,并附带 status(BullMQ + Redis 异常时降级为 503/degraded)、version、uptime、initialized、mcpReady、dependencies、queue 等诊断信息——这意味着探针可以直接用它区分两种运行时。
/api/info 仅由 worker 运行时提供;Server beta 客户端应改用 /v1/info(定义于 ServerV1Routes.ts)。
6. 搜索、上下文与指令路由
| 遗留路径 | Server beta 原生替代 | 状态 |
|---|---|---|
GET /api/search |
POST /v1/search |
unsupported(遗留 GET,见注 1) |
GET /api/timeline |
(暂无) | unsupported |
GET /api/search/observations |
POST /v1/search |
unsupported(遗留形状,新客户端使用 /v1/search) |
GET /api/search/by-file |
(暂无) | unsupported |
GET /api/context/recent |
POST /v1/context |
unsupported(遗留 GET 形状) |
GET /api/context/preview |
(暂无) | unsupported |
GET /api/context/inject |
(暂无) | unsupported |
POST /api/context/semantic |
POST /v1/context |
unsupported |
GET /api/onboarding/explainer |
(暂无) | unsupported |
GET /api/timeline/by-query |
(暂无) | unsupported |
注 1:遗留
GET /api/search接受查询串参数,返回非规范化(denormalized)的 SQLite 形状结果。Server beta 的POST /v1/search接受 JSON 请求体{projectId, query, limit},返回规范化后的 observation 数组。项目刻意不做遗留形状的适配,原因有二:(a) 遗留调用方已分期迁移到走/v1/search的 MCP 搜索工具;(b) 支持 SQLite 形状意味着要把 SQLite 读层塞回 Postgres 运行时,这与 Phase 9 的反模式守卫直接冲突。
POST /v1/search 与 POST /v1/context 均实现在 ServerV1PostgresRoutes.ts,二者共享同一条 FTS 检索路径与同样的作用域守卫,/v1/context 额外返回上下文包(Phase 8)。
7. 记忆写入、设置与日志路由
7.1 记忆写入
| 遗留路径 | Server beta 原生替代 | 状态 |
|---|---|---|
POST /api/memory/save |
POST /v1/memories |
unsupported(遗留 schema——新客户端使用 /v1/memories) |
POST /v1/memories 在 Server beta 中是"直接/手动插入 observation"的路由(见 ServerV1PostgresRoutes.ts 的注释 "compat alias")。
7.2 设置与运行时控制
| 遗留路径 | Server beta 原生替代 | 状态 |
|---|---|---|
GET /api/settings |
(无——server-beta 中设置即环境变量) | unsupported |
POST /api/settings |
(无——server-beta 中设置即环境变量) | unsupported |
GET /api/mcp/status |
GET /v1/info |
unsupported(遗留形状) |
POST /api/mcp/toggle |
(无——server-beta 中 MCP 常开) | unsupported |
Server beta 的设置就是环境变量加上 api_keys 表中的 API key 面,不存在可变的用户设置 JSON 文件。因此没有 settings 读写 API:配置发生在部署层(compose 环境变量、API key 生命周期管理),而非运行时 HTTP 层。
7.3 日志
| 遗留路径 | Server beta 原生替代 | 状态 |
|---|---|---|
GET /api/logs |
(无——server-beta 日志走 stdout) | unsupported |
POST /api/logs/clear |
(无——日志为只追加流) | unsupported |
这与容器化部署的日志采集方式(stdout 交给容器运行时)一致。
8. 数据查看器路由(只读遗留数据)
| 遗留路径 | Server beta 原生替代 | 状态 |
|---|---|---|
GET /api/observations |
POST /v1/search / /v1/context |
unsupported(见注 2) |
GET /api/summaries |
(暂无) | unsupported(注 2) |
GET /api/prompts |
(暂无) | unsupported(注 2) |
GET /api/observation/:id |
(暂无) | unsupported |
GET /api/observations/by-file |
(暂无) | unsupported |
POST /api/observations/batch |
(暂无) | unsupported |
GET /api/session/:id |
GET /v1/sessions/:id |
unsupported(遗留形状) |
POST /api/sdk-sessions/batch |
(暂无) | unsupported |
GET /api/prompt/:id |
(暂无) | unsupported |
GET /api/stats |
(暂无) | unsupported |
GET /api/projects |
GET /v1/projects(计划中) |
unsupported |
GET /api/processing-status |
(暂无) | unsupported |
POST /api/processing |
(暂无) | unsupported |
POST /api/import |
(暂无) | unsupported |
注 2:遗留数据查看器路由返回跨 worker 专属表(如
sdk_sessions.message_id)JOIN 出的 SQLite 形状行。Server beta 的数据存在 Postgres 中,形状是另一种规范化结构。要复现遗留的 JOIN 形状,需要一个与规范/v1/*API 相互竞争的翻译层——这在 Phase 9 的范围之外。查看器 UI 目前继续使用 worker 的/api/*数据路由;在纯 Server beta 部署中,查看器被期望直接调用/v1/*(计划在后续阶段实现)。这些路由被标注为unsupported,是为了让调用方明确知道:若需要遗留 SQLite 数据查看器,必须运行 worker 运行时。
9. 语料库(Corpus)与 Chroma 向量状态
| 遗留路径 | 状态 |
|---|---|
POST /api/corpus |
unsupported |
GET /api/corpus |
unsupported |
GET /api/corpus/:name |
unsupported |
DELETE /api/corpus/:name |
unsupported |
POST /api/corpus/:name/rebuild |
unsupported |
POST /api/corpus/:name/prime |
unsupported |
POST /api/corpus/:name/query |
unsupported |
POST /api/corpus/:name/reprime |
unsupported |
GET /api/chroma/status |
unsupported(server-beta 为 Postgres-only) |
Corpus 是 worker 功能,由 Chroma 提供向量后端;Server beta 的存储层是 Postgres-only。把 corpus 子系统迁移到 Server beta 不在 Phase 9 范围内——因此依赖 corpus 工作流的部署必须保留 worker 运行时。
10. 反模式守卫:Parity Map 的强制性边界
Parity Map 最后给出了 Phase 9 强制执行的两条 grep 守卫,必须返回零匹配:
rg -n "services/worker/http/routes|WorkerService" src/server/compat src/server/runtime
rg -n "from '.*services/worker" src/server/compat
即兼容适配器位于 src/server/compat/,只允许调用白名单内的四个目标:
- src/server/services/IngestEventsService.ts
- src/server/services/EndSessionService.ts
src/storage/postgres/*- src/server/middleware/postgres-auth.ts
适配器绝不触碰 worker 路由类、worker 的 DatabaseManager 或 WorkerService——这正是 Phase 9 强制的"承重级解耦":Server beta 的 HTTP 与队列管线在代码上不可能依赖遗留 worker,两个运行时可以独立演进、独立部署(对应 docs/server.md 中"容器 entrypoint 运行 bun server-service.cjs --daemon 且从不运行 bun worker-service.cjs"的声明,并有 e2e 脚本 scripts/e2e-server-docker.sh 断言任何容器内都不存在 worker-service.cjs 进程)。
11. 迁移决策清单
基于这张对等地图,客户端迁移路径可以归纳为:
- 会话生命周期:
POST /v1/sessions/start直接切换(旧 init 会 404);observations与summarize可先经适配器无感运行,再按计划切到POST /v1/events与POST /v1/sessions/:id/end,两个端点均要求项目作用域 API key(scope 含memories:write)。 - 探测:用
GET /api/health的runtime字段确认目标端实际运行时是server-beta还是 worker,再决定走哪条路径。 - 搜索/上下文:全部改用
POST /v1/search与POST /v1/context(JSON 体{projectId, query, limit}),不要期待遗留 GET 形状。 - 必须留在 worker 的能力:数据查看器的
/api/*只读数据路由、corpus/Chroma 全家族、/api/settings可变设置面、/api/logs文件日志——这些是 Server beta 的有意识缺口,需要时保留 worker 运行时即可,两套运行时可在同一部署中并存。
该地图随 Phase 演进更新((暂无) 与"计划中"条目即后续阶段的工作项),阅读时以仓库中 docs/server-parity-map.md 的当前版本为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00