Keep both Remote contributions master and this branch add: the mount loop
now carries commandsRemote, goalsRemote, pluginInventoryRemote, and
messageFeedbackRemote, with both new tsconfig references retained.
The step and the Models page both asked one question of a join that
describes every provider: is deepseek-official's credential stored? A user
who configured some other route was taken over on every blank session, and
the DeepSeek setup card opened over them on every visit to Models with a
Cancel that could not close it — while clearing the add card's draft,
because it shared the row-editor close handler.
providerUsable(row) now answers what both surfaces need: the route is
registered and whatever credential its profile names is stored. Readiness
(renamed onboardingReadiness) ends on any usable row, needsSetup takes the
same fact, and each card kind owns its own close handler.
Fixes#2325
Adapt to two contract changes master introduced:
- The generated Remote face now wraps every business result in
RemoteResult, folding carrier failures into an ok:false branch instead
of rejecting. The controller reads that envelope at its three call
sites and maps a carrier failure onto the same settled shape the
controls already render; three specs cover the new branch.
- Client packages split their tsconfig into host and client halves, and
the host aggregate now compiles any test not named *.client.spec.*.
Rename this package's specs to the client convention and drop the
../connection project reference, which pointed at a solution file that
no longer carries the client sources.
Keep master's mount loop with its rollback-on-failure in api-remotes and
add messageFeedbackRemote to it.
A test file under packages/client now says which face it covers:
`*.client.spec.{ts,tsx}` and its `*.client.{ts,tsx}` helpers belong to the
Client aggregate, `*.host.spec.ts` to the host aggregate. The carrier's four
node-half specs take the Host suffix.
The two suffixes are mutually exclusive, so each aggregate excludes the
other's and both keep one broad test glob: `exclude` wins over `include`, and
`packages/client/**` no longer has to be excluded wholesale from the host
program with per-file `files` entries carved back out of it. A Host-face spec
that reaches only Host source therefore needs no cross-face project
reference, which the split-project rule rejects.
vitest still discovers every file through `**/*.spec.{ts,tsx}`.
The Gateway and the carrier each compiled both halves under one tsconfig, so
the Host aggregate built their browser faces — including the face that owns
`ctx.remote`, the most likely future consumer of a generated `/remote`
contribution. Both packages now expose a host and a client face, and each
aggregate references only its own; three modules the halves share appear in
both file lists, as api/remotes already does.
The two apps/web specs in the Host aggregate restate the conversation engine's
Context key format instead of importing the Client runtime for it. A drift
makes the key miss its rendered node, so the assertion fails loudly.
The Host aggregate now reaches one Client project, the carrier's host face,
which the Gateway's own dispatch face needs; no generated contribution is
reachable from it.
Resolve additive conflicts in the api-remotes client assembly by keeping
both the message-feedback remote mount and master's forwarded-event
allowlist, and regenerate the module graph.
#2201 landed three moves this branch sits on top of. `bindSettingsScope` became
`ctx.settingsScope.bind` on the Settings surface, so the section injects that
service and the Remote face instead of importing a free function; the
`SettingsScope` types moved into the runtime contract module, which is where
this branch's `base`, `user`, and `unset` now live; and the client
`credentials/changed` event became the forwarded owner event
`credentials/updated`, which the web-search card subscribes to through
`ctx.remote.$on`.
The generated module graph crossed mermaid's 1000-edge render guard with this
branch's 14 new edges. The guard doubles again, exactly as it did at 500 — the
graph counts real package edges, and trimming them to fit a renderer default
would make the document lie about the dependency set.
Three seams: the tsconfig path map gained a mapping on each side and keeps
both; the event-producer matrix is generated, so it was regenerated rather
than hand-merged row by row.
Consume the durable message-feedback sidecar from #2217 in the browser:
per-message Like/Dislike with an optional note, contributed through a
declared assistant-actions slot.
- carry MessageId on finalized AssistantMessageNode so a target is nameable
- declare conversation.chat.assistant-actions and render it in the
IconActions row between copy and branch
- hold one FeedbackController per Session with per-item ifVersion CAS,
reconciling a version-conflict from the reply's authoritative item
- mount messageFeedbackRemote alongside goalsRemote
Master gave the agent-preset nav its own glyph; this branch's plugins glyph
sits beside it, and both keep their own icon. Master's stricter
no-base-to-string rule also reaches this branch's new provider test, whose
fetch-call assertion now names the types it reads instead of stringifying an
unknown body.
The /feedback acknowledgement now echoes the receiving session id and
reports the mounted telemetry backend's sharing policy: the telemetry seam
exposes a backend-independent TelemetrySharingStatus through a required
abstract sharing member on the Telemetry service, the OTel backend maps its
mode onto it, and the command appends one policy-only sharing sentence
(full / feedback-only / disabled / not configured) to the acknowledgement.
The web client renders the text through the existing command row without a
client change; a new assembled-browser e2e mounts the shipped telemetry row
in FULL mode against a local dead endpoint and pins the shipped default
sentence as a keyless golden.
The web-app bundle gained dsh-app-boot on master beside this branch's
dependency list; both rows stand, and verify-cordis-config confirms every
patch row still resolves from that manifest.
A launcher provides three values before the tree mounts: ctx.cmdlineArgs
(get() is the whole interface) carrying everything after its own flags,
ctx.appExit for a bounded exit, and ctx.appPatches for decisions a later
recomposition must keep. An app's startup row injects cmdlineArgs and calls
runStartup() with its own commander program.
Rows the app configures inject its startup service, so they wait until the
startup row has resolved their values and provided it; --help prints,
disables those rows, and exits without the app ever starting.
A changed row is recycled — disabled, then re-enabled with its new values —
because a row's config is resolved when the Loader creates its fiber, while
the row is still waiting. Recycling never touches inject: an inject update
restarts the row from its unwrapped callback and loses the plugin's own
static injections. A mount still in flight is allowed to settle first, so
the disable has a fiber to dispose instead of racing one into existence.
Resolves the vendored-package rescope (cordis -> @deepseek-ai/cordis,
schemastery -> @deepseek-ai/schemastery) against this branch's settings
namespaces: the bash capability namespace, its two executors' section
installs, and the new plugin-config client package all move to the scoped
specifiers.
Three scenarios over the real wire: the cards this deployment's exposed
namespaces produce, one field edited down to the settings document with the
override badge that layering produces, and the reset that clears it back to
the composed default.
Every other settings golden gains the same nav entry and nothing else.