ctx.tools.register(definition, { when }) adds a synchronous or asynchronous
condition. AgentLoop resolves conditions once per proposed step with the
agent, the model selection that step preparation captured, and the turn
signal, before prompt assembly and agent/pre-step. The resulting
ResolvedToolSet governs tool declarations, Native dispatch, PTC bindings,
and nested calls for that step and its retries, and the turn disposes it
when the proposal is rejected or fails, when no step opens, and after the
step. A false condition removes the tool everywhere; a thrown condition
ends the turn with TOOL_ACTIVATION_FAILED. Calls whose registration was
replaced or removed during the step return UNKNOWN_TOOL.
system-prompt/assemble now transforms PromptPresentation without a tool
array, so tool declarations stay provider-owned. exit_plan_mode is declared
only for steps whose selected plan state is active, the same state that
renders plan guidance; it still rejects a call before the selection is
logged.
Model selection stays as master defines it; this change adds no model
selection event or Agent field.
13 KiB
System Prompt Assembly
English | 中文
The system-prompt package owns the data exchanged between prompt contributors and one assembly call. The package README documents registration, ordering, scoping, and rendering behavior; this page records the exact cross-package types that plugins implement or pass.
Source: packages/core/system-prompt/src/index.ts.
Assembly context
AssembleContext identifies the scope layer one assembly resolves and may carry the explicit control signal for that request. It is merge-extensible: dsh-agent adds optional agent and modelSelection fields. assembleContextFor(agent, signal) sets the agent, scope, and signal; the loop supplies the model selection captured for the step and retained across retries, with omitted reasoning effort using adapter defaults. A bare assembly has neither scope nor signal.
/** Merge-extensible context for one prompt assembly. */
interface AssembleContext {
/**
* Scope whose providers and waterfall listeners participate. When absent,
* only global providers and subject-less listeners participate.
*/
scope?: ScopeKey
/** Explicit control signal for the turn that requested this assembly, when any. */
signal?: AbortSignal
}
Tool-provider result
ToolProviderResult.schemas is the model-visible set for the current assembly. knownNames is the provider's pre-restriction name universe used to distinguish a configured-name typo from a known tool that is deliberately hidden in this scope.
/** Tool schemas visible in one assembly and their pre-restriction name set. */
interface ToolProviderResult {
/** The schemas this provider contributes to THIS assembly. */
readonly schemas: readonly ToolSchema[]
/** The pre-restriction name universe for config validation (defaults to `schemas`' names). */
readonly knownNames?: readonly string[]
}
Prompt sections
The exported PERSONA_PREFIX_SECTION (deployment:persona-prefix) and PERSONA_SUFFIX_SECTION (deployment:persona-suffix) name the slots shared by global configuration and scoped contributions. Their PromptSectionOrderName entries are DEPLOYMENT_PERSONA_PREFIX and DEPLOYMENT_PERSONA_SUFFIX; the package README owns their placement and template configuration.
PromptSection is a readonly same-process registration contract. Its text may be static or resolved from the current assembly context. Sections sort by ascending order and then code-unit name; repository contributors resolve the service-owned named allocation through getSectionOrder(). Runtime-context contributors resolve their independent allocation through getContextOrder(). One effective complete section becomes the sole prompt section after cooperative assembly. agent-loop renders the assembled sections with renderPrompt and commits the text as a system/message surface node — appended as surface node 0 on the first step, then replaced in place when the rendered text changes or, when the prepared call declares systemPromptUpdate: 'in-history', appended after the cached history for non-empty updates in a continuing series — so the prompt reaches the model as a message of derived history rather than as a request field (decision; decision rule).
/** One contributed section of the system prompt (registry input). */
interface PromptSection {
/** Unique name — a duplicate registration throws (see {@link SystemPrompt.section}). */
readonly name: string
/**
* Sections are concatenated in ascending order. Equal orders use code-unit
* name order.
*/
readonly order: number
/**
* Static text or a provider evaluated at each assembly with that assembly's
* {@link AssembleContext}. The text may reference `{{variable}}`s — they are
* interpolated later, by {@link renderPrompt}, unless `interpolate` is false.
*/
readonly text: string | ((context: AssembleContext) => string)
/** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
readonly interpolate?: boolean
/**
* Treat this contribution as the complete system prompt. Assembly still
* runs the cooperative waterfall so tools, contexts, and variables can be
* resolved, then restores this exact section as the sole prompt section.
* More than one effective complete section makes assembly fail.
*/
readonly complete?: boolean
}
Dynamic prompt context
PromptContext is the cache-safe counterpart to PromptSection. The assembly resolves and orders these contributions, while agent-loop logs their complete current snapshot after retained model history only when it changed or compaction removed it.
/** Dynamic model context materialized as a durable user-role snapshot. */
interface PromptContext {
/** Unique name — a duplicate registration throws (see {@link SystemPrompt.context}). */
readonly name: string
/** Contexts are joined in ascending order. */
readonly order: number
/** Static text or a provider evaluated at assembly and admission refresh. Empty text contributes nothing. */
readonly text: string | ((context: AssembleContext) => string)
/** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
readonly interpolate?: boolean
/** Keep this operational context when optional runtime context is disabled. */
readonly required?: boolean
}
required: true keeps an operational contribution when optional runtime context is suppressed. interpolate: false preserves literal text, including variable-like directory names. AssembledContext carries the resolved text and that interpolation choice into rendering.
/** One resolved dynamic context contribution. */
interface AssembledContext {
/** The contributing context's unique name. */
name: string
/** The resolved text before variable interpolation. */
text: string
/** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
interpolate?: boolean
}
Cordis API
Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — the language sides differ only in locale-specific paired document paths. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.
ctx.systemPrompt — SystemPrompt
Registry service for the prompt inputs assembled before each model step.
/**
* Register an ordered prompt section in the calling context's scope. A scoped
* section shadows a global section with the same name; duplicates within one
* layer and non-finite orders throw. Registration and disposal emit
* `system-prompt/change`.
* @param section - the section to register.
* @returns the exact Cordis effect disposer.
*/
section(section: PromptSection): () => void
/**
* Resolve the centrally owned placement of a repository prompt section.
* @param name - stable section placement name.
* @returns the section's numeric sort order.
*/
getSectionOrder(name: PromptSectionOrderName): number
/**
* Resolve the centrally owned placement of a repository runtime context.
* @param name - stable context placement name.
* @returns the context's numeric sort order.
*/
getContextOrder(name: PromptContextOrderName): number
/**
* Register ordered dynamic context in the calling context's scope. Scoped
* entries shadow global entries with the same name.
* @param context - the context contribution to register.
* @returns the exact Cordis effect disposer.
*/
context(context: PromptContext): () => void
/**
* Suppress optional dynamic runtime-context contributions in the calling
* context's scope without changing the services that own or enforce those
* facts. Multiple suppressors remain independently disposable.
* @returns the exact Cordis effect disposer.
*/
suppressRuntimeContext(): () => void
/**
* Register a tool-schema provider in the calling context's scope. Global and
* matching scoped providers both contribute; returning the reserved
* {@link TOOL_ORDER_REST} name makes assembly fail.
* @param provider - evaluated for each assembly with its context.
* @returns the exact Cordis effect disposer.
*/
tools(provider: (context: AssembleContext) => ToolProviderResult): () => void
/**
* Register a prompt variable in the calling context's scope. Scoped values
* shadow globals; invalid or duplicate names throw. A provider may return
* `undefined`, but rendering a section that references that value then fails.
* @param name - the `[a-z][a-z0-9_]*` reference name.
* @param provider - evaluated for each assembly.
* @returns the exact Cordis effect disposer.
*/
variable(name: string, provider: (context: AssembleContext) => string | undefined): () => void
/**
* Refresh accepted registered runtime facts for request admission. Contexts
* added only by the assembly waterfall retain their accepted values. Current
* suppression removes optional contexts, and missing required registrations
* are restored in registry order. Sections, tools, and interpolation variables
* retain the accepted assembly; their providers and waterfall do not rerun.
* @param assembly - accepted assembly for this scope and step.
* @param context - the same scope and current plugin-defined assembly fields.
* @returns the accepted assembly with current runtime-context provider text.
*/
refreshContext(assembly: PromptAssembly, context: AssembleContext = {}): PromptAssembly
/**
* Assemble global and scoped providers, detach tool parameters, apply
* canonical ordering, then run the presentation waterfall. Scoped sections
* and variables shadow globals. The waterfall's text is authoritative except
* that an effective complete section is restored afterwards as the sole
* prompt section; tool declarations stay provider-owned.
* @param context - the optional scope, step tool set, and plugin-defined assembly fields.
* @returns the post-waterfall assembly with ordered tool declarations and any complete prompt enforced.
*/
async assemble(context: AssembleContext = {}): Promise<PromptAssembly>
Source: packages/core/system-prompt/src/index.ts
system-prompt/* events
system-prompt/assemble — waterfall
Presentation waterfall over assembled sections, contexts, and variables. Scope-filtered dispatch (@deepseek-ai/dsh-scope): scoped listeners receive only that scope's assemblies. The returned text is authoritative; tool declarations remain provider-owned. A supplied signal controls only this explicit assembly request and must not be retained to control later turns. A registered complete section is restored after this waterfall, so listeners cannot add to or replace that scope's system prompt.
/**
* Presentation waterfall over assembled sections, contexts, and variables.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): scoped listeners
* receive only that scope's assemblies. The returned text is authoritative; tool declarations remain provider-owned.
* A supplied signal controls only this explicit assembly request and must not
* be retained to control later turns. A registered complete section is
* restored after this waterfall, so listeners cannot add to or replace
* that scope's system prompt.
* @param assembly - the mutable assembly built from registered providers.
* @param context - the caller's per-assembly context.
* @mode waterfall
*/
'system-prompt/assemble'(this: Scoped<SystemPrompt>, assembly: PromptPresentation, context: AssembleContext, next: () => Promise<PromptPresentation>): Promise<PromptPresentation>
Types: Scoped
Source: packages/core/system-prompt/src/index.ts
system-prompt/change — emit
Emitted when any prompt provider changes. This registry notification is unfiltered because a global change affects every scope.
/**
* Emitted when any prompt provider changes. This registry notification is
* unfiltered because a global change affects every scope.
* @mode emit
*/
'system-prompt/change'(): void