Master arrivals re-grafted onto the plan-mode surfaces: - Package invariant runtime contracts: every package owns ./invariant — added the dsh-mode companion (mode/set payload validation: non-empty trimmed name), its unit spec, exports/peer wiring, and the mode group in the tsconfig invariant paths glob. - Interception waterfalls gained an AbortSignal parameter (agent/prompt-submit, agent/turn-continuation) and ToolExecutionInput.signal became required: listeners and test dispatch updated; the exit tool now forwards exec.signal unconditionally. - TUI question dialog: master's position/unanswered header layout kept, the plan-review detail block re-grafted between question and answers. - LSP capability family arrived: lsp tool joins the catalog pin, dsh-lsp examples deps beside dsh-mode; lsp-definition snapshot refreshed with the mode command and modes advertisement. - docs/architecture.md and packages/README.md regenerated tables re-grafted (ctx.modes row, mode package row) and condensed within word ceilings. - examples/acp-agent cordis.yml: kept the deployment-owned plan instructions (incl. the conversational-agreement sentence) beside master's routed-model compaction comment; snapshot expected outputs refreshed keyless and noise fixtures restored.
@deepseek-ai/dsh-user-interaction
Abstract user-interaction seam. It owns ctx.userInteraction, the service a model-facing tool or permission plugin uses when it needs to pause work and ask the human for a decision.
Service: UserInteractionService (ctx key: userInteraction)
Public API
ctx.userInteraction.registerProvider(provider): () => voidRegister the UI-side provider. Only one provider may be active in a context; disposal unregisters it.ctx.userInteraction.ask(request): Promise<AskUserQuestionAnswer>Ask the active provider and wait for the answer.
Key Types
AskUserQuestionRequest—{ questions: [{ id, question, detail?, header?, options?, multiSelect? }], agent?, signal? };detailsupplies supporting text that providers render with the question without turning it into an option label.AskUserQuestionOption—{ label, description? }.AskUserQuestionAnswer—{ answers: [{ id, selected, custom? }] }.UserInteractionProvider— UI implementation withask(request).UserInteractionError—HarnessErrorsubclass with codes such asEMPTY_QUESTIONS,NO_PROVIDER,DUPLICATE_PROVIDER, andASK_ABORTED.
When an answer includes custom, selected is empty; custom text is an override rather than a supplement to selected choices.
Role
This is the interface package. Model-facing consumers such as @deepseek-ai/dsh-tool-ask-user depend on this seam; the interactive dsh-tui and structured dsh-acp front doors provide the provider. The loop stays unchanged: a tool call awaits a promise, and the tool result resumes the normal agent loop.
Model Experience
Indirectly, through dsh-tool-ask-user, which retains a successful provider answer as compact JSON or one of these failures: Error: ask_user_question was aborted before the user answered, Error: ask_user_question requires at least one question, Error: no user-interaction provider is registered, or Error: <message>. Waiting for the human adds no tokens.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
- One provider per context — there is no routing or fan-out to multiple UIs; a second registration throws
DUPLICATE_PROVIDER, and with none registeredask()throwsNO_PROVIDERrather than degrading. - The vocabulary is the question-form shape only — selectable options plus optional custom text; richer interaction shapes (file pickers, diff-preview confirmations) have no seam vocabulary yet.