feat(web,ws): 会话级消息缓存 + 图桶 + 忙态按会话过滤(裁决 16.4.5/16.4.2 + 修 16.4.1 的帧缺口)

- 16.4.5:非激活会话的帧**不再丢弃**,按自己的 sessionId 落进该会话桶(新增 store/sessionCache.ts 纯函数 +
  useAppStore.sessionMessages/flowGraphs);切会话改为「存回/取出」而非清空;流式增量 O(1) 定位桶、
  只延长已存在的助手气泡(不凭一帧 delta 凭空造消息)
- 16.4.2:图按会话入桶(上限 12 个),Backthink 页取 sess-backthink 的图而不再被用户会话图顶替;
  空态区分「实时通道未连接」与「尚未产生链路图」;wire_runtime 的图源白名单注释按裁决口径重写(零行为改动)
- 修 16.4.1 的缺口:handlers/session.ts 补 `session_idle` / `session_interrupted`(此前落到未知帧兜底 ⇒
  sessionBusy 可能卡 true ⇒ 预约永不落地);忙态**置忙与置闲两侧**统一按 frameIsActive 过滤(只过滤置闲
  会让后台会话的忙帧点亮徽标而它自己的 idle 被丢 ⇒ 永久卡忙)
- 清理:删除 lib/streamScope.ts(口径已被 16.4.5 推翻)与只剩测试在用的 messageEventApplies
- 复核:tsc 0 错 + vite build 成功;messageSync 69 / session 19 项逻辑测试;4 条变异反证
This commit is contained in:
AskaEth committed 2026-10-04 13:41:02 +08:00
1 parent f454273c5a
commit b480b04dd0
15 files changed
+733 -300

No files matched your search

+12 -6
View File
@@ -173,15 +173,21 @@ func (a *app) wireRuntime() error {
tracer := trace.New(a.traceWriter)
// Realtime flow visualization (design/19 §1): the publisher accumulates the
// loop's trace spans into a per-session flow graph and publishes
// ws.graph_update on each span transition. It is gated to the owner's web
// sessions ("web" and web sub-sessions), so IM / stranger / sub-agent turns
// never leak into the admin panel. A nil flow keeps the loop's prior behavior.
// ws.graph_update on each span transition.
//
// 图源白名单(裁决 16.4.2):它决定**哪些会话产出实时链路图**,也是后端能提供
// 完整链路导图的那一侧 —— 主人 web 会话族("web" 主会话 + "web:<uuid>" 次级会话,
// IM 主人私聊镜像也落主会话)与自主后台思考会话(design/20 §2)。子代理的 span 由
// loop.WithFlowBranch 并进**派生它的那个会话**,渲染成该会话图的一条泳道(design/19
// §2),因此也在这份白名单里。
//
// 注意口径(裁决 16.4.2):design/19 §1 的「仅发布/不发」说的是**用户侧前端**观察到
// 的现象,不是「后端可以少画图」——管理后台的图源由此保持完整,与用户端发布策略
// 是两件事。隔离的渠道会话(群聊/陌生人,sess-xxx)不在此白名单内:那是 design/19 §1
// 发布会话范围(裁决 01.4.5)的既有裁决,本批未改。nil flow 保持 loop 原行为。
flowPub := loop.NewFlowPublisher(
func(subject string, payload any) { _ = a.bus.Publish(context.Background(), subject, payload) },
func(sessionID string) bool {
// Publish the web/main flow graph (web chat + IM owner-private mirror) AND the
// autonomous-backthink session, so the backthink page's flow link graph renders
// too (design/20 §2). Other isolated channel sessions (sess-xxx) stay filtered.
return sessionID == history.MainSessionID ||
strings.HasPrefix(sessionID, history.MainSessionID+":") ||
sessionID == backthink.DefaultSessionID