The remote side re-merged master (i18n workflow round) and restored the
pre-architecture ModelSelector package with the Session-side state
machine. Reconciliation keeps that merge's master content and reasserts
this branch's model architecture: business state lives in ui-model's
shared per-session directory (ctx.models), not on Session/
ConversationSnapshot; the input.model seat entry and the /model popup
stay the two same-source entries; the history modelTarget piggyback stays
removed. The ui-model-selector package, its roster row, and its allowlist
entry go; the smoke-real roster names ui-model.
The seat's dropdown follows figma 496:26454: the root pane is the
Model / Effort cell pair (14/22 label, value in the tertiary tone, right
chevron), each drilling into its own list — the provider-grouped model
list over the shared directory, and the High/Max effort levels. The
trigger (313:14108) shows both values: model name plus effort in the
caption tone. Effort is a client-local display echo on the shared
directory state for now — the design pairs the two as one selection, but
no wire carries a per-session effort override yet (the deepseek adapter's
reasoningEffort is deployment config); the directory state documents that
boundary, so wiring it later is a submit-path change, not a UI one.
Escape backs out of a drilled pane before closing.
The todo row and the todo plan strip were registered without a contract in this
package's README, which still described only the bash sample. Both sides now
carry the two registrations: TodoRow's `todo_write` toolview key with its
attempted-write summary and non-ok dot semantics, and TodoDock's
`conversation.input.dock` entry at `order: -1` with the dock-owned selection,
empty-hidden panel, collapse behavior, and the composer-takeover consequence
that hides the whole dock. README.i18n.yaml re-recorded.
- ui-model README pair (short-form Model Experience + audited registry
entry + Known Limitations), recorded in the pairing manifest
- llm-deepseek README pair rebased onto master's version with this PR's
real deltas (friendly names, 256k, selector consumers) on both sides;
runtime/ui-conversation READMEs revert to master (their sections
documented the removed Session state machine and control slot)
- config catalog regenerated for the ui-model roster row
- ModelDirectory.store JSDoc; ui-model files join the TODO(gui) coverage
deferral lane beside their ui-command/ui-slash siblings
Three ds-review-bot round-8 findings on the todo display surfaces.
TodoPanel took the whole `useSession` hook and cast the snapshot to reach
`todos`, which put slot plumbing and an unchecked cast inside the presentation
component. The panel now takes `todos: readonly TodoItem[]`; TodoDock does the
selecting, matching the QueueDock posture the dock slot already establishes.
The todo row carried `onClick` with no keyboard route, so its details panel was
mouse-only. It now takes ToolRow's route verbatim: `role="button"`,
`tabIndex={0}`, and an Enter/Space handler that claims the event. The row stays
a `<div>` because a `<button>` flattens its inline spans.
`session.history`'s tail-only `todos` field was documented at the TS signature
but not in the apiproxy README pair, and the Agent Note claimed the feature
added no new wire vocabulary. Both README sides now state the tail/omission
semantics (an omitted field on a tail response is the empty plan, not unchanged
state), and the note records the one added field instead of denying it.
Tests: TodoPanel specs render the plain list; new TodoDock specs cover selection,
live follow, rollback-to-empty, and the registration shape; a new row spec pins
Enter/Space activation and non-activating keys.
- unused SessionId import and an unnecessary assertion in ui-model
- two over-length fake-api lines split
- trigger chrome calibrated against figma 313:14108's ToggleButton
(13/20 medium secondary at 28px; chevron in label-caption = the mock's
#ADB2B8; the mock's leading icon is invisible there, so the seat has
none, and its 'High' effort text has no wire concept to render)
An omitted `todos` on a tail request was treated as "no projection carried"
and preserved the prior value. Every installWindow caller is a tail request
(doOpen, its gap re-pull, repairGap; loadOlder prepends without it), which the
host answers with the full-log projection or omits only when the log holds no
todo/write — so the field's absence is the authoritative empty list. A live
write whose host crashed before persisting therefore left the rolled-back plan
on screen indefinitely; the assignment now clears it on the next open or
resync. Widened the parameter to an explicit `| undefined` so the two meanings
cannot be conflated again, and updated the JSDoc at both declaring seams plus
the bilingual README/note pair.
The original ModelSelector returns as the conversation.input.model seat
occupant (the named single slot the composer bar renders before its primary
button), alongside — not instead of — the /model popup. Both entries are
projections of ONE per-session directory owned by the new ModelService
(ctx.models): the same session.models load, the same selectModel submit,
the same host-reported current — a switch in either entry is what the
other shows next, asserted both ways in the plugin spec.
Trigger chrome follows figma 313:14108's composer ToggleButton (13/20
medium secondary label, 4px gap, caption chevron, 28px chip height
matching the sibling Plan/Read-only selects); the menu keeps the original
upward provider-grouped single-select with keyboard navigation. The
seat honors the owner locked bit as its disabled state.
The automation-only ACP bridge deliberately omits todo presentation (its
edge test asserts plan updates are omitted; the todo-write tool note records
the mapping's retirement). Chinese counterpart updated, pair re-recorded;
the TodoPanel header comment drops the same claim.
- host trio kept on the merged api-proxy: session.models (provider-grouped
advisory directory), session.selectModel (validated provider, advisory
model), installAgentLlmTarget threaded through create/resume/ensureSession;
the gateway declares the llm inject it reads
- history no longer piggybacks modelTarget: the current target travels on
session.models alone (the /model popup is the sole consumer)
- new @deepseek-ai/dsh-client-ui-model plugin: /model popupSelect over the
wire — options load the directory (group label in the detail column,
provider-local failures listed inline), onSelect routes selectModel;
failures ride the popup shell's error/retry surface
- ModelSelector package, conversation.composer.control slot, and the
Session-side modelSelection state machine are removed: model selection
belongs to the /model popup; the named conversation.input.model seat
stays empty until a control-seat entry is designed for it
The master merge committed the i18n.yaml with unresolved conflict hunks
(carried over from the adapt branch's own master merge); re-recorded via
verify-translation-pairing --write. 518 pairs consistent.
The session/queued frame no longer carries steering — AgentMessage no longer
has the field. The client derives it from the same ordered turn boundaries the
host saw (a frame arriving while a turn is open joined the steering FIFO).
- TodoPanel mounts through a 'conversation.input.dock' list entry
(todoDockEntry, QueueDock posture, order -1 above the queue rows) instead
of a ConversationRoot hardcode; the inner component is unchanged and takes
useSession from the dock entry's standard kit.
- The verify-todo-display.mjs chromium probe is replaced by an assembled
keyless snapshot (apps/web/tests/todo-display.snapshot.ts, the
code-mode-fixture idiom) pinning the TodoRow summary/state, the dock
panel content, and the collapse round-trip over built bundles.
- Fake snapshots across specs gain the todos field; bilingual note/READMEs
updated for the dock mount and the snapshot.