Session V4 reserves developer-role messages for incremental model-visible session changes. The role, tool addition/removal blocks, and deferred-loading marker need a durable representation before independent provider and UI integrations can emit or consume them. Known developer events must match an open turn and step; unsupported providers and UI consumers fail explicitly. Request headers already capture full tool definitions. Persisting another ToolSchema in each addition would duplicate Session information and create two definitions that could drift. Additions therefore store toolName, and the containing developer event uses headerSeq to select one earlier known request/header for all its additions. A same-name replacement keeps its historical definition identity through restart, fork and surface changes. Removals require no definition copy, and ordinary developer messages omit headerSeq. Generic sourceEventSeqs remains independent derivation metadata. The rejected inline tool field is optional never with @persistenceReserved. The schema extractor retains that uninhabited slot, so permitting a future value requires a format bump instead of being mistaken for compatible optional metadata. Unrelated optional fields remain additive and readable. Native and Session admission reject missing, forward, non-header, unknown, ambiguous and incomplete referenced definitions, and reject the retired inline tool field. Validate complete schema fields only for referenced definitions, preserving unrelated header behavior and extra JSON metadata. Unknown ignorable records remain opaque. Empty developer nodes retain their surface position without producing a message or shadowing the system head. No automatic emitter, provider activation, UI implementation, or speculative public resolver is added. Keep the core-owned developer source binding beside the introduced role, including the request-only type rejection and attribution round trips. Update consumer guards, SDK/snapshot handling, bilingual owner documentation, and the same unreleased V4 acknowledgement. Keep earlier tool/source migration work and its documentation in the parent. Preserve the existing recorded Cordis inspection generation from the original developer change. Integration keeps the final source layer as the sole parent and preserves master's Messages input filtering, empty-user omission, empty tool results, and system-update placement. Developer/deferred guards still run before conversion. Native V4 admission, header references, reservation declarations, and generic tooling match the validated marker candidate. Parent-owned fixture and generator fixes remain in the parent. Earlier feature validation passed full build, full lint, 41 doc-sync gates, and 16 hygiene gates. The unchanged native implementation retains that evidence. The focused Session/V4/request-input run passes 884 tests across 38 files with 100% statement, branch, function and line coverage for the V3-to-V4 package and Session surface. Consumer tests pass 482 with one existing skip; tool, message and optional-field tests pass 146. Cordis inspection and compaction replays pass in both source and built modes (two cases per mode). Ten invalid referenced-definition/unknown-header controls failed before their scoped guards and pass after them. Reservation validation: the actual generated developer root records tool as optional primitive never. Widening it requires a bump, while traceId remains additive and preserved; all 85 focused reader/classifier tests pass. Final-parent validation: 205 tests across eight provider, type, catalog, Session and native-format files pass. The preserved parent fixture verifies Messages input-history replay; runtime/declaration and parent-owned-file comparisons show no loss during the final rebase. Co-authored-by: Tianyi Cui <[email protected]>
description, kind
| description | kind |
|---|---|
| System-prompt assembly for users and maintainers adding prompt sections, variables, tool-schema sources, or configuring the model-facing prompt. | package-reference |
@deepseek-ai/dsh-system-prompt
English | 中文
Summary
dsh-system-prompt lets agents receive one ordered system prompt and the available tool schemas for each model step. Use it to add prompt sections, dynamic runtime facts, reusable variables, or tool schemas, or to control the fixed harness identity, deployment persona, runtime context, and model-facing tool order. Agent-scoped contributions override same-named global defaults without affecting other agents. Invalid complete-prompt combinations and unresolved variables fail assembly instead of sending a malformed prompt.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Mount dsh-system-prompt wherever agents run: it provides ctx.systemPrompt, the registry every prompt contribution lands in. Contributions are scoped — registering through agent.ctx affects that agent alone and shadows a same-named global.
Configure the prompt
The config owns the fixed opener, runtime context, deployment persona prefix and suffix, and tool order; everything else comes from registered contributions.
- name: '@deepseek-ai/dsh-system-prompt'
config:
includeHarnessIdentity: true
includeRuntimeContext: true
personaPrefix: 'You are the deployment assistant.'
toolOrder: ['<unlisted-tools>']
| Field | Default | Meaning |
|---|---|---|
includeHarnessIdentity |
true |
Include the fixed You are an AI agent powered by DeepSeek Harness. first-party opener at order −1000. Set false only when a compatibility deployment owns the complete system prompt. |
includeRuntimeContext |
true |
Include ordered dynamic runtime context in assembly |
personaPrefix |
'' |
Global persona prefix template at order 0, before first-party guidance |
personaSuffix |
'' |
Global deployment:persona-suffix template at order 10200, after first-party guidance |
toolOrder |
— | Explicit model-facing tool order with one '<unlisted-tools>' rest entry |
The generated configuration catalog is the exhaustive source for every accepted field. A toolOrder list without exactly one rest entry or with duplicates fails at load; a listed name with no registered tool rejects every assemble().
Contribute a prompt section
Sections carry static or context-resolved text with an order; they are concatenated in ascending order and equal orders use code-unit name order. Repository-owned contributors resolve centrally allocated positions through ctx.systemPrompt.getSectionOrder(name); runtime-context contributors use getContextOrder(name). External contributions may use any finite order. A complete: true section becomes the exact complete prompt after assembly; more than one effective complete section makes assembly fail.
ctx.systemPrompt.section({
name: 'tool:bash',
order: 100,
text: 'Prefer bash for file and process operations.',
})
Set interpolate: false on a section to preserve its text literally, including {{…}} groups in generated tool documentation. Other sections interpolate variables by default.
Contribute a prompt variable
Variables are referenced from section text as {{name}} and resolved at each assembly; scoped variables shadow a same-named global for that agent. The loop supplies model and cwd; any plugin can register the facts it owns.
ctx.systemPrompt.variable('cwd', ({ agent }) => agent?.session.header.cwd)
Contribute tool schemas
Tool-schema providers are evaluated per assembly and contribute the model-visible ToolSchema set; ToolRuntime registers itself automatically, so most tools need no manual wiring here. A provider returns the post-restriction visible set plus the pre-restriction name universe used by toolOrder.
Suppress runtime context
suppressRuntimeContext() removes every dynamic runtime-context contribution for the calling scope without disabling the services that own the underlying facts; multiple suppressors compose and the effect restores context when none remains.
Understand the implementation
Implementation internals — click to expand
This section explains how the package realizes the behavior above; the observable contract is covered in Use this package.
Design concept
The package is a registry plus a cooperative assembly pipeline. One assemble() call merges the global layer with the requested scope's layer, detaches tool parameters, canonicalizes section order by number and then name, runs the scope-filtered system-prompt/assemble waterfall, restores an effective complete section as the sole prompt section, and applies any active runtime-context suppressor. Sections and dynamic contexts are separate inputs: sections become prompt text, while contexts become sourced user-role snapshots in model history under the loop. Tool schemas are part of the assembly by design — "what the model is told it can do" is one coherent thing, even though adapters transmit schemas as a separate wire field.
Source map
| File | Role |
|---|---|
src/index.ts |
Plugin entry: SystemPrompt service, config, assembly pipeline, renderPrompt |
src/invariant.ts |
Invariant companion |
Assembly and rendering
Assembly resolves and renders in two stages: assemble() returns sections with resolved-but-uninterpolated text, the ordered tool schemas, and every registered variable resolved against the context, while renderPrompt() interpolates {{variable}} references unless a section sets interpolate: false, drops empty sections, and joins with blank lines — strictly, an unknown reference, a registered-but-valueless reference, or a malformed complete group throws, because a malformed prompt is worse than a loud failure. toolOrder canonicalizes the collected tools before the waterfall (registration order is a plugin-load artifact); a waterfall listener that mutates the list owns the determinism of what it emits.
Scoping
Scoped sections, variables, and tool providers shadow globals for one agent, and the assembly waterfall dispatches scope-filtered. Registry-change notifications (system-prompt/change) are deliberately unfiltered because a global change affects every scope.
Further Exploration
The package-level contract is enough for most consumers; read these when you need the surrounding domain.
- System-prompt subsystem — the exact cross-package types and generated service API.
- tools package — the tool registry whose schemas flow into assembly.
- Prompt variables Agent Note — who owns which prompt facts.
- First-party prompt order Agent Note — the sparse named order allocation.
- Core group map — how the core packages compose.
Model Experience
System prompt
What the model sees
First-party sections render the harness identity, deployment persona prefix (including the model-name introduction), reusable instructions (including the generated tools SDK and structured-output guidance), then the environment-bearing suffix: harness source (10000), Web surface (10100), and deployment persona suffix (10200). External section orders and assembly listeners remain authoritative. includeHarnessIdentity: false omits only that fixed opener. Empty sections disappear; scoped sections and variables can shadow globals for one agent. The system-prompt/assemble waterfall determines the delivered prompt and tool schemas unless one effective section declares itself complete — that exact section then becomes the whole system prompt while the waterfall's contexts, tools, and variables remain. The rendered prompt reaches the model as a system-role message of derived history — surface node 0, or the latest system node after an in-history update — neither the loop request nor request/header carries a separate system field. If the complete rendering is empty, the loop clears every active system node through logged empty replacements, so no older prompt remains in model history. Ordered dynamic contexts are separate from sections and become sourced user-role snapshots only when present; includeRuntimeContext: false or a scoped suppressor removes them all.
Harness identity
You are an AI agent powered by DeepSeek Harness.
Token effect
Identity is a fixed per-request cost when enabled. Persona prefixes, suffixes, and plugin text are repeated per request and scale with their rendered content.
KV Cache effect
Prefix-stable while identity, persona, variables, section text, and order render identically: an unchanged rendering leaves the system nodes untouched unless an incapable route or a new request series must consolidate retained in-history prompts. Without systemPromptUpdate, non-empty prompt text is consolidated at the first system node through logged per-node replacements, so a head rewrite loses prefix reuse from its first changed token; when the prepared call declares systemPromptUpdate: 'in-history', the agent loop appends a non-empty changed prompt after the cached history inside a continuing request series, so the prefix through that history stays reusable (decision rule). With the same model, persona prefix, tools, and preceding instructions, different source paths, local Web URLs, or persona suffix values leave the reusable first-party prefix unchanged. Persona prefix changes can alter the early prefix. Any change may invalidate reuse from the first changed token; provider cache sharing and measured hit rates are not guaranteed.
Tool schemas
What the model sees
For shipped tools, the model receives the per-agent-visible subset of the generated tool schemas, ordered by configuration or lexicographically after restrictions and assembly interception. Extensions can contribute additional definitions through the same registry. Sections and schema providers are separate assembly inputs. A restriction does not remove a section registration: tool-guidance plugins use text({ scope }) and ctx.tools.get(name, scope) to return empty text or select applicable fragments. Arbitrary static sections are not automatically rewritten.
Token effect
Schema tokens repeat on every request. Restricting a tool removes its entire schema cost for that agent but not a separate prompt section; reordering changes cache shape but not semantic content.
KV Cache effect
Prefix-stable while the visible schema set, rendering, and order are unchanged. Registration, restriction, or reordering may invalidate reuse from the first changed schema token.
Known Limitations and Deferred Work
These limits define when prompt assembly needs special care. They are current package constraints, not a task backlog.
- Deployment-authored prompt text is config/composition only — this plugin owns the global persona prefix and suffix defaults, creator plugins may register agent-scoped shadows, and other sections come from the plugin that owns the fact; there is no end-user prompt-editing API.
- No inline escape syntax in interpolated text — use
interpolate: falsewhen a whole section must preserve literal braces. toolOrdermisconfiguration surfaces at prompt assembly (the first turn), not at boot — only shape violations throw at config load.
Dev Note
Working context for maintainers — click to expand
None.