Files
deepseek-harness/packages/client/ui-input-trigger/README.zh.md
T
creatixchu 8d9fee19f9 feat(commands): route composer image attachments through slash commands
A claimed slash command consumed only the text half of the composer
submission: /goal with reference images executed, cleared the draft, and
silently stranded the images in the rail. Model-visible attachment intent
had no route through the command plane.

The submission envelope is now modeled end to end. CommandDefinition
input.images declares acceptance; the declaration rides the descriptor to
every client, onto the minted CommandClaim, and into the input machine's
claim snapshot. commands.execute carries the submission's base64 images
and enforces the declaration in the executor: non-declaring commands, a
missing attachment store, and exceeded batch limits settle as logged
error results before the handler runs. Admission reuses the attachment
package's new admitEncodedImages, extracted from api-proxy's prompt path
so both wire endpoints share one limits/validation/commit sequence.

Producers own model visibility: /goal submits one user followup (image
blocks + a fixed reference line) after a successful create/edit so goal
rounds read the images from session history; /plan folds them into its
steered message. Grammar misfits (/goal pause, bare /plan, /plan off)
return direct errors and the composer keeps the images.

On the client, enter adjudication carries a SubmitEnvelope and every
command route that cannot consume images throws a localized refusal that
renders as one composer notice with draft and images retained; the
claimed pre-gate applies the same copy. An accepting claim serializes the
draft images, forwards them to commands.execute, and clears plus releases
them only on a success outcome.

The assembled web test roster gains the ui-input-trigger and ui-commands
plugins, mirroring the shipped composition, so slash submissions exercise
the command plane; a new keyless snapshot pins the refusal banner and the
accepting /goal flow over the built client graph.
2026-08-17 18:57:55 +08:00

4.4 KiB
Raw Blame History

@deepseek-ai/dsh-client-ui-input-trigger

English | 中文

输入触发流水线插件:光标处的 /@ 检测(词边界 + guard tier 规则)、分组候选菜单,以及把 pick 路由到已注册 source。ctx.inputTriggers 拥有 source roster,并按会话 scopesessionOf)各解析一个 InputTriggerController;对话接线层在 controller 上驱动 trackarbitrateonSpaceadjudicate。同一个 controller 还暴露 toggleSource,供 chrome launcher 在一段合成 selection span 上只打开一个已注册 source;所得候选仍走通常的菜单、键盘仲裁、pick callback 与 scoped 输入改写。source 每次调用收到一个 ClientSessionContext 投影——会话始终由 agent(智能体)支撑,因此投影只含会话身份。source 在它能触达的每个会话 controller 中都会被预热:scope 创建时 roster 中已有的 source 会在 controller 构造期间预热,晚于此注册的 source 由注册动作本身预热进每个仍存续的 controller。lexicon 名录在预热后仍会变化的 source 实现 subscribeLexicon(session, listener)controller 每收到通知就重拉,并把聚合结果经其 lexicon 快照 store 发布。流水线与命令无关:空格/回车裁决按注册序轮询可选的 matchSpacematchEnter 钩子,第一个非 undefined 的应答胜出。回车裁决还携带 SubmitEnvelope(composer 的图片附件数量),使 source 能拒绝它无法整体消费的提交;命令接受 composer 图片时,CommandClaim 声明 images: true,其 submit 随之以第三个参数收到序列化后的图片载荷。

分层:src/core/ 是纯内核——detectTriggermenuReduceseedGroupsMENU_CLOSEDexactMatch,零 ReactDOMcordissrc/client/service.ts 是壳层,把内核接到菜单快照 store、逐 hit 候选拉取(以 generation 把关、后继请求经 AbortSignal 取代旧请求、失败的 source 静默丢弃并留一条 console 记录)和三条 pick 路径上。src/types.ts 与两个 contract.ts 文件是冻结的跨包约定;变更需经主线程仲裁。

MenuView 把菜单 store 渲染进 conversation.input.overlay slot(列表类,会话 scope),菜单关闭期间渲染 null。键入式 trigger 会 seed 为该 trigger 注册的所有 source;程序化 launcher 只 seed 所请求的 source,并在菜单关闭或重新开始键入式 tracking 前,通过 controller 的 launcher 快照 store 发布该 source 名称。分组按可选的 InputTriggerSource.order 排序(越小越靠前,默认 0,同值保持注册序),组标题行经 inputTriggers.menu locale 命名空间本地化(未知 source 显示其原名);列表高度受限于 composer 上方的可用空间,指针落在菜单与所在 composer 卡片之外即关闭菜单。该 slot 由 ui-conversation 的组合器条目拥有(锚点、children 声明、生命周期);其 SlotMap 类型合并放在本包的 src/client/slots.ts,因为依赖方向(ui-conversation → ui-input-trigger)不允许反向的类型导入。combobox 模式:焦点始终留在 textarea,行在 mousedown 时完成 pick,高亮由 aria-activedescendant 承载。

/client 导出接口是插件主体(applyinject)、InputTriggerServiceMenuViewInjected 与约定类型。MenuView 本身是内部实现——slot 注册以闭包持有它。

模型体验

无。触发流水线只是浏览器呈现——pick 产出 CommandClaimReferenceInsert 数据,其模型可见后果(宿主命令执行;插入的引用文本随普通提示词发送)由负责消费这些数据的宿主包与输入状态机包负责。

KV Cache 影响

无;该包既不组装也不发送提供方请求。

已知限制与暂缓事项

  • 只有全局 source 层:会话 scope 的 source 注册(逐会话遮蔽、类 ScopedLayers 机制)已有设计但未启用;台账记录着触发条件(出现真实的逐会话 source 需求)。
  • InputTriggerCandidate.icon 以文本渲染:MenuView 把该字符串原样放进图标位;与设计系统图标枚举(iconFile 五变体家族)的接入将在该枚举交付后完成。
  • overlay 的 SlotMap 合并归属与 slot 所有权分离:唯一的 conversation.input.overlay 合并放在本包,而 ui-conversation 负责其锚点、children 声明和生命周期,因为依赖方向是 ui-conversation → ui-input-trigger。