feat(card): 新增 persistentStreamCard 配置支持会话内复用同一张流式卡片#557
Draft
yujiaze wants to merge 1 commit into
Draft
Conversation
默认行为下每轮对话都会「发一张新流式卡 + 撤回上一轮旧卡」,长会话里卡片 反复重发/撤回,体验较吵。新增 per-bot 全局配置 persistentStreamCard: 开启后一个会话只在首轮 POST 一张卡片,后续每轮都用 updateMessage 原地 PATCH 同一张卡(标题跟随最新一轮),不再发新撤旧。 实现要点: - daemon.ts beginNewTurn:持久模式且已有存活卡片(streamCardId + workerPort) 时,跳过冻结旧卡入 frozenCards、且不置 streamCardPending。下一轮 screen_update 因此天然命中 worker-pool 既有的复用 PATCH 分支 (restoredCardId = streamCardId && !streamCardPending),无需新增 PATCH 逻辑。 首轮无卡时仍照常 POST 一次。 - 刻意不干预 re-fork / relay / repo 切换路径(它们显式清空 streamCardId, worker 端口已失效无法可靠复用),保持原有重发行为。 - 与 disableStreamingCard(完全不发卡)互斥,后者优先。 配置贯穿:bot-registry(字段 + normalize)、card-prefs-store(接口/默认值/ 读写同步)、dashboard-ipc-server + bot-payload + bot-defaults(前端类型)+ bot-defaults-page(卡片行为区新增开关)+ i18n(中英文案)。 文档:docs-site zh|en 的 cards.md 与 bots-json.md。 影响面:改动集中在飞书卡片层(im/lark 无关,daemon 卡片编排 + card-prefs), 与 CLI / 后端(PtyBackend/TmuxBackend)无关,默认关闭时行为完全不变。 话题会话 / 群会话 / 私聊均走同一 beginNewTurn 路径,一致生效。 验证: - tsc 全量编译通过(--noEmit 与 emit 均 exit 0) - dashboard 前端 esbuild bundle 成功 - 直接相关单测通过:card-prefs-auto-start + bot-registry(×2) 共 91/91 - 邻近单测通过:event-dispatcher + card-handler-grant + cli-adapters 共 550/550 - 全量 unit 另有若干 pre-existing 失败(session-discovery / v3-distillation-runner / vc-meeting 等,与本改动无关;stash 对比干净基线上同样失败)
deepcoldy
reviewed
Jul 22, 2026
deepcoldy
left a comment
Owner
There was a problem hiding this comment.
这里有一个会绕过 persistentStreamCard 的后续 turn 路径,建议在 ready 前修掉并补回归测试:
src/im/lark/card-handler.ts 的 retry_last_task 仍无条件执行 ds.streamCardPending = true。该分支会先 scheduleCardPatch 更新当前卡,但后续任一 screen_update 会因为 pending=true 进入 worker-pool 的 POST 分支,发新卡并 recall 旧卡。因此开启 persistentStreamCard 后,用户从限额卡点击“重试上次任务”仍会出现本 PR 想消除的“发新卡 + 撤旧卡”。现有 test/card-integration.test.ts 还固定断言 retry 后 pending=true,说明当前测试没有覆盖新开关语义。
建议:
- retry 路径与
beginNewTurn共用同一“本 turn 是否复用已有卡”判断(至少在 persistent + real card + live worker 时保持 pending=false); - 新增核心状态机回归:默认模式第二轮仍 POST;persistent 模式第二轮只 PATCH 同一 message id;
retry_last_task在 persistent 模式也只 PATCH;首轮、re-fork/relay 等刻意例外保持原行为; - PR 描述说“一个会话每个后续 turn 都复用”,但实现刻意排除了 re-fork/relay/repo switch,文档最好同步说明这些例外。
本地验证:pnpm build 通过;相关 6 个测试文件 325/325 通过,但本 PR 没新增测试覆盖上述分支。全量 unit 为 9861/9865,3 个时区失败可在 PR parent 复现,另 1 个 v3-host 并发用例单跑即通过,均与本改动无关。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
默认每轮对话都「发新流式卡 + 撤回旧卡」,长会话反复重发/撤回体验较吵。
改动
新增 per-bot 全局配置
persistentStreamCard:开启后一个会话只在首轮 POST 一张卡,后续每轮用
updateMessage原地 PATCH 同一张卡(标题跟随最新一轮)。与disableStreamingCard互斥(后者优先),默认关闭、行为不变。实现要点
beginNewTurn:持久模式且已有存活卡片时,跳过冻结旧卡、不置streamCardPending,使下轮 screen_update 天然命中 worker-pool 既有复用 PATCH 分支
(restoredCardId = streamCardId && !streamCardPending),首轮无卡仍 POST 一次。
影响面
集中在飞书卡片编排层,不碰 im/lark 事件路由,与 CLI / 后端无关;话题群/普通群/
私聊同走 beginNewTurn 一致生效;默认关闭时行为完全一致。
验证
vc-meeting 等,与本改动无关,stash 对比干净基线同样失败)