The vocabulary maps grow by declaration merging, and the admission policy stated on TurnEndReasonMap is that a variant lands with its first emitter. Three declared items had no producer and no consumer: - CacheHint and the cache?: CacheHint fields on TextBlock/ToolResultBlock: nothing constructs a block with cache:, and neither adapter reads .cache — DeepSeek prompt caching is automatic (hints map OUT of responses, never IN). - MessageSourceMap.agent: zero constructors; the subagent backends send the parent prompt with no source (logs as user), and the envelope renderer interpolates source.kind without routing on it. - TurnTriggerMap.continuation: the loop structurally cannot emit it — continuation is further steps within a turn, never a new turn — and its only writer was an llm-replay test fixture that needed any non-message trigger (now an injection trigger). Each variant returns the day it gains a real producer, via the same merge-extensible maps. Docs updated in the same change: the MessageSourceMap paste in core.md, the TurnTriggerMap paste in session.md (manifest untouched — both symbols survive), the content-block vocabulary RFC's cache-hints consequence line, and the RFC moved to implemented/ and amended to shipped reality (the image block's own cache field had already left with the drop-image RFC).
core/ — product API spine
The packages every harness build is assembled from: the session log, the system-prompt assembly, the tool registry, the agent vocabulary, and the one concrete loop that drives them. These are product packages — the stable surface plugins and consumers build against.
| Package | Role | ctx key |
|---|---|---|
session/ |
Event-sourced session log + in-memory store | ctx.sessions |
system-prompt/ |
Prompt-section + tool-schema assembly registry | ctx.systemPrompt |
tools/ |
Tool registry + tools/pre-execute/tools/post-execute pipeline |
ctx.tools |
agent/ |
Agent interface, registry, agent/* event vocabulary |
ctx.agents |
agent-loop/ |
The concrete loop plugin: ReactLoopAgent + the loop driver |
ctx.agentLoop |
agent-core/ |
Bundle plugin: the providerless/executor-less/UI-less spine as code | (loads the spine) |
agent-loop is the one concrete implementation of the agent seam and lives here because it is the harness's default product loop; everything else in core/ is interface/vocabulary. Plugins depend on the agent vocabulary, never on agent-loop directly, so the loop stays swappable.
agent-core is the composition counterpart: one bundle plugin that loads the whole providerless spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) and forwards agent-loop's agents list as its own config. App packages (ui/stdio-agent, ui/acp-agent) consume it and add only a front door; a leaf adds the swappable backends plus any optional product tools it wants to expose. It lives in core/ because it composes exclusively core/ + interface packages and ships no provider, executor, or UI of its own.