Web UI drops steer entry and interjection chrome
Web UI 去掉 steer 入口与插话 chrome
Mid-turn steering is a host/agent-loop capability (`mode:'steer'`, a durable `user/message`). The Web product already locked the composer while a turn runs and never shipped a queue/steer menu, yet the client still threaded `'queue' | 'steer'` through the input machine, `conversation.send`, and locale keys, and rendered consumed steering as a badged 「插话」/「Interjection」 bubble. That left a half-built UI surface: an un
English
Problem
Mid-turn steering is a host/agent-loop capability (mode:'steer', a durable user/message). The Web product already locked the composer while a turn runs and never shipped a queue/steer menu, yet the client still threaded 'queue' | 'steer' through the input machine, conversation.send, and locale keys, and rendered consumed steering as a badged 「插话」/「Interjection」 bubble. That left a half-built UI surface: an unused submit mode, a product label for a gesture users cannot perform, and e2e goldens that pinned chrome the product does not own.
Decision
Keep host and runtime steering intact. Remove only the Web UI entry and chrome:
InputMachine/SessionInput/InputActions.submit/ hubdefaultSinkare queue-only; they always callsession.prompt(..., 'queue').ConversationService.send(text)drops its mode argument and always queues.- Durable steer content renders as a plain right-aligned bubble (no badge, no user IconActions) so external/host steers stay visible on replay.
- Delete
message.steeringlocale strings and the unused badge CSS. - The web steering e2e still POSTs
mode:'steer'over/api/session.promptand asserts durable + model-visible obedience; it no longer expects interjection chrome. Update web input machine note fact lines to match.
Alternatives considered
Delete host steering entirely. Out of scope; the user asked only for Web UI display and entry. Agent-loop drain, session events, and the wire mode remain load-bearing for ACP/TUI/automation.
Hide durable steer user/message content from the transcript. Would lie on replay when an external client steers; rejected in favor of a plain bubble.
Keep the mode parameter but only ever pass 'queue'. Leaves dead API surface and tests that invent 'steer' paths the composer cannot reach.
Consequences
- Superseded in part. Decision bullets 1 and 3 through 5 no longer describe master: composer steering shipped later, and the context-source and steer marks decision owns its caption. The current facts follow.
- Host steering ownership is unchanged: agent-loop drain, session events, and the wire mode remain load-bearing for ACP, automation, and non-Web clients.
ConversationService.send(text)still takes no mode and always queues; the composer's Steer gesture usessession.prompt(mode: 'steer')instead.- Durable steer
user/messagecontent still folds into the transcript, so an externally submitted steer stays truthful on replay. It now carries the interjection caption instead of rendering as a bare bubble. - Non-user next-step items (
agent.injectcontext: approval notices, task completion, attached snapshots) broadcast with thecontextplacement and never render as pending steering bubbles; they stay invisible until claimed as durableuser/messagecontext cards.
Testing
packages/client/ui-conversationunit/jsdom coverage: input machine enter/sink, ConversationService routing, the MessageItem steering arm, InputBar submit.apps/web/tests/steering.e2e.tskeyless replay plus its goldens, which pin the caption.packages/host/apiproxysession/queueprojection test asserts user-origin next-step items staysteeringwhile plugin-origin items land ascontext.
中文
问题
中途 steering 是 host/agent-loop 能力(mode:'steer'、持久 user/message)。Web 产品已在 turn 运行中锁定 composer,且从未交付排队/steer 菜单,但客户端仍把 'queue' | 'steer' 穿进 input machine、conversation.send 与 locale 键,并把已消费的 steering 渲染成带「插话」/「Interjection」徽章的气泡。这留下半成品 UI:用不到的提交 mode、用户做不到的手势却有产品文案,以及把产品并不拥有的 chrome 钉死在 e2e golden 上。
决策
保留 host 与 runtime 的 steering。只去掉 Web UI 入口与 chrome:
InputMachine/SessionInput/InputActions.submit/hubdefaultSink仅 queue;始终调用session.prompt(..., 'queue')。ConversationService.send(text)去掉 mode 参数,始终排队。- 持久 steer 内容渲染为右对齐普通气泡(无徽章、无用户 IconActions),以便外部/host steer 在回放时仍可见。
- 删除
message.steeringlocale 字符串与未使用的徽章 CSS。 - web steering e2e 仍通过
/api/session.promptPOSTmode:'steer',并断言持久化与模型可见服从;不再期望插话 chrome。同步更新 web input machine note 中的事实行。
曾考虑的替代方案
整段删除 host steering。 超出范围;用户只要求清 Web UI 展示与入口。agent-loop 排空、session 事件与线缆 mode 对 ACP/TUI/自动化仍是承重能力。
在 transcript 中隐藏持久 steer user/message 内容。 外部客户端 steer 时回放会失真,因此改为普通气泡。
保留 mode 参数但永远只传 'queue'。 留下死 API 面与只会虚构 composer 到不了的 'steer' 路径的测试。
后果
- 部分被取代。 决策中的第 1 条和第 3 至 5 条已经不再描述 master:composer steering 后来已经交付,上下文来源与 steer 标识决策负责定义其标注。下面列出当前事实。
- host 侧 steering 的归属未变:agent-loop 排空、session 事件与线缆 mode 对 ACP、自动化和非 Web 客户端仍然必要。
ConversationService.send(text)仍然不接 mode,始终排队;composer 的 Steer 手势改走session.prompt(mode: 'steer')。- 持久 steer
user/message内容仍然折叠进 transcript,因此外部提交的 steer 会如实出现在回放中。它现在带有插话标注,而不是无标识气泡。 - 非用户来源的 next-step 项(
agent.inject上下文:审批通知、任务完成、附加快照)以contextplacement 广播,绝不渲染为待处理 steering 气泡;领取为持久user/messagecontext card 前保持不可见。
测试
packages/client/ui-conversationunit/jsdom 覆盖:input machine enter/sink、ConversationService 路由、MessageItem steering 分支、InputBar submit。apps/web/tests/steering.e2e.ts无密钥回放及其黄金基线,后者会检查插话标注。packages/host/apiproxy的session/queue投影测试断言用户来源的 next-step 项保持steering,而插件来源的项落入context。