DeepSeek Harness system prompt sections in a DSH plugin

Plugin DevelopmentPublished 2026-10-07Author: DeepSeek Plugin Market
DSH pluginDeepSeek Harnesssystem promptctx.systemPromptwaterfall
DSH plugin system prompt sections: how do you add to the prompt via ctx.systemPrompt, and how authoritative is the assemble waterfall?

A DSH plugin adds to the system prompt not by concatenating a string field but by registering contributors through ctx.systemPrompt, which one assembly call resolves, orders, and renders into a surface node (source). This article covers the four contribution kinds and their ordering, why the prompt is "surface node 0" rather than a request field, and the authority and ceiling of the assemble waterfall; where the prompt comes from and why it grows is explained in the system prompt explained, while this article covers the contract a developer must implement.

The four kinds of contribution and their ordering in a DSH plugin system prompt

The system prompt assembly package manages the data exchanged between prompt contributors and one assembly call, across four contribution kinds: section, context, tools, and variable (source).

  1. section — the prompt text itself. A PromptSection has a unique name (duplicate registration throws), an order (concatenated ascending, with equal orders using code-unit name order), text (a static string or a provider evaluated at each assembly, referencing {{variable}}s interpolated later by renderPrompt), and an optional complete.
  2. context — the cache-safe counterpart of a section, materialized as a durable user-role snapshot; a PromptContext is likewise unique by name, joined in ascending order, and empty text contributes nothing.
  3. tools — a tool schema provider returning a ToolProviderResult: schemas is the schema set visible to the model in this assembly, and knownNames is the pre-restriction name universe used to tell a typo in configuration from a known tool deliberately hidden in this scope.
  4. variable — a prompt variable whose name matches [a-z][a-z0-9_]*; the provider may return undefined, but rendering a section that references that value then fails.

Ordering is two-stage: sections first sort by ascending order, then by code-unit name order. Repository contributors resolve their centrally owned placement via getSectionOrder() (such as DEPLOYMENT_PERSONA_PREFIX / DEPLOYMENT_PERSONA_SUFFIX), and runtime-context contributors resolve a separate allocation via getContextOrder(). By scope, scoped sections and variables shadow same-named globals, and duplicates within one layer and non-finite orders both throw.

A DSH plugin system prompt is surface node 0, not a request field

After cooperative assembly, one effective complete section becomes the sole prompt section; the agent loop renders the sections with renderPrompt and submits the text as the system/message surface node (source). There are three ways it is submitted:

  1. First step — appended as surface node 0.
  2. When the rendered text changes — the node is replaced in place.
  3. Sequence continuation where the prepared call declares systemPromptUpdate: 'in-history' — the non-empty update is appended after the cached history.

The key takeaway: the system prompt reaches the model as a message in the derived history rather than a request field. Changing the prompt therefore means replacing or appending a surface node, not editing a request header — which is why prompt changes flow through the log and replay uniformly.

The context path is more restrained: assembly resolves and orders those contributions, but the agent loop records it after the retained model history only when the full current snapshot changes or compaction removes it.

The authority and ceiling of the DSH plugin assemble waterfall

system-prompt/assemble is an expert waterfall over the assembled sections, contexts, tools, and variables with scope-filtered dispatch, so scoped listeners receive only that scope's assemblies and the returned value is authoritative (source). Three boundaries cannot be crossed:

  1. A complete section is restored after the waterfall — if an effective complete section is registered, assembly restores it as the sole prompt section after the waterfall, so listeners cannot add to or replace that scope's system prompt. More than one effective complete section makes assembly fail.
  2. The signal belongs to this assembly only — the signal the waterfall receives controls only this explicit assembly request and must not be retained to control later turns.
  3. Reserved names and suppression — a tools() provider returning the reserved TOOL_ORDER_REST name makes assembly fail; suppressRuntimeContext() can suppress every dynamic runtime-context contribution in the calling context's scope without changing the services that own or enforce those facts, and multiple suppressors remain independently disposable.

