Nested terminal cards
嵌套 terminal 卡片
A shell command dispatched through `run_code` carries the arguments and rendered output needed for a terminal card, but rejecting every block with `parentCallId` hides that presentation solely because the call is nested. The rejection also affects running prompts and selected-child Details.
English
Problem
A shell command dispatched through run_code carries the arguments and rendered output needed for a terminal card, but rejecting every block with parentCallId hides that presentation solely because the call is nested. The rejection also affects running prompts and selected-child Details.
Decision
terminalCardModel applies the same eligibility checks to root and PTC dispatch calls, without rejecting parentCallId. Supported running and settled bash, pwsh, and terminal_send calls use the existing terminal card. Background calls, tool errors, malformed inputs, missing call heads, and unsupported result content retain generic fallback. Persistent shells remain eligible while running and generic when settled; a nonzero process exit remains terminal result data rather than a tool error.
This partially supersedes only the terminal child-card prohibition in Client-derived tool presentation. That note remains active for Client presentation ownership and the diff/read/search/web child restrictions. No Host presenter, event, schema, metadata, call-tree, or model-context change is required. The metadata and execution-local value decisions in canonical tool output and PTC typed returns remain intact; metadata omission does not prohibit Client-derived terminal cards.
Shell output ending in a recognized spill-policy notice uses generic output: expandable in BashRow, raw fallback in Details. The notice can follow or replace the exit marker, so its absence at the end does not justify a successful terminal status. The browser-safe @deepseek-ai/dsh-spill-policy/notice entry owns the text convention: the producer calls formatSpillNotice(omitted, ref) and the Client calls hasSpillNotice(text). Both share delimiters, and omission validation reuses describeOmitted rather than duplicating its prose. The formatter preserves the persisted spelling byte-for-byte; existing Session result bytes stay untouched, with no Session format change or migration.
Alternatives considered
Keep the blanket nested-call rejection. Rejected because nesting does not remove the raw facts the terminal model already consumes. It hides usable shell output while the same call renders as a terminal at the root.
Enable every nested structured card. Rejected because other card models have independent metadata requirements and child restrictions. This fix changes only terminal eligibility.
Parse exit markers around spill suffixes. Rejected because truncation can remove the real status; conservative generic output avoids guessing success from an incomplete result.
Maintain a separate UI notice regex. Rejected because it duplicates the producer's text convention and can drift from persisted output. The shared browser-safe owner keeps formatting and recognition together without loading the Host plugin in the browser.
Consequences
Rows and Details share terminal derivation for nested calls without a second renderer or presentation hint. Generic fallback and settled-persistent behavior remain separate from terminal-card eligibility. The parent-child relationship still controls tree placement, not terminal rendering. Text recognition cannot authenticate output: a tool can print the same notice. A match selects conservative generic presentation, not proof of spill source or process status.
Verification
The terminal card specs cover root/child eligibility, running and settled Details, and fallback cases. The assembled PTC dispatch specs cover nested terminal rendering through the conversation tree. The notice specs pin the historical spelling with a literal fixture independent of the formatter. The spill-policy-to-UI specs exercise actual root and PTC spill production, unchanged full text and programmatic values, byte caps, notice-only output, and terminal fallback. Browser replay owns the visible nested-card change; nonterminal child behavior remains outside this fix.
中文
问题
经 run_code 分发的 shell 命令携带 terminal 卡片所需的参数与渲染输出,但对所有带有 parentCallId 的块一律拒绝,会仅因调用嵌套而隐藏这类展示。该拒绝也影响运行中的命令提示行与选中子调用的 Details。
决策
terminalCardModel 对根调用与 PTC dispatch 调用应用相同的适用检查,不因 parentCallId 拒绝调用。受支持的运行中与已完成的 bash、pwsh 和 terminal_send 调用使用现有 terminal 卡片。后台调用、工具错误、格式错误的输入、缺失的调用头和不受支持的结果内容保留通用回退。持久 shell 在运行中仍可使用 terminal,完成后使用通用展示;非零进程退出仍是 terminal 结果数据,而非工具错误。
本文仅部分取代 Client 派生工具展示中的 terminal 子调用卡片禁令。该文继续负责 Client 展示所有权及 diff/read/search/web 子调用限制。无需更改 Host 展示转换器、事件、schema、元数据、调用树或模型上下文。规范工具输出与 PTC 类型化返回值中的元数据和执行期值决策保持不变;省略元数据不禁止 Client 派生 terminal 卡片。
以已识别的 spill 策略提示结尾的 shell 输出使用通用展示:在 BashRow 中可展开,在 Details 中使用原始回退。提示可能位于退出标记之后或取代它,因此末尾缺少退出标记不能作为 terminal 成功状态的依据。浏览器安全入口 @deepseek-ai/dsh-spill-policy/notice 负责文本约定:生产方调用 formatSpillNotice(omitted, ref),Client 调用 hasSpillNotice(text)。两者共用分隔符,省略信息校验复用 describeOmitted,不复制其文案。格式化函数逐字节保留持久化拼写;现有 Session 结果字节保持不变,不更改 Session 格式,也不执行迁移。
考虑过的替代方案
保留对嵌套调用的一律拒绝。 不予采用,因为嵌套不会移除 terminal model 已消费的原始事实。这会隐藏可用的 shell 输出,而同一调用位于根时却可渲染为 terminal。
启用所有嵌套结构化卡片。 不予采用,因为其他 card model 有独立的元数据要求与子调用限制。本修复只改变 terminal 适用性。
解析 spill 后缀附近的退出标记。 不予采用,因为截断可能移除真实状态;保守的通用输出避免从不完整结果猜测成功。
维护独立的 UI 通知正则表达式。 不予采用,因为它复制生产方的文本约定,可能与持久化输出偏离。共享的浏览器安全模块统一负责格式化与识别,无需在浏览器中加载 Host 插件。
后果
行与 Details 对嵌套调用共享 terminal 派生,不增加第二个渲染器或展示提示字段。通用回退与已完成持久 shell 的行为仍独立于 terminal 卡片适用性。父子关系仍控制树中的位置,而非 terminal 渲染。文本识别无法认证输出来源:工具也能打印相同的提示。匹配结果只选择保守的通用展示,不能证明 spill 来源或进程状态。
验证
Terminal 卡片测试覆盖根/子调用适用性、运行中与已完成的 Details 以及回退情况。组装后的 PTC dispatch 测试覆盖经对话树渲染的嵌套 terminal。通知测试使用独立于格式化函数的字面量 fixture(测试前置数据)固定历史拼写。spill-policy 到 UI 的测试覆盖真实的根调用与 PTC spill 生成、保持不变的完整文本和程序化值、字节上限、仅含通知的输出以及 terminal 回退。浏览器回放负责验证可见的嵌套卡片变化;非 terminal 子调用行为不属于本修复。