Two overstated evidence claims, reworded to the precise fact: - prune-dead-core-spine-surface: runLoop has no importer OUTSIDE the package (its own agent.ts drives the loop with it); the dead surface is the public re-export, not the function. - generic-long-running-tool-runtime census: BashTask.done has no consumer through the public seam, but is production-load-bearing inside dsh-bash-local (disposal awaits it); only the public completion surface is single-consumer. Also fold the reviewer-located doc sites into the removal sets so the implementing PRs need no re-discovery: the llm/pi-ai/compact-basic README rows and the adding-an-llm-adapter cookbook line (prefill/image), the content-block-vocabulary RFC's has-a-home consequence lines (cache/prefill/image), the tools.md paste + type-equiv manifest row + tools README row (callId), and the session-surface RFC's full-rebuild-after-replacement sentence (invalidate).
4.2 KiB
RFC: Drop GenerateOptions.prefill and ToolSchema.strict — request knobs with no working end-to-end path
Status: proposed
Problem
Two request-contract knobs ride the whole request pipeline, yet neither can do anything today:
prefill(packages/llm/llm/src/types.ts) has no production setter — the loop assemblesmodel/system/tools/messagesplussessionId/signal, and the compaction backend adds onlymaxTokens— and BOTH adapters reject it:packages/llm/llm-deepseek/src/serialize.tsandpackages/llm/llm-pi-ai/src/adapter.tseach throwLlmError('UNSUPPORTED')on a non-undefinedprefill. The field's entire observable behavior is two throws, each pinned by one adapter test. DeepSeek's chat-prefix completion is a Beta feature on a base URL neither adapter targets.strict(ToolSchema, same file) is threaded throughDefineToolOptions/defineTool(packages/core/tools/src/schema.ts), the registry'sschemas()allowlist (packages/core/tools/src/index.ts), the deepseek wire mapping (packages/llm/llm-deepseek/src/serialize.ts, whose wire-type note records that strict mode requires the/betabase URL the adapter does not use), and a per-tool payload-patching pass inpackages/llm/llm-pi-ai/src/adapter.ts. No shipped tool sets it —rgacross everytool-*package src andexamples/finds zerostrict:producers; the only setters are dsh-tools unit tests.
Both knobs are adapter-symmetric, so removal sheds them from both twins together — the twin-adapter design is untouched.
Proposal
- Remove
prefillfromGenerateOptions, both adapters' UNSUPPORTED guards, the tests pinning the throws, the paste lines in core.md, the adapter README rows documenting the rejection, and the cookbook line using prefill as the UNSUPPORTED example (adding-an-llm-adapter.md); amend the content-block vocabulary RFC's consequence line naming prefill as having a home, per implemented/AGENTS.md. - Remove
strictfromToolSchema,DefineToolOptions,defineTool, and theschemas()allowlist; drop the deepseek serializer branch; simplify the pi-ai payload fixup to the unconditional scrub of pi-ai's own strict default (that half exists for wire parity with the hand-rolled twin and survives); drop the setter tests and the core.md paste line.
This RFC deliberately does NOT touch temperature, stop, or maxTokens: those are honored end-to-end by both adapters and are the natural first targets of a request-mutating hook plugin on agent/request.
Why not keep them?
"An explicit UNSUPPORTED throw is honest contract behavior" — but a knob whose only implementation across both twins is rejection promises nothing, and deleting it upgrades the failure mode: an accidental setter becomes a compile error instead of a runtime throw. "Strict schema adherence is an officially documented provider feature with complete plumbing" — but a knob is not product surface until a shipped tool sets it AND an endpoint honors it; today neither is true. Each returns with its first real producer: prefill together with an adapter that implements chat-prefix completion (and a stated policy for adapters that do not), strict together with a tool that wants it and a beta-endpoint story.
Acceptance criteria
rg prefilland a tool-schema-scopedrg strictreturn only this RFC (and unrelated prose such asstrictEqual).- Both adapters compile and their contract tests pass without the guards; the pi-ai fixup still scrubs the library's strict default (wire parity pinned by its serializer tests).
- Doc pastes and the type-equiv manifest in sync;
pnpm run doc-syncgreen.
Risks
A hooks/config plugin arriving via the interception seams may want to set request fields — it will reach for temperature/stop (kept, working), not a field adapters reject. If chat-prefix completion or strict mode become product features, the re-add lands with the adapter/endpoint work, where the contract can say what actually happens rather than "everyone throws".