Files
deepseek-harness/docs/rfc/proposed/simplification/2026-07-04-drop-image-content-block.md
Tianyi Cui e13bbcb5d5 docs(rfc): propose nine simplification RFCs from a five-domain survey
Survey of master for surface area whose consumers are tests/docs only,
classified per candidate (production vs non-production corpus, rg + call-site
reads). New proposed/simplification RFCs:

- prune-producerless-vocabulary-variants: CacheHint/cache? fields,
  MessageSource 'agent', TurnTrigger 'continuation' (the TurnEndReasonMap
  omitted-until-emitted policy, applied)
- drop-inert-request-knobs: GenerateOptions.prefill (both adapters throw
  UNSUPPORTED), ToolSchema.strict (zero setters; beta-URL-only feature)
- drop-web-providers-change-event: the llm/adapter-change precedent replayed
- drop-image-content-block: no producer; every consumer silently drops it
- prune-write-only-fs-surface: fs-local STREAM_MIN_SIZE/streamMinSize,
  FsTarget.inputPath, FsEditOutcome.replacements/replaceAll,
  FileReadOutcome.limit/version
- prune-unimplemented-subagent-vocabulary: outputSchema/structured,
  toolFilter, sendMessage/resume (depthLimit stays)
- trim-acp-bridge-unreachable-surface: agentName/agentVersion knobs
  (resolves TODO(double-default)), toolKindFor name-sniffing
- prune-dead-core-spine-surface: SurfaceManager.invalidate(), runLoop/Inbox
  exports, ToolExecutionResult.callId
- share-app-bin-boot-glue: the twin coverage-exempt bin helpers

Also supplements three existing proposed RFCs with survey evidence: the bash
seam consumption census (generic-long-running-tool-runtime), three more
static inventories (discover-package-inventory), and the bridge's already-1:1
id usage (unify-agent-and-session-id).
2026-07-04 03:00:25 +08:00

3.5 KiB

RFC: Drop the image content block until a path can honor it

Status: proposed

Problem

ImageBlock (packages/llm/llm/src/types.ts) has no production producer, and every consumer on every path DROPS it: the deepseek adapter's serializer skips image blocks (a documented MVP limitation), the pi-ai converter skips them as unrepresentable, the ACP codec neither advertises image prompt capability nor forwards image blocks outbound and REJECTS image prompt content inbound, and the compaction estimator charges a flat token constant and renders [image]. An ImageBlock constructed today would silently vanish from the wire — the vocabulary advertises a capability no path honors, which is the silent-data-loss shape AGENTS.md's defensive patterns warn against. The only constructors anywhere are tests pinning the skip/drop/estimate branches.

Proposal

Remove ImageBlock, its ContentBlockMap entry, and the explicit skip/estimate branches in the deepseek serializer, the pi-ai converter, the ACP codec's outbound mapping, and compact-basic — the default arms those switches already carry for plugin-added block types absorb the cases. Update the vocabulary line in architecture.md, the pastes in core.md and llm-streaming.md, and the type-equiv manifest; drop or retarget the tests that construct image blocks to exercise the removed branches. The ACP codec's inbound rejection of image PROMPT content is unaffected — that guard is about protocol content a client can send regardless of our vocabulary, and it stays.

Why not keep it?

This is the most contested cut in the batch. Multimodal input (screenshots) is a plausible near-term coding-agent feature, and the content-block vocabulary RFC reserved the slot deliberately. Two responses. First, ContentBlockMap is merge-extensible by design: a real multimodal feature reintroduces image in core in the same coordinated change that maps it in the adapters, advertises and renders it in ACP, and prices it in compaction — the producer and its consumers arrive together, which is how the map is meant to grow. Second, the middle option — keep the type but make adapters throw UNSUPPORTED instead of silently dropping — converts this into exactly the shape the request-knobs RFC argues against: surface whose only implementation is rejection. Absence (a compile error at the would-be producer) is strictly clearer than either silent loss or universal throw.

If review lands on keeping the slot, the fallback this RFC records is: keep ImageBlock but replace every silent skip with a loud rejection, and document that policy in the vocabulary — the current silent drop is the one state with no defender.

Acceptance criteria

  • No ImageBlock / harness type: 'image' block construction outside this RFC; the codec's inbound ACP-image rejection still passes its tests.
  • Adapter/codec/compaction switches handle the case through their unknown-block default arms (pinned by the existing plugin-added-block tests where present).
  • Doc pastes, the manifest, and the architecture vocabulary list updated; pnpm run doc-sync green.

Risks

Re-adding a core vocabulary type later touches several packages at once — but that coordinated change is the shape a real multimodal feature needs anyway (adapter mapping, ACP advertisement, compaction pricing), and none of it exists today to preserve.