Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@
- DSH Desktop 0.11.0 将 `dsh-ui` 围栏呈现为 `<dsh-ui>…</dsh-ui>` 纯文本标签时,DOM 通道现可识别助手消息中的完整标签文本,渲染卡片并在卸载时恢复原文(#268)。
- `list` 项支持 `label`/`name` 标题字段与 `description`/`content`/`text`/`body`/`detail` 正文字段;只有正文的记录也能显示,无法渲染的记录字段会给出位置明确的警告(#265)。
- **GenUI reasoning-only 回合进入宿主重试**:当前回合调用 `validate_dsh_ui` 后,普通顶层会话若以 `stop` 结束且完整内容块只有 reasoning,插件会将终止结果改写为 `EMPTY_RESPONSE`,交由宿主现有重试策略处理;普通会话、子代理、辅助请求及含正文或工具调用的结果保持原行为。`fenceFeedback: false` 仍只关闭同回合围栏修正(#259)。
- **完善 GenUI 交付判定与 fence-feedback 重载恢复**:`render_ui` 只有明确返回 `status=rendered` 才计为正式交付,正常返回的 `status=invalid` 会继续进入零交付修正;plugin reload 时主动读取历史会话,恢复当前 turn 的 render fingerprint、delivery turn 和已消费的 correction budget,避免重复发送 delivery reminder 或重新获得修正次数;render fingerprint 去重仅限当前 turn,新 turn 可重新修正相同错误。每 turn 最多两条 correction 的上限由 render failure 与 nothing-delivered reminder 共同使用(#263、#267)。
- **非英文宿主 locale 下围栏永远不渲染**:通用代码块的判定依赖硬编码标题白名单(`Code` / `Code block` / `代码块`),宿主 locale 为其它语言时(如俄语包的 «Код»),本地化标题被当成真实语言,同一份 `dsh-ui` 围栏既不走带标签路径也不进内容识别——界面永远停在代码块。现在判定反转:banner 标签命中**已知真实语言闭集**(语言 id 有限、由插件维护)才视为带语言,空标签与任何本地化通用词一律视为无语言、交给内容校验把关——同一份正文的渲染结果不再因宿主语言而不同(#258)。
- **围栏自修:每轮两次机会 + 把正文原样交回**。真实会话里「第一次修正(围栏渲染失败)被模型答成又一个只含 reasoning 的回合」出现 4/4 次——单次预算让这些轮次直接空转;同时那条 reasoning-only 修正只描述要求、未给出目标,模型会把上一轮的思考逐字节重放。现在:每轮至多两次修正(第二次专用于该情形)、reasoning-only 修正直接**附上要输出的围栏正文**并要求「只输出这一段、不要先写思考」,渲染失败路径仍受「每个围栏至多一次」约束(#235、#240)。
- **同一份围栏允许重试**:上面的「每轮两次」曾把第二次机会让「同一围栏只修一次」的账本挡掉——真实卡住的那轮里,模型收到修正后返回了**逐字节相同**的 reasoning(`len=1402 · md5=118fcc9af635`),指纹相同 ⇒ 判定为「已修过」⇒ 不再重试,`turn/end` 后正文仍为空。现在 reasoning-only 路径**不再受该账本约束**(正文本身没问题,问题只是模型不写进正文),只受每轮预算约束;第二次的文案也改为「这是第 2 次要求:你已经连续把回答写在思考里、正文留空」,以便打破原样重放。
- **思考块里的围栏会被追回正文**:模型有时把 `dsh-ui` 围栏写进 reasoning(思考)块就结束本轮——正文里没有围栏,界面什么都没挂载,用户只看到一张折叠的思考卡(真实会话 1020 turns 里出现 5 次)。现在 `fence-feedback` 在 `agent/turn-stopping` 边界发现「正文无围栏、reasoning 里有完整围栏」时,借宿主的轮内转向把 `next=emit_fence_in_body` 送回同一轮,要求把同一份围栏原样写进回答正文;与渲染失败路径共用同一套上限(子代理不触发、可取消;每轮预算见下条)(#235)。
- **多行文本/代码的复制不再丢换行**:行内渲染曾把每个 `\n` 变成 `<br>`,而 `<br>` 对 `textContent` 与 `Selection.toString()` **零贡献**——用户选中表格单元格复制出来的是一整行,`python - <<'PY' … PY` 这类 heredoc 结构被毁、必须手工拼回。现在换行在 DOM 里保持为**真实换行符**(选中/复制原样),由容器的 `white-space` 负责呈现:代码形单元格 `.tdCode` 用 `pre-wrap`(换行 + 行首缩进都保留),散文单元格 `.tdMultiline` 用 `pre-line`(换行呈现、空格照常折叠),单行单元格仍是 `nowrap`;`calloutBody`/`liTitle`/`liDesc`/`kvValue`/`tlDesc`/`detailBody`/`accBody` 同步声明 `pre-line`,因为它们此前正是靠 `<br>` 硬断行(#233)。
- **多行表格单元格的缩进**:`.table td` 的 `nowrap`(表格的数据语气)会折叠单元格里的空白,模型写进单元格的代码缩进会消失;行内 `` `code` `` 同步改为 `pre-wrap`,不再吞掉自身空格(#233)。
- **「未转义引号 + 尾部杂字符」叠在一起时围栏不再整条失守**:模型常在 JSON 根值之后追加 `</p>` 或一句解释(真实会话 539 条围栏里 240 条有尾随 `</p>`),这类正文本来靠 `parsePartialGenuiSpec` 的平衡前缀就能渲染;但一旦**同时**还有字符串内未转义的半角引号,tier-1 因为「必须整体 parse 通过」而放弃、tier-2 同样放弃——两个缺陷叠加就把整条围栏变成代码块 + 红横幅。现在 tier-2 在整体 parse 失败时回退到**平衡前缀**(根值结束处)并采用它;tier-1 保持严格(它也在流式期运行,采用前缀可能发布半截正文)。真实语料 37 条不可渲染 → 22 条(救回 15 条,占失败数 40%)。
- SKILL.md 使用规则第 3 条补充:`}` 之后不要再写任何字符(常见错误是追加 `</p>` 或解释)。
- **通用代码块的内容识别接受标点级修复**:宿主会隐去它不认识的语言(高亮器不支持 `dsh-ui`),此时同一份围栏在 DOM 里是一个「代码块 / Code block / Code」标签的普通代码块;当 ChatSnapshot 的语言来源在那一行上不可用时,内容识别是唯一出路。此前它要求 `JSON.parse(raw)` **直接**通过,于是「只差一个未转义引号(tier-1 可修)」的围栏永远不会被接管——真实会话(seq 40530)里模型终于把围栏写进正文、正文经 tier-1 修 14 处后可正常渲染,界面却始终停在代码块。现在内容识别跑与带标签路径**相同的 tier-1 修复**,并且在整体 parse 失败时**裁到第一个平衡根值**(`trimToBalancedRoot`,只裁剪、不补全结构)——真实样本里模型把工具调用模板泄漏在 JSON 之后且围栏没闭合,正是这一种;结构级 tier-2 刻意不参与(兜底不该接管只是"长得像 JSON"的普通代码),canonical 规范 / 未知字段 / 归一化等价的检查保持不变。
- **回合状态的两处漏补救**:① 失败的 `render_ui` 调用曾被算作「已交付」——只要本回合调用过 `render_ui`,回合边界就认定已回答、不再提醒,渲染失败的用户拿不到任何纠正。现在交付只由 `tool/result` 决定(无内部错误且结果块非 `isError` 才算成功),结果未到达时回合边界保持静默、不与晚到结果赛跑。② 同回合的合成上下文消息(`agent.inject()` 通知、成员消息等)会清掉本回合的验证/交付状态——它们走 `user/message` 但并非真实用户提示,回合边界检查时状态早已被清空。现在重置只发生在正式的 `turn/start` 边界与直接用户提示(`source.kind === 'user'`)上(#236 review 确认的两项)。
- **回合状态的两处漏补救**:① `render_ui` 是否交付由 `tool/result` 决定,只有无内部错误、结果块非 `isError` 且协议明确返回 `status=rendered` 才算成功;结果未到达时回合边界保持静默,不与晚到结果赛跑。② 同回合的合成上下文消息(`agent.inject()` 通知、成员消息等)不会清掉本回合的验证/交付状态;重置只发生在正式的 `turn/start` 边界与直接用户提示(`source.kind === 'user'`)上(#236、#263 review)。

## [0.11.3] - 2026-09-29

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ The following is the detailed capability reference. Every behavior is constraine
- **Event loop**: buttons, checkboxes, radio buttons, switches, selects, inputs, textareas, submits, and quizzes send one event immediately per gesture; slider drags keep trailing-edge debounce, sending only the final value per slider, with different `id`s handled independently
- **Tool channel**: the `render_ui` tool renders the same spec as a card in the tool row (deliverable-style UI goes through the tool, answer-style UI through the fence)
- **Session panel**: a persistent dock above the composer; `render_ui` / `panel: true` fences update the same surface in place; `/panel` opens it from the client (`/panel <instruction>` customizes via the model, `/panel clear` clears); the top border is draggable to resize; `append: true` merges incrementally — same-named tabs append content, new tabs get added; the whole panel caps at 200 nodes / 200 appends, after which the model should send `replace` to rebuild
- **Fence auto-repair**: enabled by default; set `fenceFeedback: false` in this plugin's config (under the plugin entry's `config:` in your profile's cordis.patch.yml) to disable same-turn fence corrections. A validated GenUI turn that ends with reasoning only still becomes `EMPTY_RESPONSE` and uses the host's retry policy. When a reply's final dsh-ui fence fails to render, the plugin steers the SAME turn with the per-node diagnosis so the model can resend a fixed fence; at most two corrections per turn (the second is reserved for a first correction that was answered with another reasoning-only turn) and one per fence body, never in subagents, so it cannot loop. A turn whose body has no fence but whose reasoning composed one is corrected the same way, with the body handed back verbatim.
- **Fence auto-repair**: enabled by default; set `fenceFeedback: false` in this plugin's config (under the plugin entry's `config:` in your profile's cordis.patch.yml) to disable same-turn fence corrections. A validated GenUI turn that ends with reasoning only still becomes `EMPTY_RESPONSE` and uses the host's retry policy. When a reply's final dsh-ui fence fails to render, the plugin steers the SAME turn with the per-node diagnosis so the model can resend a fixed fence. At most two corrections are sent per turn, shared by render failures and validated turns with no formal delivery; each fence fingerprint is corrected once per turn, and a nothing-delivered reminder is sent once per turn. A new turn can correct the same failed fence again. Plugin reload restores the current turn's fence fingerprints and correction count, along with delivery reminders, from persisted correction messages. Corrections are never sent in subagent sessions.
- **Self-healing & limits**: every fence passes a spec guard — bad nodes are silently dropped (the surviving siblings keep rendering: one bad component no longer degrades the whole fence), numbers clamped, strings truncated; the whole tree is capped at 200 nodes / 8 nesting levels; pathological specs never crash the UI
- **Canonical component protocol**: native field aliases such as `card.label` → `title`, `table.data`/`table.items` → `rows`, `callout.kind`/`callout.desc` → `tone`/`content` (tone value `danger` → `error`), `steps.items` → `steps`, `keyvalue.items` → `pairs` (record `label` → `key`), and `file-tree.nodes` → `items` (record `label` → `name`, `type` defaults to `dir` when children exist) are normalized deterministically before validation and rendering. A root-level component array is adopted as `items`, and a double-encoded JSON string is decoded once. `validate_dsh_ui` reports these normalizations and warns about unknown native fields without blocking custom renderer nodes.
- **Chart error self-healing**: mermaid failures auto-retry with repairs (strip backticks, quote Chinese/space labels, remove `<br/>`) before degrading to source; a broken chart never hits the screen
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ dsh plugin --profile web add link:$PWD
- **事件循环**:按钮、复选框、单选、开关、下拉、输入、文本域、提交和测验等离散交互按一次手势一次事件立即回传模型;`slider` 连续拖动保留尾沿防抖,同一滑块只回传最终值,不同 `id` 的滑块互不合并。
- **工具通道**:`render_ui` 工具把同一份 spec 渲染成工具行卡片(交付物型 UI 走工具、回答型 UI 走围栏)
- **会话面板**:composer 上方常驻 dock,`render_ui` / `panel: true` 围栏原地更新同一块界面;`/panel` 命令客户端直开(`/panel <指令>` 转模型定制、`/panel clear` 清空);顶边框可拖拽调高;`append: true` 增量合并——同名标签页追加内容、新标签页新增;整面板默认最多 200 节点 / 200 条追加,达到上限后模型应发送 `replace` 重建
- **围栏自修**:默认开启;在 profile 的 cordis.patch.yml 中本插件条目的 `config:` 下设置 `fenceFeedback: false` 可以关闭同回合围栏修正。已调用 `validate_dsh_ui` 的 GenUI 回合若只输出 reasoning 并以 `stop` 结束,仍会转换为 `EMPTY_RESPONSE`,交给宿主重试策略处理。回答最终的 dsh-ui 围栏无法渲染时,插件借宿主的轮内转向(steer)把逐节点诊断送回**同一轮**,模型重发修好的围栏;每轮至多两次(第二次专门用于「第一次修正被答成又一个只含思考的回合」)、每个围栏至多一次、子代理不触发,不会循环。正文没有围栏但 reasoning 里写好了成品的回合同样会被修正,并把那段正文原样交回。
- **围栏自修**:默认开启;在 profile 的 cordis.patch.yml 中本插件条目的 `config:` 下设置 `fenceFeedback: false` 可以关闭同回合围栏修正。已调用 `validate_dsh_ui` 的 GenUI 回合若只输出 reasoning 并以 `stop` 结束,仍会转换为 `EMPTY_RESPONSE`,交给宿主重试策略处理。回答最终的 dsh-ui 围栏无法渲染时,插件借宿主的轮内转向(steer)把逐节点诊断送回**同一轮**,模型重发修好的围栏。每个 turn 最多发送两条 correction,渲染失败与“已验证但没有正式交付”共用该上限;同一 turn 内相同围栏 fingerprint 只修正一次,新 turn 可以重新修正相同错误;同一 turn 的零交付提醒只发送一次。plugin reload 后会从持久化的 correction message 恢复当前 turn 的围栏去重记录、已消费的 correction 次数以及零交付提醒记录。子代理回合不会收到围栏修正。
- **自愈与上限**:每个围栏过规格守卫——坏节点静默丢弃(同围栏其余组件照常渲染,单个坏组件不再拖垮整条围栏)、数值钳位、字符串截断,整树 ≤200 节点 / 8 层嵌套,病态 spec 不会拖垮界面
- **统一组件协议**:`card.label` → `title`、`table.data`/`table.items` → `rows`、`callout.kind`/`callout.desc` → `tone`/`content`(tone 值 `danger` → `error`)、`steps.items` → `steps`、`keyvalue.items` → `pairs`(记录内 `label` → `key`)、`file-tree.nodes` → `items`(记录内 `label` → `name`、有 children 时缺省 `dir`)等原生字段别名会在校验和渲染前确定性归一化;根级组件数组视为 `items`、双重编码的 JSON 字符串解一层;`validate_dsh_ui` 会报告归一化结果,并对原生组件未知字段给出警告,同时保持自定义 renderer 节点的透明兼容。
- **图错误自愈**:mermaid 渲染失败自动修复重试(剥反引号、引号化中文/空格标签、去 `<br/>`),仍失败才降级源码;错误图永不直接上屏
Expand Down
Loading
Loading