refactor(client): command decorations replace the hostBacked contribution mode
A popup on a host command is not a second command — it is what that command's BARE invocation does on this client. CommandContribution loses hostBacked (contributions are pure client commands again; a host-name collision fails loud, unchanged for /model), and the contract gains CommandDecoration + command.decorate(): key = the HOST command name, no catalog row, no claim participation. Dispatch consults decorations only on the bare paths (menu pick / bare enter) after the host row resolves; space and argued enter never see them — the two edges hostBacked had to guard explicitly hold by construction in the decoration model. A decorated name with no host row in the session's directory never fires (a decoration cannot manufacture a command). ui-permission switches register→decorate with zero behavior change (options still read the permissions projection; a pick still submits '/permission <preset>'). Specs rewrite to the decoration semantics: no catalog row, bare-enter popup vs argued-enter host claim, space host claim, no-host-row miss, unavailable fall-through, duplicate fail-loud.
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* Permission preset plugin, browser half — the `/permission` popupSelect
|
||||
* (the bare-invocation picker the user asked for: one flat list of presets,
|
||||
* current value marked active, a pick executes the switch). The contribution
|
||||
* is hostBacked: the host's `/permission` command owns the catalog row, the
|
||||
* argued path (`/permission <preset>` still switches directly), and the
|
||||
* lifecycle logging — this entry only opens the picker on a bare pick/enter.
|
||||
* Options and the active mark read the session's `permissions` projection
|
||||
* (the same host-computed select the composer chip renders); a pick submits
|
||||
* the `/permission <preset>` command line, so both surfaces write through
|
||||
* one path and the pushed projection frame is the one confirmation.
|
||||
* Permission preset plugin, browser half — a popupSelect DECORATION hung on
|
||||
* the host `/permission` command: one flat list of presets, current value
|
||||
* marked active, a pick executes the switch. The decoration owns only the
|
||||
* bare invocation; the host command keeps its catalog row, the argued path
|
||||
* (`/permission <preset>` still switches directly), and the lifecycle
|
||||
* logging. Options and the active mark read the session's `permissions`
|
||||
* projection (the same host-computed select the composer chip renders); a
|
||||
* pick submits the `/permission <preset>` command line, so both surfaces
|
||||
* write through one path and the pushed projection frame is the one
|
||||
* confirmation.
|
||||
*/
|
||||
import type { ClientContext, SessionFace } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { CommandServiceContract, SelectOption } from '@deepseek-ai/dsh-client-ui-command/client'
|
||||
@@ -45,10 +45,8 @@ export function apply(ctx: ClientContext): void {
|
||||
const sessions = ctx.sessions
|
||||
const sessionFor = (session: ClientSessionContext): SessionFace | undefined =>
|
||||
sessions.binding(session.sessionId)?.session
|
||||
ctx.effect(() => command.register({
|
||||
ctx.effect(() => command.decorate({
|
||||
name: 'permission',
|
||||
description: 'Switch the permission preset (sandbox mode + approval policy)',
|
||||
hostBacked: true,
|
||||
// The picker exists exactly while the projection does: a permission-less
|
||||
// host serves no key and the bare invocation falls through to the host
|
||||
// command (which is absent too — the line simply misses).
|
||||
@@ -68,5 +66,5 @@ export function apply(ctx: ClientContext): void {
|
||||
if (!result.value.matched) throw new Error('the host offers no /permission command')
|
||||
},
|
||||
},
|
||||
}), 'ui-permission: /permission contribution')
|
||||
}), 'ui-permission: /permission decoration')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user