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 declared-provider-catalog note still recorded modalities as closed
because "nothing reads them: context.ts keeps only text blocks" — the
same reasoning this change disproves. Rewrite that fact in place, keep
pricing closed for its own still-current reason, and cross-link both
directions.
The new resolver cases never crossed the config schema, so neither
acceptance path this change adds had an executable gate: config.spec.ts
now refuses an unknown modality at both levels, pins the empty route
list to the namespace validator the settings seam actually runs, and
pins the `[]` materialization the inheritance rule depends on. A
catalog.spec.ts case re-asserts every rung through a written settings
section and `ctx.llm`, which is what the note's Testing section claimed.
Also export PiAiModality from the package entry — it types two public
config fields and consumers could not name it — record the unverified
declaration as a Known Limitation, and stop sharing one fallback array
across a route's models.
A model the installed pi-ai catalog does not describe was reported as
text-only with no way to say otherwise, so a vision model added through
the custom-provider form was refused at every image admission point.
The justification in the source described the DeepSeek chat-completions
serializer, which does reject image blocks; the pi-ai request converter
and every wire protocol it speaks carry images.
Modalities now resolve entry `input` -> installed catalog entry -> route
`defaultInput`, the chain the two capacity fallbacks already use, so the
route value is a fallback and never narrows a catalog model. Its default
stays `[text]`: nothing can interrogate a gateway for its modalities, and
over-claiming admits an image the provider rejects mid-turn, after prompt
admission has already committed the message.
The navigation bar named `/guide/` while the manifest published the guide's
first page at `guide/quickstart.md`, so the item served a 404 in both locales.
`landingLink` resolves each item against `orderedPages`, the ordering the
sidebar already renders, and a test asserts every navigation target is a route
the manifest publishes.
The VitePress config declared no position for the subsystem or other-interface
sections, so `indexOf` returned -1 and sorted them ahead of every declared
group: the reference landing page's own sidebar entry sat 1549px below the
fold. Four subsystem pages also shared `order` values with pages in the same
section, resolved only by sort stability and array concatenation order.
Section placement and collapse move into the manifest as a per-locale
declaration, and `sectionSpec` throws for an undeclared section instead of
sorting it silently to the top. Subsystem pages are grouped by concern, the
six topical groups collapse until one holds the page being read, and page
order derives from array position.
The projector drops the language-switcher line and repository badge the
canonical pages carry for their GitHub readers. The navigation bar gains the
DeepSeek wordmark, a release-stage tag, and a favicon; the sidebar scrollbar
rests invisible and appears while scrolling. Subsystem pages carry a two-level
outline, and the two plugin-development tracks now cross-link.
parseCmdline(ctx, program): void only adapts commander control flow to
the launcher: it parses the immutable cmdlineArgs snapshot and turns
help, version, parse errors, and action rejections into a ctx.appExit
request. App validation and the ctx.provide of the app-owned service
live in the program's own synchronous .action(), which commander runs
inside parse — program.error(...) there shares the exit path with a
grammar rejection. Deletes the CmdlinePlan export, its unread ctx
parameter, the type-unsound (() => ({}) as T) default, and the
T | undefined return with its per-caller publish guard.
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
Second alignment step for #2248: document-level drag intake behind the new
DropOverlay atom, lightbox close icon and shared dialog mask, DeepSeek Chat
thumbnail rules (single 240px long edge with ratio clamp, 64px tiles, merged
consecutive assistant images), image limits raised to 20/10MiB/100MiB and
published to clients as the imageLimits projection, whole-batch intake
pre-check with product-copy banners, and attachment-error reasons mapped to
localized copy.
Overlaps master on the preset e2e, the web scaffold, and the generated
config-catalog triplet; all merged textually, so the catalog is regenerated and
its pairing hashes re-recorded rather than trusted.
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.