refactor(acp): reduce bridge to automation protocol
This commit is contained in:
@@ -16,7 +16,7 @@ The model-facing request vocabulary is deliberately aligned with the product-res
|
||||
|
||||
Providers return `{ answers: [{ id, selected, custom? }] }`. `selected` is always an array of selected option labels, so single-select and `multi_select` answers share one result shape. `custom` carries a free-text "Other" answer; optionless questions collect `custom` directly. When `custom` is present, it overrides any selected choices and `selected` is empty. A provider that supports partial completion represents a deliberately skipped item with the existing `{ id, selected: [] }` shape, preserving the other answers without extending the tool result vocabulary.
|
||||
|
||||
`UserInteractionError` extends `HarnessError`, so failures such as `NO_PROVIDER`, `ASK_ABORTED`, ACP cancellation, or missing session routing survive `ctx.tools.execute()` as machine-routable `{ name, code }` tool errors. This matches the structured-error taxonomy and lets the model or a wrapping plugin distinguish "user cancelled" from a generic thrown exception.
|
||||
`UserInteractionError` extends `HarnessError`, so failures such as `NO_PROVIDER`, `ASK_ABORTED`, or missing request ownership survive `ctx.tools.execute()` as machine-routable `{ name, code }` tool errors. This matches the structured-error taxonomy and lets the model or a wrapping plugin distinguish "user cancelled" from a generic thrown exception.
|
||||
|
||||
## UI mappings
|
||||
|
||||
@@ -26,9 +26,7 @@ The Web composer shows one question at a time while retaining every request in t
|
||||
|
||||
`dsh-tui` renders each question as a keyboard overlay, shows option descriptions, supports single- and multi-select choices plus free-form custom answers, and rejects pending questions on abort, provider disposal, or terminal shutdown. Batched and simultaneous requests are queued so one overlay owns keyboard focus at a time.
|
||||
|
||||
`dsh-acp` provides the same seam for ACP sessions. It resolves the calling `Agent` through `ownedRecord`, requiring the forward session-map record at `agent.session.id` to own that exact agent object, and calls ACP `unstable_createElicitation` with a session-scoped form for each question. Single-select options become a `choice` string enum; `multi_select` options become a `choice` array enum; optionless questions use a required `custom` text field. If the client returns both `choice` and non-empty `custom`, the custom answer wins. ACP `decline`/`cancel`, a missing answer, a missing session, and a client without elicitation support all become structured `UserInteractionError`s.
|
||||
|
||||
The ACP mapping deliberately uses elicitation, not `session/request_permission`. `request_permission` is still reserved for the separate permission gate: it is a yes/no-or-policy authorization protocol around tool execution. `ask_user_question` is a general information-gathering tool with optional free-form answers, so ACP form elicitation is the closer protocol fit. The bridge's session routing is shared with the future permission gate, but the user intent is different.
|
||||
The Web host exposes the same seam through its selected question provider. Stable request ids, whole-request cancellation, owner abort, per-item skips, and structured batch settlement keep browser interaction behind the provider-neutral service.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
@@ -36,18 +34,16 @@ The ACP mapping deliberately uses elicitation, not `session/request_permission`.
|
||||
|
||||
**Core-owned ask-user packages.** The first implementation split the seam and the model-facing tool across `packages/core` and `packages/ui`, but both names describe one UI-backed human-interaction affordance. The seam remains provider-neutral, but it is not providerless core infrastructure like sessions, tools, or the agent registry. Keeping `dsh-user-interaction` and `dsh-tool-ask-user` together under `packages/ui` makes the package map match the product boundary: apps and bridges provide the human-answer provider, and the stdio app opts into the model-facing tool.
|
||||
|
||||
**ACP `session/request_permission`.** Permission requests are authorization around tool execution; `ask_user_question` is information gathering with optional free-form answers. Using permission for general questions would collapse two different product concepts and make the future permission gate harder to reason about.
|
||||
**Use a permission request for general questions.** Permission requests authorize tool execution; `ask_user_question` gathers information with optional free-form answers. Reusing the permission channel would collapse two different product concepts.
|
||||
|
||||
**A loop-level pause primitive.** The agent loop already knows how to await a tool call and resume from a tool result. Adding a new loop special case would duplicate that async shape and make every loop implementation learn about a UI concern.
|
||||
|
||||
## Consequences
|
||||
|
||||
ACP elicitation is currently marked unstable in the SDK. The fallback is still structured: if a client does not implement it, the tool returns `ASK_FAILED` rather than hanging. A later ACP stabilization may rename or reshape the method; that migration should stay inside `dsh-acp` because the core `ctx.userInteraction` vocabulary is provider-neutral.
|
||||
|
||||
The feature gives the model a powerful pause primitive, so prompt guidance matters. The tool description tells the model to ask concise questions and use options when possible. Product policy can later wrap `tools/execute` to restrict when the tool is allowed, but the loop should not special-case it.
|
||||
|
||||
`dsh-user-interaction` and `dsh-tool-ask-user` both live in `packages/ui` because they form one product-facing human-interaction capability. `agent-core` does not load either the tool or a provider. `dsh-tui-demo` opts into the seam, TUI provider, and model-facing tool. `dsh web` boots the seam/provider in the host runtime and exposes the tool through the selected Web question plugin. `acp-agent` keeps only the `userInteraction` seam/provider by default: ACP elicitation support is still client-dependent, so an ACP leaf must opt into the model-facing tool deliberately once its client can complete elicitation requests.
|
||||
`dsh-user-interaction` and `dsh-tool-ask-user` both live in `packages/ui` because they form one product-facing human-interaction capability. `agent-core` does not load either the tool or a provider. `dsh-tui-demo` opts into the seam, TUI provider, and model-facing tool. `dsh web` boots the seam/provider in the host runtime and exposes the tool through the selected Web question plugin. The ACP automation app mounts neither the seam nor the tool.
|
||||
|
||||
## Testing
|
||||
|
||||
Unit coverage pins provider registration/disposal, duplicate-provider rejection, abort-before-provider, empty-question rejection, structured tool errors through `ctx.tools.execute()`, batched answers, multi-select answers, custom answers, explicit per-item skips, and the model schema including the removal of `value`, `recommended`, `allow_custom`, and `desc`. TUI tests cover option descriptions, queued requests, shutdown/abort cleanup, optionless free-form input, invalid choices, duplicate multi-select selections, and batched question flows. ACP bridge tests drive a real in-memory ACP connection with the real `ask_user_question` tool and verify selected-option, custom-overrides-choice, multi-select, and optionless free-form elicitation paths continue the agent loop. Web tests pin stable-id replay, response validation, first-wins settlement, duplicate and late responses, whole-request cancellation versus owner abort, single-select advance, IME-safe Enter submission, per-item skip preservation, composer takeover, structured batch submission, and restoration of the normal composer.
|
||||
Unit coverage pins provider registration/disposal, duplicate-provider rejection, abort-before-provider, empty-question rejection, structured tool errors through `ctx.tools.execute()`, batched answers, multi-select answers, custom answers, explicit per-item skips, and the model schema including the removal of `value`, `recommended`, `allow_custom`, and `desc`. TUI tests cover option descriptions, queued requests, shutdown/abort cleanup, optionless free-form input, invalid choices, duplicate multi-select selections, and batched question flows. Web tests pin stable-id replay, response validation, first-wins settlement, duplicate and late responses, whole-request cancellation versus owner abort, single-select advance, IME-safe Enter submission, per-item skip preservation, composer takeover, structured batch submission, and restoration of the normal composer.
|
||||
|
||||
Reference in New Issue
Block a user