首页
/ claude-mem Server Beta 路由对等地图:从 Worker 遗留 /api/* 迁移到 /v1/* 的完整对照指南

claude-mem Server Beta 路由对等地图:从 Worker 遗留 /api/* 迁移到 /v1/* 的完整对照指南

2026-09-06 15:18:18作者:劳婵绚Shirley

本文基于仓库中的 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/*(或其他非遗留路径)下有自有的原生实现,客户端应当迁移过去;
  • adaptersrc/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.tsPOST /v1/sessions/start 的 create-or-find 语义)。

3.1 为什么 init 没有适配器而 observations/summarize 有

POST /v1/sessions/startServerV1PostgresRoutes.ts 中是幂等的 create-or-find:同一 externalSessionId 的并发调用通过唯一约束兜底。而 init 的旧契约(隐式返回本地 DB 自增 id)无法在不改变客户端解析逻辑的前提下翻译,因此选择"硬切换 + 404 提示",把迁移压力留给钩子层;observations 与 summarize 的旧请求体则可以在服务端无损翻译,所以保留适配器。

4. 兼容性适配器的源码级实现

4.1 观察事件适配器:遗留 payload 到 agent_event 的翻译规则

SessionsObservationsAdapter.ts 头部注释完整声明了四条翻译规则:

  1. contentSessionId(Claude Code 会话 UUID)成为 Server beta server_sessions 行的 external_session_id,作用域限定在 API key 所属的 team 与 project 内,采用 create-or-found 语义;
  2. 工具调用形状(tool_nametool_inputtool_responsetool_use_id)被映射为 sourceAdapter='claude-code-compat'eventType='tool_use'agent_event,payload 逐字保留遗留字段;
  3. API key 必须是项目作用域——跨项目(team 级)的兼容调用返回 400,绝不允许兼容流量绕过项目作用域;
  4. 适配器从不触碰 worker 代码,从不直接入队观察事件,也从不使用 src/services/worker/* 的类型。

请求体通过 zod schema 校验(contentSessionIdtool_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)、versionuptimeinitializedmcpReadydependenciesqueue 等诊断信息——这意味着探针可以直接用它区分两种运行时。

/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/searchPOST /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/,只允许调用白名单内的四个目标:

适配器绝不触碰 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. 迁移决策清单

基于这张对等地图,客户端迁移路径可以归纳为:

  1. 会话生命周期POST /v1/sessions/start 直接切换(旧 init 会 404);observationssummarize 可先经适配器无感运行,再按计划切到 POST /v1/eventsPOST /v1/sessions/:id/end,两个端点均要求项目作用域 API key(scope 含 memories:write)。
  2. 探测:用 GET /api/healthruntime 字段确认目标端实际运行时是 server-beta 还是 worker,再决定走哪条路径。
  3. 搜索/上下文:全部改用 POST /v1/searchPOST /v1/context(JSON 体 {projectId, query, limit}),不要期待遗留 GET 形状。
  4. 必须留在 worker 的能力:数据查看器的 /api/* 只读数据路由、corpus/Chroma 全家族、/api/settings 可变设置面、/api/logs 文件日志——这些是 Server beta 的有意识缺口,需要时保留 worker 运行时即可,两套运行时可在同一部署中并存。

该地图随 Phase 演进更新((暂无) 与"计划中"条目即后续阶段的工作项),阅读时以仓库中 docs/server-parity-map.md 的当前版本为准。

登录后查看全文
热门项目推荐
相关项目推荐