Also: system-prompt/change is an unfiltered registry notification (a global change affects every scope); ctx.systemPrompt.assemble(context) assembles global and scoped providers, detaches tool parameters, applies canonical ordering, runs the waterfall, then enforces any complete section.

Three self-checks when done: is name unique and order finite; when replacing the whole prompt, did you use complete instead of mutating the waterfall result; and did you avoid caching signal to control later turns. To find community implementations of prompt plugins, search the DSH Plugin Hub; for extension-point ownership see the feature-to-mechanism map.

FAQ

How does a DSH plugin add a section to the system prompt?

A DSH plugin registers an ordered prompt section in the calling context's scope with ctx.systemPrompt.section(section): a PromptSection needs a unique name, an ascending order, and text that is either a static string or a provider evaluated at each assembly, and the call returns the exact Cordis effect disposer. A scoped section shadows a global section with the same name, while duplicates within one layer and non-finite orders throw.

What is the difference between section, context, tools, and variable in a DSH plugin?

Prompt assembly in a DSH plugin has four kinds of contribution: section is the prompt text itself, context is dynamic runtime context materialized as a durable user-role snapshot, tools provides the tool schemas visible to the model in this assembly, and variable is an interpolation value referenced by {{variable}}. Both sections and contexts are read-only in-process conventions, ordered by ascending order and then by code-unit name order.

Is a DSH plugin system prompt a request field or a history message?

A DSH plugin system prompt is neither a request field nor a history message, but a surface node: the agent loop renders the assembled sections with renderPrompt and submits the text as the system/message surface node — appended as surface node 0 for the first step, then replaced in place when the rendered text changes, or appended after the cached history during sequence continuation when the prepared call declares systemPromptUpdate: 'in-history'. So the prompt reaches the model as a message in the derived history rather than a request field.

Why is the system-prompt/assemble waterfall value authoritative in a DSH plugin?

The DSH plugin system-prompt/assemble value is authoritative because it is an expert waterfall over the assembled sections, contexts, tools, and variables with scope-filtered dispatch, so scoped listeners receive only that scope's assemblies. It has one ceiling: if an effective complete section is registered, assembly restores it as the sole prompt section after the waterfall, so listeners cannot add to or replace that scope's system prompt.

How does a DSH plugin replace the system prompt as a whole?

In a DSH plugin, set a PromptSection's complete to true: that contribution is treated as the complete system prompt, assembly still runs the cooperative waterfall to resolve tools, contexts, and variables, and then restores that exact section as the sole prompt section. Note that more than one effective complete section makes assembly fail, and a tools provider returning the reserved TOOL_ORDER_REST name also fails assembly.

Related Terms

AssembleContext
AssembleContext is the merge-extensible context in DeepSeek Harness that identifies the scope layer one prompt assembly resolves, carrying an optional scope and the explicit signal for that request; dsh-agent adds an agent field, assembleContextFor(agent, signal) sets these together, and a bare assembly has neither scope nor signal.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/system-prompt
PromptSection
PromptSection is the read-only in-process prompt registration convention, carrying a unique name, an ascending order, text that is static or a provider (and may reference {{variable}}s), and an optional complete flag; sections concatenate in ascending order then by code-unit name order, and duplicate registration throws.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/system-prompt
PromptContext
PromptContext is the cache-safe counterpart of PromptSection, carrying a unique name, an ascending order, and text; assembly resolves and orders these contributions, and the agent loop records it after the retained model history only when the full current snapshot changes or compaction removes it.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/system-prompt
systemPromptUpdate: 'in-history'
systemPromptUpdate: 'in-history' is a declaration on a prepared call meaning that when the rendered text changes, the non-empty update is appended after the cached history instead of replacing the system/message surface node in place, preserving derived-history semantics for prompt changes during sequence continuation.— https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/system-prompt

Sources