The DeepSeek search and Search/Fetch API providers now report readiness
from credential presence alone, on any model route: the account token
first on the deepseek-account route, then a literal apiKey or a
configured apiKeyEnv reference. A conversation on another provider's
model keeps web_search and web_fetch while a DeepSeek key is configured,
as on master, so the route refusal WEB_PROVIDER_ROUTE_DISABLED and its
upgrade guide are removed.
The pi-ai-conversation-updates and deepseek-messages-system-prompt
snapshots replay their master recordings again, the headless keyless
smoke points its search key reference at an unset variable, and the
readiness Agent Note records the rejected route and endpoint rules.
Review found two faults in the endpoint and configuration checks. The
providers took the official chat route's endpoint from the first active
dsh-llm-deepseek-api-key Loader row, so a composition that isolates several
instances answered for an unrelated one: a correct route lost search, or a
gateway's key reached DeepSeek. And a key saved on the Web search settings
page lands under the default DEEPSEEK_API_KEY reference, so the inference
that search had its own configuration never saw it.
Both DeepSeek web packages now serve only the deepseek-account and
deepseek-official routes, whatever key they are configured with, and no
longer compare the chat endpoint with their own. ready() checks the route
and then credential presence as before; the API-key resolver repeats the
route check and refuses with WEB_PROVIDER_ROUTE_DISABLED. The packages no
longer read Loader rows or depend on dsh-llm-deepseek-api-key, so the
lockfile, the module graph, and the keyless smoke expectations that the
endpoint check had changed return to master's.
An upgrade guide covers conversations on other providers' models, which
lose DeepSeek web search and, with the Search and Fetch APIs selected,
web_fetch.
Replace the FadeText measuring component with a self scroll timeline: it is
active only while a box overflows, so the end mask applies to clipped text
alone. Browsers without scroll-driven animations keep the ellipsis.
Model and effort text in the composer trigger, root menu values, and option
rows now clips and fades over its last 24px while it overflows, following the
measured-clip mask used by dockkit tab titles and PathLabel.
* ci: run native Windows PR jobs in a separate workflow
GitHub re-runs failed jobs only after a whole run finishes, so slow Windows jobs blocked re-running failed Linux jobs. CI Windows now owns the native Windows jobs and the Windows x64 Python runtime target, with its own required 'windows checks passed' verdict.
Refs #6893
* ci: run Python PR jobs in a separate workflow
Python rarely fails while the Windows lanes run long, so the Python SDK suite and the Linux/Windows x64 runtime matrix move into CI Python. The three pull-request workflows can now be re-run without waiting for each other, and branch protection gains "python checks passed" beside the existing verdicts.
Refs #6893
* docs: record the three PR verdicts in the failover runbook
* test: pin the shared environment across the PR workflows
* test: read the python SDK lane from CI Python
* test(sdk): parse session JSONL frames in the keyless smoke
Node 24.19 returned only the first frame of a concatenated zstd log, so the single JSON.parse saw just the header; 24.22 returns every frame and the JSONL body broke the parse. Read frames explicitly and select the main session by its header so both versions agree.
Profile templates and the default, optional, on-demand, retired, and
installation-owned bundle lists move to the new data-only package
@deepseek-ai/dsh-official-bundles, so editing a bundle list no longer
touches app-boot.
Master's step-scoped conditional activation resolves each tool's condition
before a model step assembles, and both the declarations and dispatch read
that admitted set. Prompt assembly no longer carries tool declarations, so
tool-web's system-prompt/assemble listener could not remove web_search any
more, and removing a declaration there never stopped a repeated call from
running.
tool-web now registers web_search with a when condition that asks
ctx.web.searchReady() about the route of the step's model selection, and its
guidance section renders from the step's admitted set. A step that does not
admit the tool leaves it out of declarations, guidance, and PTC program
bindings, and refuses a call to it as UNKNOWN_TOOL without reaching a
provider. Tests that execute web_search directly now run inside a resolved
step, as the executor requires for conditional registrations.
Docs, the readiness Agent Note, and the event producer/consumer table follow
the new mechanism; the tools README gains an explicit conditional-tools
anchor for cross-locale links.
Store a frozen copy of ThemeDefinition.icons, treat null as absent,
require replacement element ids under the symbol id, document the trust
level and ignored root attributes, and boot a theme plugin through the
Loader to check the sprite swaps and restores icons.
* feat(ui-user-questions): show answered batches as shared message bubbles
* refactor(ui-primitives): share clipboard confirmation lifecycle
* test(web): include answered question bubbles in steering goldens
* fix(ui-user-questions): keep answered turns collapsible
* test(ui-user-questions): drop the removed compactAnswer expectation
master removed ChatTurnProcessPresentation.compactAnswer in aaeb344
("fix(chat): keep the reply gap on a folded final answer"); reply spacing
now comes from the adjacent-row rules. The rebase left this assertion
behind, so the parametrized replay/live/prepend cases failed on every CI
coverage lane even though typecheck stayed green: toMatchObject accepts
the stale property.
* fix(ui-user-questions): address answer bubble review feedback
* test(web): observe file card styles after both cards mount
---------
Co-authored-by: imccyu <[email protected]>
The /api/file route bounds files by maxImageBytes, whose default is now
50 MiB, so the 20 MiB + 1 byte fixture no longer exceeded it and the
route served it with 200 instead of 413.
Linux CI lays the title-only header at 470px with the actions beside the
title, while macOS wraps them, so that width depends on the platform
font. The title-only case now records 1280, 600, and 390px; the tabs case
keeps all four widths.