dsh-input-queue 使用与实现说明
August 14, 2026 · View on GitHub
1. 目标
让"在 agent 忙碌时排队输入补充信息"变得可见、可管理、可发现:
- 排队消息一目了然(顺序、数量、内容);
- 每条可编辑 / 删除 / 立即插话 / 调整顺序;
- 忙碌且队列为空时给出可发现的提示;
- 设置(General)提供「队列面板默认展开」开关(
localStorage持久化,客户端共享 store)。
2. 与 DSH 原生排队机制的关系
DSH 0.1.0-rc.6 已经内置了基础排队/steering 管线,本插件复用而非重造:
| 原生能力 | 位置 |
|---|---|
| 忙碌时回车默认排队 | busyEnter: 'queue'(ui-conversation 会话设置,`BusyEnterBehavior = 'queue' |
| 瞬态收件箱快照 | ConversationSnapshot.queue(session/queue mux frame,QueuedInboxItem[]) |
| 队列操作 | `updateQueue(itemId, { kind: 'edit' |
| 队列发送 | conversation.send(text)(queued turn)、`prompt(content, 'queue' |
| 极小队列条 | 内置 QueueDock(conversation.input.dock id queue,order 20,多条折叠为计数头) |
本插件在 conversation.input.dock 以 id: input-queue、order: 30 注册一个富面板,与内置 QueueDock 并存(排在它之后),不替换任何内置 seat。
3. 组件与数据流
ConversationSnapshot.queue ──(useSession 选择器)──▶ QueuePanel
└─ filter placement==='queued' ──▶ queued: QueuedMessage[]
QueuePanel 操作:
edit ─▶ updateQueue(id, { kind:'edit', content:[{type:'text',text}] })
remove ─▶ updateQueue(id, { kind:'remove' })
steer ─▶ updateQueue(id, { kind:'steer' }) (仅 running 时可用)
reorder─▶ for each queued: updateQueue(id,{kind:'remove'})
for each reordered: conversation.send(rowText)
3.1 注册(src/client/index.tsx)
export const inject = ['slots', 'conversation', 'sessions']
export function apply(ctx) {
ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
name: 'conversation.input.dock',
id: 'input-queue',
order: 30,
inject: (sessionId) => {
const actx = ctx.sessions.scope(sessionId)
const conversation = actx.get('conversation')
return {
updateQueue: (id, a) => conversation.updateQueue(id, a),
send: (text) => conversation.send(text),
notify: (level, text) => conversation.input.for(actx).notify(level, text),
}
},
}, QueuePanel))
}
ctx.sessions.scope(sessionId)取得会话作用域 Context,再actx.get('conversation')拿会话作用域的IConversation。- 面板通过
useSession(会话标准 kit)读快照,通过注入面拿动词,不 import 任何 conversation 实现。
3.2 面板状态(src/client/QueuePanel.tsx)
queued:inbox.filter(row => row.placement === 'queued')。running:useSession(s => s.running)—— steer 按钮仅在 running 时可用。queueMutable:useSession(s => s.subagent === null)—— 子代理会话的队列不可改(与内置 QueueDock 同判断)。busyId:单飞互斥,任何动作进行中禁用其余动作。revealed:长文本展开/收起(预览截断PREVIEW_LENGTH = 120)。
3.3 顺序调整(src/client/queue-model.ts)
FIFO 队列没有原生 reorder 动词,采用"移除后重排":
- 校验所有排队消息
text !== null(非文本消息不可重排,避免丢附件)。 moveItem(queued, from, to)计算目标顺序(纯函数)。- 逐条
remove原队列,再按新顺序send(rowText)(row.text ?? row.preview)。
纯函数 moveItem / rowText / reorderableRows 独立于 React,供 scripts/verify.mjs 离线断言。
4. 已知限制与风险
- 非文本消息不可重排:
QueuedMessage.text === null(含图片/附件块)时,上移/下移会提示失败;编辑按钮同样禁用。 - 重排竞争:
remove+send之间,若 host 恰好正把某条消息从queued认领为steering/context,可能产生一条重复消息,删除即可。概率低、无数据丢失。 - 瞬态队列:
session/queue不落盘,刷新页面后排队消息丢失(DSH 原生行为)。 - 顺序:
order: 30,排在内置QueueDock(20)之后;如想隐藏内置QueueDock,可在 profile 组合里移除conversation-queue-dock行(超出本插件范围)。
5. 验证
pnpm typecheck # host + client 两个 program
pnpm build # tsc → tsc → tsdown
pnpm verify # 纯逻辑 + 产物/补丁形态冒烟(离线)
更完整的验证金字塔(独立 profile、真实 GUI)参见 agent-teams 的 docs/verification-guide.md;本插件为纯 client 面板,离线验证覆盖纯逻辑与产物形态,GUI 行为需在独立 web profile 里人工确认(安装后刷新页面,忙碌时回车排队,观察面板逐条操作)。