DSH plugin Conversation node: match, register, replay
To add a business row to the Web Client's Chat view, a DeepSeek Harness (DSH) plugin registers a Conversation node: match a group of persistent Session events into one Context with a stable (kind, id), fold a Definition into State, and inject a conversation.chat.node renderer by key. Conversation is the target-neutral assembly layer between the client event window and the browser view, and a business row is its established extension path.
How a DSH plugin Conversation node matches: linking an event family by stable (kind, id)
Before writing a Definition, choose a stable business id — every event that forms the same Node must carry that id, or derive it independently from its own payload (source). The official docs use a review job to define the event contract:
| Event | Role | Facts that must persist |
|---|---|---|
review/start | the single start | reviewId, Turn/Step coordinates, title |
review/progress | update | the same reviewId, coordinates, replayable progress |
review/end | update | the same reviewId, coordinates, final summary |
The core discipline: match(event) is an identity extractor, not a fold — it receives only the current SessionEventLike and returns the Definition's internal id and lifecycle role; after a hit the Assembler locates the Context by (kind, id). A client must never guess that an update belongs to "the most recent unfinished Context", and each (kind, id) may have at most one start event.
How a DSH plugin registers a Conversation node: Definition and the chat node slot
A Definition needs match, start, and update, plus target and buildViewNode; registration has two steps — register the Definition, then inject the renderer (source).
export const inject = ['uiConversation', 'slots']
export function apply(ctx: ClientContext): void {
ctx.uiConversation.events.register(reviewDefinition)
ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
name: 'conversation.chat.node',
key: 'review-job',
}, ReviewNodeView))
}
Read it as three steps:
- Register the Definition —
ctx.uiConversation.events.register(definition), declaringkindandtarget. Expect: the same key on the same target registers only once and is identifiable at render time. - Inject the renderer by key — register the component into the
conversation.chat.nodeslot. Expect: the renderer reads onlynode.dataand restricted Location hooks. - Handle visibility — when temporarily leaving the visible stream return
visibility: 'hidden', and do not returnnullto retract an already-published node. Expect: the key stays stable so replay does not misalign.
Node identity must be stable: once a target Node is published it must keep returning the same key and preserve context.key as the React-side identity; anchorSeq is chosen from persistent ordering evidence.
How a DSH plugin Conversation node replays: replace, prepend, and publication
History may be requested from the tail backward, page by page; the Assembler sorts accepted inputs by their first seq and then enters State replay (source). There are three window paths:
- replace (open / resync / gap repair) — rebuild the loaded window, match each event once per Definition, then replay Contexts that already have a start. Expect:
startfirst, thenupdatein ascendingseq. - prepend (an earlier page) — match only the newly added earlier inputs, merge them into Contexts by
(kind, id), and replay only the affected ones. Expect: a newly discovered start activates the updates already collected. - append (live events) — call
matchonce per Definition and update only the hit Context. Expect: the hot path does not scan the whole window.
publication controls when State is materialized: use immediate for structural or terminal changes, animation-frame for high-frequency visible deltas, and none to only accumulate State for a later publication. Hard performance constraint: the normal append hot path must not walk the full event window, all Contexts, or already-rendered Nodes — put accumulated facts in State, put per-Turn/Step shared information in Location data, and use reader.previous() for indexed prior dependencies.
Three self-checks before you finish: is the business id carried stably on every event; does the renderer consume only node.data; and can you add an earlier page without replacing existing keyed nodes? To put UI into a different slot, see plugin UI; for ready-made DSH plugins to compare against, search the DSH Plugin Hub.
FAQ
A DSH plugin Conversation node is a client-side extension form that adds a business row to the Chat view. It folds a persistent Session event family into one Context and publishes it as a renderable node by view target; the official example adds a progress row for a review job.
A DSH plugin Conversation node Definition needs three core methods — match, start, and update — plus target and buildViewNode. match(event) only extracts identity, returning the internal id and the start/update role; start initializes State, and every later hit goes through update.
In a DSH plugin, match(event) is an identity extractor rather than a fold: it receives only the current SessionEventLike and returns the internal id and lifecycle role. The earliest hit's start initializes State, and every later Match (including other start events) calls update; when a transient Match is removed the engine re-selects start and recomputes State.
A DSH plugin registers a Conversation node renderer in two steps: it declares inject = ['uiConversation', 'slots'], then calls ctx.uiConversation.events.register(definition) in apply and injects the component into the conversation.chat.node slot by key. The renderer consumes only node.data and restricted Location hooks.
A DSH plugin keeps replay consistent with a stable business id and a replayable event family: every event that forms the same Node must carry the same id, and replaying in ascending log seq yields State deterministically. If the tail window has only update, the Assembler keeps a pending Context until earlier pagination supplies start.
Related Terms
- ConversationNodeDefinition
- ConversationNodeDefinition is the DSH plugin definition object registered for a Conversation node. It contains kind, target, match, start, update, an optional buildLocationData, and buildViewNode, folding a persistent Session event family into a renderable node.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/conversation
- Event Definition
- Event Definition is the matching unit of a DSH plugin. One match handles a single persistent event or a client-only transient event, linking inputs by a stable (kind, id), folding deterministic State, and optionally materializing a target node.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/conversation
- publication
- publication is the DSH plugin option that controls when State changes are materialized into the view. Use immediate for structural or terminal changes, animation-frame for high-frequency visible deltas, and none to only accumulate State for a later publication.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/conversation
- conversation.chat.node
- conversation.chat.node is the keyed slot where a DSH plugin registers Chat business-node renderers. Registration binds a component by key, and the renderer reads the target's own node data.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/extension-cookbook
Sources
- DeepSeek Harness docs - Conversation assembly· deepseek-ai
- DeepSeek Harness docs - Extension cookbook· deepseek-ai