发帖前必读
要解决的问题
LLM 的文本输出与终端显示强耦合:模型输出的原文直接进渲染层,无法在「显示层」做重渲染。典型场景:
- 把
<mermaid> 标签 / mermaid ``` 代码块在回合结束后渲染成 ASCII 图就地展示
- 表格美化、长输出折叠、脱敏展示等显示层变换
现有 hook 事件都无法安全地只改「显示」:在 Stop / PostToolUse 等钩子里改写消息会污染 transcript 与模型上下文,破坏 prompt cache 与对话一致性。
建议方案
新增第 28 个 hook 事件 AssistantRender:
- 触发时机:回合末(Stop hooks 之后)逐条 assistant 消息触发,输入
message_id + text_blocks(非空 text 块原文与 block_index)
- 输出协议:
hookSpecificOutput.updatedBlocks = [{blockIndex, text}],未列出的块显示原文
- 仅替换显示层:结果写入内容寻址缓存(
sha1(原文)[:16]),transcript 与模型上下文始终保留原文
- 单脚本语义:仅支持注册一个有效 hook(去重后计数 >1 时发可见警告并整体不渲染)——渲染不是管道,各 hook 拿到的都是原始 text_blocks,多脚本只会重复渲染并互相覆盖
- 零开销短路:无 hook 注册时不构造输入、不 spawn
- 安全边界:30s 超时(显示路径钩子收紧);
/clear 与 --resume/--continue 时清空渲染缓存
使用示例(settings.json):
{
"hooks": {
"AssistantRender": [
{ "hooks": [{ "type": "command", "command": "node ~/plugins/mermaid-inline.mjs" }] }
]
}
}
hook 从 stdin 收到:
{ "hook_event_name": "AssistantRender", "message_id": "msg_...", "text_blocks": [{ "block_index": 0, "text": "<mermaid>...</mermaid>" }] }
stdout 返回:
{ "continue": true, "hookSpecificOutput": { "hookEventName": "AssistantRender", "updatedBlocks": [{ "blockIndex": 0, "text": "ASCII 图" }] } }
即可把该 text 块的终端显示替换为渲染结果。
配套实现:renderCache 模块、MessageRow renderCacheVersion / 重绘纪元重绘机制(解决 OffscreenFreeze 静态定格行的重印)、/clear 缓存清理接线、全类型层注册(HOOK_EVENTS ×4、zod Schema、SDK 生成类型)。
考虑过的替代方案
- 在 Stop / PostToolUse 钩子里改写消息:污染 transcript 与模型上下文,破坏 prompt cache 与对话一致性,放弃。
- 把渲染器硬编码进核心:不可扩展,社区无法挂自己的渲染管道,放弃。
- 多 hook 管道式串联:渲染不是管道(各 hook 只见原始输入,看不到上一个 hook 的输出),串联语义不成立,故采用单脚本语义。
补充信息
- 测试:新增 3 个测试文件共 13 用例(执行器 0 / 1 / >1 注册三等价类、内容寻址缓存行为、memo 比较器版本号机制),
bun test 全过;tsc --noEmit 零错误;biome 通过
- 真实场景端到端验证过:mermaid 源码块在回合末被渲染类 hook 就地替换为 ASCII 图,transcript 中始终为源码,
/clear 后缓存正确失效
- 实现见配套 PR
发帖前必读
要解决的问题
LLM 的文本输出与终端显示强耦合:模型输出的原文直接进渲染层,无法在「显示层」做重渲染。典型场景:
<mermaid>标签 /mermaid ``` 代码块在回合结束后渲染成 ASCII 图就地展示现有 hook 事件都无法安全地只改「显示」:在 Stop / PostToolUse 等钩子里改写消息会污染 transcript 与模型上下文,破坏 prompt cache 与对话一致性。
建议方案
新增第 28 个 hook 事件
AssistantRender:message_id+text_blocks(非空 text 块原文与block_index)hookSpecificOutput.updatedBlocks = [{blockIndex, text}],未列出的块显示原文sha1(原文)[:16]),transcript 与模型上下文始终保留原文/clear与--resume/--continue时清空渲染缓存使用示例(settings.json):
{ "hooks": { "AssistantRender": [ { "hooks": [{ "type": "command", "command": "node ~/plugins/mermaid-inline.mjs" }] } ] } }hook 从 stdin 收到:
{ "hook_event_name": "AssistantRender", "message_id": "msg_...", "text_blocks": [{ "block_index": 0, "text": "<mermaid>...</mermaid>" }] }stdout 返回:
{ "continue": true, "hookSpecificOutput": { "hookEventName": "AssistantRender", "updatedBlocks": [{ "blockIndex": 0, "text": "ASCII 图" }] } }即可把该 text 块的终端显示替换为渲染结果。
配套实现:
renderCache模块、MessageRowrenderCacheVersion/ 重绘纪元重绘机制(解决 OffscreenFreeze 静态定格行的重印)、/clear缓存清理接线、全类型层注册(HOOK_EVENTS×4、zod Schema、SDK 生成类型)。考虑过的替代方案
补充信息
bun test全过;tsc --noEmit零错误;biome 通过/clear后缓存正确失效