- dsh web gains --provider/--model: a non-deepseek provider mounts the
matching pi-ai catalog route (ambient credentials), making image input
reachable from the shipped Web assembly; requires an explicit --model
- attachment-local syncs the publication directories after the hard-link
publish so a reported durable reference survives a crash (POSIX; Windows
relies on filesystem metadata journaling)
- the attachment seam gains storage-free validateImage; the host validates a
complete multi-image prompt before persisting any member, so one malformed
image cannot strand valid members as unreferenced objects
- startSession sends before navigating: a rejected first send keeps the empty
state, its error strip, and the complete draft mounted
- the webserver rejects an undeclared-length body the moment it crosses the
configured limit instead of draining a potentially endless stream to EOF
- regenerate docs/module-graph.md for the attachment packages
- pack @deepseek-ai/dsh-attachment in the packed-install e2e closure so npm
resolves the new dsh-llm peer from the tarball set instead of the registry
- gate the pi-ai anthropic e2e profile strictly on ANTHROPIC_API_KEY: the
DeepSeek endpoint does not serve anthropic-messages, so the DEEPSEEK_API_KEY
fallback turned the keyless skip into a 404 across the whole suite
A well-formed provider stream that ends with finish_reason stop and zero
content blocks previously became a successful empty assistant message: the
turn completed silently, and drivers like goal-session counted the no-op
round. Both adapters now map that degenerate completion to a finish
{kind:'error'} with the new canonical EMPTY_RESPONSE code from dsh-llm, and
dsh-llm-retry adds the code to its default retryable set, so the existing
closed-step recovery path retries it and fails loud once the budget is
exhausted.
Covered by adapter unit tests, an llm-retry default-policy test, and a new
authored keyless ACP snapshot (empty-response-retry) with a deterministic
1 ms zero-jitter retry overlay.
pi-ai 0.80 restructured its entrypoints: the static catalog reads now
live on /providers/all as getBuiltinModels/getBuiltinProviders (keyed by
the catalog-only BuiltinProvider type, replacing KnownProvider at those
call sites), and the global streamSimple moved to /compat. The config
schema gains the new 'max' reasoning level.
Pass each resolved catalog model capacity into pi-ai stream conversion so the upstream full-message classifier can recognize provider-specific, silent, and length-stop overflow signals. Retain the harness text fallback for legacy provider wording and cover the catalog-resolution path with a mock-provider regression.
GenerateOptions.prefill had no production setter and both adapters
rejected it with LlmError('UNSUPPORTED') — its entire observable
behavior was two throws, each pinned by one adapter test. DeepSeek's
chat-prefix completion is a Beta feature on a base URL neither adapter
targets. ToolSchema.strict was threaded through defineTool, the
registry's schemas() allowlist, the deepseek wire mapping, a per-tool
payload-patching pass in the pi-ai adapter, and a tool-catalog render
row, yet no shipped tool set it and the internal endpoint story for
strict mode was never built.
Remove both fields end-to-end: the vocabulary in dsh-llm, the adapter
guards and wire branches, the dsh-tools threading, the tool-catalog
Strict row, the pinning tests, the core.md pastes, the adapter README
rows, and the cookbook line that used prefill as the UNSUPPORTED
example (now stated generically). The pi-ai payload fixup keeps the
half with a job: pi-ai stamps strict:false on every serialized tool,
so the fixup scrubs it unconditionally for wire parity with the
hand-rolled twin (per-tool set/delete machinery gone). temperature/
stop/maxTokens are untouched — honored end-to-end by both adapters.
Each knob returns with its first real producer: prefill with an
adapter that implements chat-prefix completion, strict with a tool
that wants it and a beta-endpoint story.
RFC: docs/rfc/implemented/simplification/2026-07-04-drop-inert-request-knobs.md
(moved from proposed/, amended to shipped reality); the content-block
vocabulary RFC's consequence line now records prefill as producer-gated.
dsh-llm owns the vocabulary (attribution.ts): AppIdentity with the version
read from the package manifest, userAgent(), and attributionHeaders(target,
identity) over a closed AttributionTarget union ('generic' | 'openrouter').
Both adapters send the headers on every provider request — llm-deepseek in
its fetch headers, llm-pi-ai through pi-ai's StreamOptions.headers — behind
an explicit attributionTarget config (never inferred from baseURL), with
mock-server tests asserting exact wire arrival and the absence of the
OpenRouter set by default.
The RFC moves to implemented/ amended with the settled identity (the
deepseek-harness token, the DeepSeek Harness title, the planned
deepseek-ai/deepseek-harness-sdk URL behind a FIXME until that repo exists)
and the explicit-config OpenRouter decision.