feat(card): 新增 persistentStreamCard 配置支持会话内复用同一张流式卡片 - #557
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
left a comment
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 并发用例单跑即通过,均与本改动无关。
|
感觉是不是用dashboard中的关闭流式卡片功能就行了,需要的时候/card召唤卡片? |
|
感谢这个方案!我们重新对照了一下现有能力:如果核心诉求是减少群里的状态卡噪音,Dashboard 中的「关闭飞书流式卡片」配合 这和本 PR 的 |
背景
默认每轮对话都「发新流式卡 + 撤回旧卡」,长会话反复重发/撤回体验较吵。
改动
新增 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 对比干净基线同样失败)