9ebc38badf
The branch now bases on the hooks stack (PR #138's head), so every 'in-flight' reference to stack content became a current-state fact. Audited all nine RFCs + three supplements claim-by-claim against the merged tree (none invalidated; several strengthened): - prune-dead-core-spine-surface: describe the landed tools/pre-execute → dispatch → tools/post-execute pipeline — listeners return Decisions, the registry builds every result and snapshots it to protect callId, and a second (mutation-guard) test pins the field; drop the resolved wait-for-interception hedge; scope the additionalContext ferry out. - prune-producerless-vocabulary-variants: the ui-stdio fixture migrated off the continuation trigger (llm-replay is the sole writer now); the stack's own additions (rejected, prompt/blocked, hook/invoked+result) all arrived with producers — the admission policy demonstrated live. - prune-unimplemented-subagent-vocabulary: enrichment landed as lastAssistantMessage only (agentType was dropped in the stack's own review — the same judgment this RFC extends); the seam RFC now names tools/pre-execute deny, which exists, sharpening the re-add path. - drop-inert-request-knobs / drop-image-content-block / trim-acp-bridge-unreachable-surface: current-state rewordings (shipped bridges set no request fields; only compact-basic has explicit image arms; 13 hook goldens also pin agentInfo). - generic-long-running-tool-runtime census: second production seam consumer (hook-protocol runHook: resolve+run, stdin/env, foreground only — background machinery stays single-consumer); scrub-duplication blast radius. - discover-package-inventory: identical 54-entry tsconfig reference sets; the comparesLog scenario knob (fixture-derivable, like recorded). - unify-agent-and-session-id: third divergence site (in-process subagent children mint two UUIDs), the hooks bridge id-lookups, and ui-stdio's labelBySession map as a consumer that deletes under unification. New RFCs from the post-stack survey: - remove-agent-steering-mirror: the last mirror-of-durable event; zero production listeners; both retention RFCs deferred its fate, and the 'no durable twin' rationale is contradicted by the adjacent append. - tighten-hook-protocol-contract: producer-less 'native' dialect, parsed-and-discarded suppressOutput, and hook/result semantics (truncation + decision-string) defined twice in the bridges instead of the lib that owns the event. - single-source-acp-replay-config: cordis.yml/cordis.snapshot.yml differ by exactly one plugin entry, with no gate on the forced symmetry.
28 lines
4.0 KiB
Markdown
28 lines
4.0 KiB
Markdown
# 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, the explicit `image` estimate/placeholder arms in compact-basic, and the image-naming comments in the deepseek serializer's, pi-ai converter's, and ACP codec's default arms — those default arms already absorb the case the way they absorb any unknown block type. Update the vocabulary line in [architecture.md](../../../architecture.md), the block list in `packages/llm/llm/README.md`, the deepseek README's image-skip row, the pi-ai README's images-not-representable row, the compact-basic README's image-estimation and `[image]`-placeholder rows, the pastes in [core.md](../../../core-data-structures/core.md) and [llm-streaming.md](../../../core-data-structures/llm-streaming.md), and the type-equiv manifest; amend the [content-block vocabulary RFC](../../implemented/architecture/2026-06-11-content-block-vocabulary.md)'s block list and multimodal-home consequence per [implemented/AGENTS.md](../../implemented/AGENTS.md); 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](../../implemented/architecture/2026-06-11-content-block-vocabulary.md) 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](2026-07-04-drop-inert-request-knobs.md) 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.
|