DSH plugin Conversation node: match, register, replay

Plugin DevelopmentPublished 2026-10-02Author: DeepSeek Plugin Market
DSH pluginDeepSeek HarnessConversation nodeWeb ClientSession events
A DSH plugin Conversation node adds a Chat row: match Session events on stable (kind, id), register a renderer, and replay via replace/prepend/append.

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:

EventRoleFacts that must persist
review/startthe single startreviewId, Turn/Step coordinates, title
review/progressupdatethe same reviewId, coordinates, replayable progress
review/endupdatethe 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).

ts
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:

  1. Register the Definition — ctx.uiConversation.events.register(definition), declaring kind and target. Expect: the same key on the same target registers only once and is identifiable at render time.
  2. Inject the renderer by key — register the component into the conversation.chat.node slot. Expect: the renderer reads only node.data and restricted Location hooks.
  3. Handle visibility — when temporarily leaving the visible stream return visibility: 'hidden', and do not return null to 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:

  1. replace (open / resync / gap repair) — rebuild the loaded window, match each event once per Definition, then replay Contexts that already have a start. Expect: start first, then update in ascending seq.
  2. 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.
  3. append (live events) — call match once 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

What is a DSH plugin Conversation node?

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.

How does a DSH plugin write a Conversation node Definition?

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.

What do match, start, and update do in a DSH plugin?

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.

How does a DSH plugin register a Conversation node renderer?

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.

How does a DSH plugin keep node replay consistent?

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