Skip to content

feat(session): Session 隔离— 边界/生命周期/记忆归属统一隔离 - #1248

Closed
cjl-ux wants to merge 38 commits into
TencentCloud:feat/server_teamfrom
cjl-ux:feat/session-isolation-v2
Closed

cjl-ux wants to merge 38 commits into
TencentCloud:feat/server_teamfrom
cjl-ux:feat/session-isolation-v2

Conversation

@cjl-ux

@cjl-ux cjl-ux commented Sep 3, 2026

Copy link
Copy Markdown

背景

以 MemoryProxy/docs/design/2026-08-27-session-isolation-design.md 为唯一方案基准(#1196 不作为参考)。
目标:任何两个请求可判定是否同属一个逻辑会话;会话状态与记忆归属按
(spaceId, userId, agentSource, sessionId) 命名空间隔离,任何 agent 客户端(含未接入白名单的新客户端)同等生效。

变更内容

  • 会话解析统一入口:stages/session.ts::sessionStage + SessionAdapter(anthropic/chat/codex/workbuddy)
  • store 键单点约定:�uildStoreSessionKey(handler / session 路由 / 遥测 / archive fence 共用)
  • auto 会话 ID:HMAC 签名绑定(keyId+scope+首问指纹)、deterministic 派生、per-key/per-key-msg、ghost 回退修复
  • 归属 fencing 两道防线:store 恢复层跨 user/space 视为新会话;archive L0 写侧 fence(含 binding 补查、fenceMiss/fenceCoverage)
  • 生命周期:L1 有界 LRU、周期 cleanup、有界台账、bypass 自愈
  • 可观测:决策计数/分解、/session-debug、prometheus 指标、admin 同口径鉴权
  • Agent 无关性:任意 agentSource 独立命名空间 + auto 会话按 agent+space 隔离(含新增未知 agent 测试与文档)

验证

px tsc --noEmit:0 错误

pm test:23 文件 309/309 通过(stages-session/forward/archive/obs、session-isolation、session-store-fence、routes-session-*、server-session-debug)

说明

cjl-ux added 30 commits August 28, 2026 22:05
- workbuddyHandler: 实现 Responses->Chat(chatCompletions) 转换,修复 PROXY_WORKBUDDY_CHAT_COMPLETIONS=1 声明但代码缺失导致 /workbuddy/*/v1/responses 原样转发上游 404;补上游 apiKey 注入
- codexHandler: 修复转换后的 body 被丢弃、原样转发导致 422 的问题
- handler/codex/workbuddy: 修复只带 x-api-key 的客户端被 401 missing user_key 拒绝(extractBearerToken 空串不回退)
- start-proxy.sh: 整体挂载本地 src 解决镜像缺模块 ERR_MODULE_NOT_FOUND;MSYS_NO_PATHCONV=1 修复 Windows Git Bash 挂载路径拼成 xxx;D 导致 config 挂载失效;workbuddy 条目补上游 key
- .gitignore: 忽略 MemoryProxy/docs 本地个人文档
- 新增 deploy/opik-compose.yml(官方栈:MySQL/ClickHouse/Redis/ZooKeeper/MinIO/backend/frontend),backend 固定映射 8080:8080 供 proxy 上报
- 新增 clickhouse_config(shard/replica 宏 + enable_time_type)、nginx_default_local.conf、otel-collector-config.yaml
- quickstart 5.1:compose 启动补 --profile opik;trace 查询改用前端 /api 路径
- 10 个常驻服务(mysql/redis/clickhouse/zookeeper/minio/backend/python-backend/frontend/jaeger/otel-collector)加 restart: unless-stopped,配合 Docker Desktop 开机自启后容器自动拉起
- pull_policy 默认 always -> missing:镜像已在本地时不再强制拉取(避免离线/镜像源不可达时 up -d 失败)
- 一次性 init 容器(mc/clickhouse-init/demo-data-generator)不加 restart,避免退出后循环重启
…y-msg 多窗口

- x-task-id 可选:header 预选路径 team+agent 即可注册;新增 taskMissingPolicy(skip 默认 / default 用 defaultTaskId 占位 / reject 走 onMismatch)
- 显式无效 x-task-id → 按 mismatch 处理(非静默忽略),满足验收标准
- autoConversationId:per-key(默认单活跃会话)与 per-key-msg(按首条用户消息指纹区分多窗口)两种策略;30 分钟 TTL + LRU 上限
- 显式会话 header 始终优先,完全向后兼容
- start-proxy.sh / .env.example 新增 PROXY_TASK_MISSING_POLICY / PROXY_AUTO_CONVERSATION_STRATEGY,自动会话默认开启
- 新增 7 个单测(per-key-msg 3 + taskMissingPolicy 4),全量 85/85 通过
…测/E2E 补全

- auto-session 支持可注入时钟与测试重置(TTL/窗口/容量可确定性测试)
- 新增 created/resumed 语义:续接会话不再误报为新建
- per-agent taskMissingPolicy(openclaw/hermes 放宽、其余严格)+ 未知 agent 走全局
- 新增验收测试 session-acceptance.test.ts(ACC-1..6)
- optimizations.test.ts 扩充:TTL 滑动窗口、per-key-msg 窗口上限/过期、容量清理、
  指纹稳定性、跨协议首条用户消息指纹、codex 显式会话 header 对齐
- 新增 scripts/qa/session-policy-e2e.sh 真实 HTTP 端到端冒烟(按 [session-auto] 日志断言)
- 新增 docs/session-policy.md(策略说明、验收对照表、E2E 用法)
- vitest 全量 116/116,tsc --noEmit 0 错误
…esumed

- extractCodexSessionId 原只认 session-id/client_metadata,忽略 x-conversation-id,
  导致 codex /responses 路径显式会话 header 失效、autoConversationId 错误接管;
  已与 resolveConversationId 的 header 集合对齐(session-id/x-conversation-id/
  x-session-id/x-chat-id/x-thread-id)
- 三个 handler 的自动会话日志改为 action=created|resumed,续接不再误报新建
- 由 E2E(ACC-4)实测发现并回归验证
- 阶段化:stages/session|forward|archive|obs + ReqCtx/SessionAdapter
- Session 隔离:确定性 sid(scope/指纹绑定 + epoch 桶)、store 归属 fencing、
  写侧 archive fence、会话结束台账、按 agent/space 分解计数、稳定 fallback 键
- 协议/公共能力:chat-anthropic/responses 兼容、error-hint、sse、headers 去重
- 部署:global-images env/start 调整

回归:258/258 + tsc 0
- Anthropic→Chat SSE:tool_calls 序号按 tool_use 出现顺序重映射为 0..n-1,
  不再泄漏 Anthropic content block index(thinking/text 占位会导致跳号)
- recordCacheUsage 按输入方字段语义读取(chat cached_tokens /
  anthropic cache_read_input_tokens),缓存命中率指标不再恒 0
- chat tool_choice:none → 移除全部 tools(Anthropic 无 none 语义,
  保留 tools 会反转成 auto)
- 回归:+7 用例(流式双场景、none 语义、cache 统计双向)
上游只发 finish_reason/[DONE] 或直接 EOF 时,finish() 现在先 ensureStarted()
再输出 message_delta/message_stop,避免缺头的非法 Anthropic SSE 流。
回归:+1 用例(空内容流仍以 message_start 开头)
…c),修复上游 URL 拼成不存在 /responses 的 404
- createChatSseToResponses 改为按 chat tool index 维护独立输出项,修复并行工具调用参数串流(前一把空参数、后一把收走全部碎片)
- createChatSseToAnthropicSse 支持 thinking: map|strip(默认 strip,与 chatJsonToAnthropicJson 一致),signature 先于 thinking 输出,开 text/tool 前先关 thinking 块
- system/developer/assistant content 数组统一提取文本,不再 JSON.stringify 当正文
- protocol-stats 采样改环形缓冲,避免每次 shift O(n)
- 新增并行工具、thinking 默认 strip、内容数组回归用例(85 -> 89)
… 键别名归一

- doRebuild 遇到 binding.userId 与当前用户不符时只拒绝恢复、不再 deleteBinding(键仅含 space+session,删了会清掉 owner 记录);owner 自己的 agent-not-found 清理保留
- autoConversationId 的隔离 scope 并入 spaceId/agentSource,同一 key 下不同客户端/space 不再共享 auto 会话
- 修正 per-key 容量淘汰时 recordExpired 误记当前请求 keyId 的问题
- hydrateFromDb 统一走 buildStoreSessionKey(workbuddy→codex 别名归一)
- 新增跨用户 binding 保留/owner 清理、agent+space 隔离回归用例(297 -> 304)
@cjl-ux cjl-ux changed the title feat(session): Session 隔离新方案(TRACK 04)— 边界/生命周期/记忆归属统一隔离,Agent 无关 feat(session): Session 隔离— 边界/生命周期/记忆归属统一隔离 Sep 3, 2026
@cjl-ux cjl-ux closed this Sep 3, 2026
@cjl-ux
cjl-ux deleted the feat/session-isolation-v2 branch September 3, 2026 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant