Repository navigation
fix: AskUserQuestion 工具卡回显用户实际选择(结构化答案走 display 载荷) - #6
Merged
7836246 merged 2 commits intoSep 26, 2026
Merged
Conversation
症状:桌面端 AskUserQuestion 工具卡在用户提交答案后,每题下方恒显示
「未提供回答」,展开模型后续 thinking 却能看到真实选择。
根因:答案在链路里没丢,是投影层不携带结构化 answers。工具结果文本被
formatModelContent 拍成 `User has answered your questions: "Q"="A"`,而
前端按其自身纪律不解析这段文本(nonCliAcpRetirement 明确断言该文本不得
被当作答案);row.input 又始终是模型声明的原始 questions(v4 投影从不
回填 modifiedInput),于是 readAskUserQuestionAnswers 的四种载体一个都
不命中,必然落到 noAnswerText。
修复:给结构化答案建一条 display 通道,而不是回填 row.input——冷恢复时
row.input 来自 durable transcript 的模型原始入参(不含 answers),而
display 会随 completedToolPartMetadata 落进 tool part 的
metadata.display,冷恢复由同一份 metadata 重建同一事件,实时链路与
replayable/冷恢复因此同源。
- contracts 新增 askUserQuestionToolResultDisplayPayloadSchema:
{kind:"ask_user_question", answers: Record<问题原文, string>},并入落库
display union。空对象是「用户未作答、runtime 自动继续」的显式语义,
与无 display(拒绝/取消/旧数据)区分。
- createAskUserQuestionDisplay 只从 handler 的结构化 output 取值,单条答案
按 4 KiB 字节截断(display 不受 tool result budget 约束),截断标记内联;
问题原文作为 key 不截断,否则命中不了 row.input 里的查表。
- packages/shared 的 v4 镜像同步新增成员:两侧都是 strict union,少一处会
让 toolOutput.display 被 .catch(undefined) 静默剥掉——这正是本 bug 的
原始形态。
- 附带 spec:docs/ask-user-question-answer-display.md(产品规则、单一
所有者、数据流、接口上限、验收场景、兼容边界)。
验证:新增 core producer 测试 5/5(多题逐条映射、多选字符串不被改写、空
对象保留、非本工具形态不产出、落库 schema 与 tool part metadata 往返、
超长截断仍过 schema)、bootstrap 投影测试 4/4(row.output.display 到达且
经 conversationRowSchema 校验后仍在场、模型可见文本与 row.input 不受影响、
空对象保留、无 display 不凭空产生);pnpm typecheck(根 + @kcode/cli
turbo 27/27)通过;pnpm lint 0 错误;architecture:check --changed 0 违规;
本次新增/改动文件 oxfmt 干净。
接上 display 通道并补齐展示口径: - readAskUserQuestionAnswers 增加 display 载体(优先级最高:desktop v4 唯一携带结构化 answers 的通道),其后仍是既有 output / input.answers / raw.* 分支,旧客户端与旧快照行为不变。 - ask-question.tsx 把 readToolResultDisplay(toolCall.raw) 传进解析,与 send-message / task-output 等卡片用同一套 shared strict schema。 - 多选答案在上游就已被合并成 ", " 字符串(客户端提交与 broker 的 normalizeAnswerValue 同款),卡片按产品列表约定在 multiple 问题上换成 「,」。已知代价:多选题里自由输入字面出现的 ", " 会按列表分隔显示 (只动标点,不改写内容),单选不动。 - 旧的单条 selected / answered_custom 形态不带问题标识,只归属单题输入; 多题输入下不再凭空记到第一题(宁可落到无答案文案,也不显示错位答案)。 验证:新增 UI 测试 6/6(display 回显、空对象语义、非本 kind 的 display 不 参与解析、多选「,」、单选原样与无答案回落、协议镜像 strict 且接受该 kind、旧形态守卫),UI 全量 66/66;pnpm typecheck 通过;pnpm lint 0 错误; architecture:check --changed 0 违规;改动文件 oxfmt 干净。
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.
症状
桌面端 AskUserQuestion 工具卡在用户提交答案后,每题下方恒显示「未提供回答」,而展开模型后续 thinking 能看到真实选择(答案没丢,是展示层读不到)。
根因
v4 投影层不携带结构化
answers:formatModelContent拍成User has answered your questions: "Q"="A",而前端按其自身纪律不解析这段文本;row.input始终是模型声明的原始questions(v4 投影从不回填modifiedInput)。于是
readAskUserQuestionAnswers支持的四种载体一个都不命中,必然落到无答案文案。修复
给结构化答案建一条
display通道。没选「回填row.input」是因为冷恢复时row.input来自 durable transcript 的模型原始入参(不含 answers),而display会随completedToolPartMetadata落进 tool part 的metadata.display,冷恢复由同一份 metadata 重建同一事件 —— 实时链路与 replayable/冷恢复同源。askUserQuestionToolResultDisplayPayloadSchema({kind, answers}),并入落库 display union;空对象是「用户未作答、runtime 自动继续」的显式语义,与无 display(拒绝/取消/旧数据)区分。createAskUserQuestionDisplay只从 handler 的结构化 output 取值,单条答案按 4 KiB 字节截断(display 不受 tool result budget 约束)。packages/shared的 v4 协议镜像同步新增成员:两侧都是 strict union,少一处会让toolOutput.display被.catch(undefined)静默剥掉 —— 这正是本 bug 的原始形态。readAskUserQuestionAnswers增加 display 载体(优先级最高);多选答案按产品列表约定用「,」显示;旧的单条selected/answered_custom形态不带问题标识,改为只归属单题输入。docs/ask-user-question-answer-display.md。验收
单题单选回显选项文本;多题每题分别回显;多选以「,」连接;自定义输入回显输入文本;无答案自动继续显示「未回答,已自动继续」;冷恢复与实时一致。
验证
row.output.display到达且经conversationRowSchema校验后仍在场、模型可见文本与row.input不受影响)、UI 解析 6(display 回显、多选「,」、旧形态守卫、协议镜像 strict)。pnpm typecheck(根 +@kcode/cliturbo 27/27)通过;pnpm lint0 错误;architecture:check --changed0 违规。Resources/glm/kcode.cjs)与 renderer(app.asar)两侧均确认含新代码。说明
这两个提交原先落在 #5(fork main 全量提交堆)里,为了让本修复可独立审查与合并,已从该分支剥离为独立分支;#5 本身不变。