@qlzt/dsh-system-prompt 0.0.0-stage → 0.2.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,88 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/core/system-prompt/README.md
5
+ /:
6
+ en: 51d99b2500db958f
7
+ zh: 91f2b113ba7989fd
8
+ /deepseek-ai-dsh-system-prompt:
9
+ en: e1cfb1611662e8ab
10
+ zh: 5476f22f8ffe923a
11
+ /deepseek-ai-dsh-system-prompt/summary:
12
+ en: f5cce6867daddbab
13
+ zh: 4a1a47623c198207
14
+ /deepseek-ai-dsh-system-prompt/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /deepseek-ai-dsh-system-prompt/use-this-package:
18
+ en: 7bea567207cd7e00
19
+ zh: ffc050b05f07ba7f
20
+ /deepseek-ai-dsh-system-prompt/use-this-package/configure-the-prompt:
21
+ en: 87e28893e6cf58d7
22
+ zh: 06f476b38c4d2038
23
+ /deepseek-ai-dsh-system-prompt/use-this-package/contribute-a-prompt-section:
24
+ en: 165fecb5329b00b6
25
+ zh: a7c595d1787916c9
26
+ /deepseek-ai-dsh-system-prompt/use-this-package/contribute-a-prompt-variable:
27
+ en: b3e4c9b4ced59488
28
+ zh: d80c791e72bd5821
29
+ /deepseek-ai-dsh-system-prompt/use-this-package/contribute-tool-schemas:
30
+ en: 009ef267dac2f270
31
+ zh: e79c24bff5cd836f
32
+ /deepseek-ai-dsh-system-prompt/use-this-package/suppress-runtime-context:
33
+ en: a664608a3b37f8d7
34
+ zh: d6dd60772f4d72b8
35
+ /deepseek-ai-dsh-system-prompt/understand-the-implementation:
36
+ en: ce7e776e9d516bdb
37
+ zh: 59d2751743db7795
38
+ /deepseek-ai-dsh-system-prompt/understand-the-implementation/design-concept:
39
+ en: c91963b89a866fed
40
+ zh: ef9ef4940145778f
41
+ /deepseek-ai-dsh-system-prompt/understand-the-implementation/source-map:
42
+ en: d9575bcb07a1145b
43
+ zh: 0f1a388780d055b1
44
+ /deepseek-ai-dsh-system-prompt/understand-the-implementation/assembly-and-rendering:
45
+ en: 54977e6c431f851e
46
+ zh: ef60007054b97ff0
47
+ /deepseek-ai-dsh-system-prompt/understand-the-implementation/scoping:
48
+ en: 1100ab1dfe961631
49
+ zh: c510275c893951b4
50
+ /deepseek-ai-dsh-system-prompt/further-exploration:
51
+ en: 108d1b4cca466bc7
52
+ zh: e1275966f83c14f9
53
+ /deepseek-ai-dsh-system-prompt/model-experience:
54
+ en: 215e7ba838619b7b
55
+ zh: a311da3843709f90
56
+ /deepseek-ai-dsh-system-prompt/model-experience/system-prompt:
57
+ en: cf78b9dd5596495f
58
+ zh: bd44560f8c2435e8
59
+ /deepseek-ai-dsh-system-prompt/model-experience/system-prompt/what-the-model-sees:
60
+ en: 0aaabee3a9883eea
61
+ zh: 13e6ead5b455fa30
62
+ /deepseek-ai-dsh-system-prompt/model-experience/system-prompt/what-the-model-sees/harness-identity:
63
+ en: 40effd71cb503319
64
+ zh: 9969356237a4f398
65
+ /deepseek-ai-dsh-system-prompt/model-experience/system-prompt/token-effect:
66
+ en: 29722e212137deaa
67
+ zh: 92cd0f1a16b10315
68
+ /deepseek-ai-dsh-system-prompt/model-experience/system-prompt/kv-cache-effect:
69
+ en: ae980c28b57dc060
70
+ zh: 331eea5b204fa9ac
71
+ /deepseek-ai-dsh-system-prompt/model-experience/tool-schemas:
72
+ en: 3039bc5e7a796b21
73
+ zh: 6b7a9eaf69c9ea53
74
+ /deepseek-ai-dsh-system-prompt/model-experience/tool-schemas/what-the-model-sees:
75
+ en: a983ef220593cf73
76
+ zh: 28770612ffc1be60
77
+ /deepseek-ai-dsh-system-prompt/model-experience/tool-schemas/token-effect:
78
+ en: f3a7d374c55e1720
79
+ zh: 0ee48af678cb875f
80
+ /deepseek-ai-dsh-system-prompt/model-experience/tool-schemas/kv-cache-effect:
81
+ en: ebd329e360f0d143
82
+ zh: 9d67fe503a6a095a
83
+ /deepseek-ai-dsh-system-prompt/known-limitations-and-deferred-work:
84
+ en: 23bb9f59ceb1620c
85
+ zh: be34f1770fa2e027
86
+ /deepseek-ai-dsh-system-prompt/known-limitations-and-deferred-work/dev-note:
87
+ en: e67f633dc6fe7773
88
+ zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -1,3 +1,190 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "System-prompt assembly for users and maintainers adding prompt sections, variables, tool-schema sources, or configuring the model-facing prompt."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @deepseek-ai/dsh-system-prompt
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ `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.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ 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.
29
+
30
+ <a id="configure-the-prompt"></a>
31
+ ### Configure the prompt
32
+
33
+ The config owns the fixed opener, runtime context, deployment persona prefix and suffix, and tool order; everything else comes from registered contributions.
34
+
35
+ ```yaml
36
+ - name: '@deepseek-ai/dsh-system-prompt'
37
+ config:
38
+ includeHarnessIdentity: true
39
+ includeRuntimeContext: true
40
+ personaPrefix: 'You are the deployment assistant.'
41
+ toolOrder: ['<unlisted-tools>']
42
+ ```
43
+
44
+ | Field | Default | Meaning |
45
+ |---|---|---|
46
+ | `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. |
47
+ | `includeRuntimeContext` | `true` | Include optional ordered dynamic runtime context in assembly |
48
+ | `personaPrefix` | `''` | Global persona prefix template at order `0`, before first-party guidance |
49
+ | `personaSuffix` | `''` | Global `deployment:persona-suffix` template at order `10200`, after first-party guidance |
50
+ | `toolOrder` | — | Explicit model-facing tool order with one `'<unlisted-tools>'` rest entry |
51
+
52
+ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-system-prompt) 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()`.
53
+
54
+ ### Contribute a prompt section
55
+
56
+ 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.
57
+
58
+ ```text
59
+ ctx.systemPrompt.section({
60
+ name: 'tool:bash',
61
+ order: 100,
62
+ text: 'Prefer bash for file and process operations.',
63
+ })
64
+ ```
65
+
66
+ Set `interpolate: false` on a section or dynamic context to preserve its text literally, including `{{…}}` groups in generated tool documentation or directory names. Other contributions interpolate variables by default.
67
+
68
+ `refreshContext(assembly, context)` refreshes registered runtime facts after pre-step and route preparation. It preserves accepted sections, tools, variables, and contexts contributed only by the assembly waterfall, applies current optional-context suppression, and restores missing required entries. Put changing facts in context providers; registered context text is read again before admission.
69
+
70
+ ### Contribute a prompt variable
71
+
72
+ Variables are referenced from section or context text as `{{name}}` and resolved at each assembly; scoped variables shadow a same-named global for that agent. The loop supplies `provider` and `model`; plugins can register the facts they own. The working-directory service supplies directory text as literal user context.
73
+
74
+ ```text
75
+ ctx.systemPrompt.variable('response_language', () => 'English')
76
+ ```
77
+
78
+ Custom persona templates must not depend on a built-in `{{cwd}}` variable: the loop does not register it, so unresolved references fail prompt assembly before any model request. Remove the `Your working directory is {{cwd}}.` clause from home, profile, or invocation `personaSuffix` settings and preset `suffix` fields, preserving unrelated text. Omit or clear an otherwise empty suffix. The [working-directory service](../../session/working-directory/README.md) supplies the required current-directory context.
79
+
80
+ ### Contribute tool schemas
81
+
82
+ 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`.
83
+
84
+ ### Suppress runtime context
85
+
86
+ `suppressRuntimeContext()` removes optional dynamic context for the calling scope without disabling the owning services. Contributions marked `required` remain visible. Multiple suppressors compose; optional context returns when none remains.
87
+
88
+ -----
89
+
90
+ <a id="understand-the-implementation"></a>
91
+ ## Understand the implementation
92
+
93
+ <details>
94
+ <summary>Implementation internals — click to expand</summary>
95
+
96
+ This section explains how the package realizes the behavior above; the observable contract is covered in [Use this package](#use-this-package).
97
+
98
+ ### Design concept
99
+
100
+ 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.
101
+
102
+ ### Source map
103
+
104
+ | File | Role |
105
+ |---|---|
106
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `SystemPrompt` service, config, assembly pipeline, `renderPrompt` |
107
+
108
+ ### Assembly and rendering
109
+
110
+ 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.
111
+
112
+ ### Scoping
113
+
114
+ 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.
115
+
116
+ </details>
117
+
118
+ -----
119
+
120
+ <a id="further-exploration"></a>
121
+ ## Further Exploration
122
+
123
+ The package-level contract is enough for most consumers; read these when you need the surrounding domain.
124
+
125
+ - [System-prompt subsystem](../../../docs/subsystems/system-prompt.md) — the exact cross-package types and generated service API.
126
+ - [tools package](../tools/README.md) — the tool registry whose schemas flow into assembly.
127
+ - [Prompt variables Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md) — who owns which prompt facts.
128
+ - [First-party prompt order Agent Note](../../../.agents/notes/archived/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md) — the sparse named order allocation.
129
+ - [Core group map](../README.md) — how the core packages compose.
130
+
131
+ -----
132
+
133
+ <a id="model-experience"></a>
134
+ ## Model Experience
135
+
136
+ ### System prompt
137
+
138
+ #### What the model sees
139
+
140
+ 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 optional contributions while required operational context remains.
141
+
142
+ ##### Harness identity
143
+
144
+ ```markdown
145
+ You are an AI agent powered by DeepSeek Harness.
146
+ ```
147
+
148
+ #### Token effect
149
+
150
+ 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.
151
+
152
+ #### KV Cache effect
153
+
154
+ 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](../agent-loop/README.md#understand-the-implementation)). 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.
155
+
156
+ ### Tool schemas
157
+
158
+ #### What the model sees
159
+
160
+ For shipped tools, the model receives the per-agent-visible subset of the [generated tool schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tools), 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.
161
+
162
+ #### Token effect
163
+
164
+ 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.
165
+
166
+ #### KV Cache effect
167
+
168
+ 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.
169
+
170
+ ## Known Limitations and Deferred Work
171
+
172
+ <a id="known-limitations-and-deferred-work"></a>
173
+
174
+
175
+ These limits define when prompt assembly needs special care. They are current package constraints, not a task backlog.
176
+
177
+ - **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.
178
+ - **No inline escape syntax in interpolated text** — use `interpolate: false` when a whole section must preserve literal braces.
179
+ - **`toolOrder` misconfiguration surfaces at prompt assembly (the first turn), not at boot** — only shape violations throw at config load.
180
+
181
+
182
+ <a id="dev-note"></a>
183
+ ### Dev Note
184
+
185
+ <details>
186
+ <summary>Working context for maintainers — click to expand</summary>
187
+
188
+ None.
189
+
190
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,190 @@
1
+ ---
2
+ description: "面向用户与维护者的系统提示词组装说明,用于添加提示词段、变量、工具 schema 来源或配置面向模型的提示词。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-system-prompt
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ `dsh-system-prompt` 让 agent 在每个模型步骤收到一份有序系统提示词与可用工具 schema。需要添加提示词段、动态运行时事实、可复用变量或工具 schema,或者控制固定 harness 身份、部署 persona、运行时上下文和面向模型的工具顺序时,请使用本包。agent 作用域的贡献会遮蔽同名全局默认值,而不影响其他 agent。无效的完整提示词组合与未解析变量会使组装失败,不会向模型发送格式错误的提示词。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 在任何运行 agent 的地方挂载 `dsh-system-prompt`:它提供 `ctx.systemPrompt`,即每个提示词贡献所落入的注册表。贡献带作用域——通过 `agent.ctx` 注册只影响该 agent,并遮蔽同名全局项。
29
+
30
+ <a id="configure-the-prompt"></a>
31
+ ### 配置提示词
32
+
33
+ 配置拥有固定开场白、runtime 上下文、部署 persona 前缀与后缀与工具顺序;其余一切来自已注册的贡献。
34
+
35
+ ```yaml
36
+ - name: '@deepseek-ai/dsh-system-prompt'
37
+ config:
38
+ includeHarnessIdentity: true
39
+ includeRuntimeContext: true
40
+ personaPrefix: 'You are the deployment assistant.'
41
+ toolOrder: ['<unlisted-tools>']
42
+ ```
43
+
44
+ | 字段 | 默认值 | 含义 |
45
+ |---|---|---|
46
+ | `includeHarnessIdentity` | `true` | 是否包含顺序为 −1000 的第一方固定开场白 `You are an AI agent powered by DeepSeek Harness.`。仅当兼容性部署拥有完整系统提示词时设为 false。 |
47
+ | `includeRuntimeContext` | `true` | 是否在组装中包含可选的有序动态 runtime 上下文 |
48
+ | `personaPrefix` | `''` | 全局 persona 前缀模板,顺序为 `0`,位于第一方指导之前 |
49
+ | `personaSuffix` | `''` | 全局 `deployment:persona-suffix` 模板,顺序为 `10200`,位于第一方指导之后 |
50
+ | `toolOrder` | — | 显式面向模型工具顺序,含一个 `'<unlisted-tools>'` 其余项标记 |
51
+
52
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-system-prompt)是每个受支持字段的穷尽式真源。没有恰好一个其余项或存在重复项的 `toolOrder` 列表会在加载时失败;已列名称没有对应已注册工具会使每次 `assemble()` 被拒绝。
53
+
54
+ ### 贡献提示词段
55
+
56
+ 段携带静态或按上下文解析的文本与 `order`;它们先按顺序值升序拼接,顺序值相同时再按名称的代码单元顺序排列。仓库自带贡献方通过 `ctx.systemPrompt.getSectionOrder(name)` 解析集中分配的位置;runtime-context 贡献方使用 `getContextOrder(name)`。外部贡献可以使用任意有限的顺序值。`complete: true` 段会在组装后成为精确的完整提示词;有效的 complete 段超过一个时,组装会失败。
57
+
58
+ ```text
59
+ ctx.systemPrompt.section({
60
+ name: 'tool:bash',
61
+ order: 100,
62
+ text: 'Prefer bash for file and process operations.',
63
+ })
64
+ ```
65
+
66
+ 在段或动态上下文上设置 `interpolate: false` 可原样保留文本,包括生成的工具文档或目录名中的 `{{…}}` 组。其他贡献默认执行变量插值。
67
+
68
+ `refreshContext(assembly, context)` 在 pre-step 与路由准备之后刷新已注册的运行时事实。它保留已接纳的段、工具、变量及仅由组装 waterfall 添加的上下文,应用当前的可选上下文抑制,并恢复缺失的必需条目。将变化的事实放在上下文 provider 中;已注册上下文的文本会在准入前再次读取。
69
+
70
+ ### 贡献提示词变量
71
+
72
+ 变量从段或上下文文本中以 `{{name}}` 引用,每次组装时解析;带作用域的变量会为该 agent 遮蔽同名全局变量。循环提供 `provider` 和 `model`;插件可注册自己拥有的事实。工作目录服务通过不执行插值的用户上下文提供目录文本。
73
+
74
+ ```text
75
+ ctx.systemPrompt.variable('response_language', () => 'English')
76
+ ```
77
+
78
+ 自定义 persona 模板不得依赖内置 `{{cwd}}` 变量:循环不注册它,因此无法解析的引用会在任何模型请求之前使提示词组装失败。请从 home、profile 或单次调用的 `personaSuffix` 设置以及 preset 的 `suffix` 字段中移除 `Your working directory is {{cwd}}.` 语句,保留无关文本。后缀没有其他内容时,可省略或清空该字段。[工作目录服务](../../session/working-directory/README.zh.md) 提供必需的当前目录上下文。
79
+
80
+ ### 贡献工具 schema
81
+
82
+ 工具 schema 提供方在每次组装时求值,并贡献模型可见的 `ToolSchema` 集合;`ToolRuntime` 会自动注册自身,因此大多数工具在此无需手动接线。提供方返回限制后的可见集合,外加 `toolOrder` 使用的限制前名称全集。
83
+
84
+ ### 抑制运行时上下文
85
+
86
+ `suppressRuntimeContext()` 移除调用作用域的可选动态上下文,但不禁用拥有这些事实的服务。标记为 `required` 的贡献仍然可见。多个抑制器独立组合;不再存在抑制器时,可选上下文恢复。
87
+
88
+ -----
89
+
90
+ <a id="understand-the-implementation"></a>
91
+ ## 理解实现
92
+
93
+ <details>
94
+ <summary>实现细节——点击展开</summary>
95
+
96
+ 本节解释该包如何实现上述行为;可观察约定已在[使用本包](#use-this-package)中完整说明。
97
+
98
+ ### 设计理念
99
+
100
+ 该包是一个注册表加一条协作式组装流水线。一次 `assemble()` 调用把全局层与所请求作用域的层合并,分离工具参数,先按数值再按名称规范化段顺序,运行按作用域筛选的 `system-prompt/assemble` waterfall,把有效的 complete 段恢复为唯一的提示词段,并实施任何当前生效的运行时上下文抑制器。段与动态上下文是独立的输入:段成为提示词文本,而上下文在循环下成为模型历史中带来源的 user 角色快照。工具 schema 按设计属于组装结果——「模型获知自己能做什么」是一个连贯整体,尽管适配器把 schema 作为独立 wire 字段传输。
101
+
102
+ ### 源码地图
103
+
104
+ | 文件 | 职责 |
105
+ |---|---|
106
+ | [`src/index.ts`](src/index.ts) | 插件入口:`SystemPrompt` 服务、配置、组装流水线、`renderPrompt` |
107
+
108
+ ### 组装与渲染
109
+
110
+ 组装分两阶段完成求值与渲染:`assemble()` 返回文本已求值但尚未插值的段、有序工具 schema,以及每个已注册变量按当前上下文求得的值;`renderPrompt()` 对未设置 `interpolate: false` 的段插值 `{{variable}}` 引用、删除空段并用空行连接——严格规则:未知引用、已注册但无值的引用或格式错误的完整组都会抛出,因为格式错误的提示词比明确失败更糟。`toolOrder` 在 waterfall 分发前规范化收集到的工具(注册顺序只是插件加载产物);修改列表的 waterfall 监听器对其输出的确定性负责。
111
+
112
+ ### 作用域
113
+
114
+ 带作用域的段、变量与工具提供方会为单个 agent 遮蔽全局项,组装 waterfall 按作用域筛选分发。注册表变更通知(`system-prompt/change`)刻意不经过筛选,因为全局变更影响每个作用域。
115
+
116
+ </details>
117
+
118
+ -----
119
+
120
+ <a id="further-exploration"></a>
121
+ ## 进一步探索
122
+
123
+ 包级约定对大多数消费方已经足够;需要周边领域时再阅读以下页面。
124
+
125
+ - [系统提示词子系统](../../../docs/subsystems/system-prompt.zh.md)——确切的跨包类型与生成的服务 API。
126
+ - [tools 包](../tools/README.zh.md)——其 schema 流入组装的工具注册表。
127
+ - [提示词变量 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md)——哪些提示词事实归谁所有。
128
+ - [第一方提示词顺序 Agent Note](../../../.agents/notes/archived/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md)——稀疏具名顺序分配。
129
+ - [core 分组地图](../README.zh.md)——core 各包如何组合。
130
+
131
+ -----
132
+
133
+ <a id="model-experience"></a>
134
+ ## 模型体验
135
+
136
+ ### 系统提示词
137
+
138
+ #### 模型看到什么
139
+
140
+ 第一方段落依次渲染 harness 身份、部署 persona 前缀(含模型名称介绍)、可复用指令(包括生成的工具 SDK 和结构化输出指导),最后是携带环境信息的后缀:harness 源码(`10000`)、Web 表层(`10100`)和部署 persona 后缀(`10200`)。外部段落的顺序与组装监听器仍决定其最终结果。`includeHarnessIdentity: false` 仅省略这个固定开场白。空段会消失;带作用域的段与变量可以为一个 agent 遮蔽全局项。`system-prompt/assemble` waterfall 决定交付的提示词与工具 schema,除非一个有效段声明自身为 complete——此时该确切段会成为完整的系统提示词,而 waterfall 得到的上下文、工具与变量保持不变。渲染后的提示词作为派生历史中的 system 角色消息——surface 第 0 号节点,或历史内更新之后最新的系统节点——到达模型;循环请求与 `request/header` 均不含单独的 `system` 字段。完整渲染结果为空时,循环通过有日志记录的空内容替换清除所有生效的系统节点,模型历史不再保留任何旧提示词。有序动态上下文与段分离,只在存在时才会成为带来源的 user 角色快照;`includeRuntimeContext: false` 或带作用域的抑制器会移除可选贡献;必需的操作上下文仍然保留。
141
+
142
+ ##### harness 身份
143
+
144
+ ```markdown
145
+ You are an AI agent powered by DeepSeek Harness.
146
+ ```
147
+
148
+ #### Token 影响
149
+
150
+ 启用时,身份是每次请求的固定成本。Persona 前缀、后缀与插件文本在每次请求中重复,成本随渲染内容增长。
151
+
152
+ #### KV Cache 影响
153
+
154
+ 只要身份、persona、变量、段文本与顺序的渲染完全相同,前缀就保持稳定:渲染未变时系统节点保持不动,除非不支持该能力的路由或新请求序列必须归并保留的历史内提示词。没有 `systemPromptUpdate` 时,非空提示词文本通过有日志记录的逐节点替换归并到首个系统节点,因此头节点重写会从首个变化的 token 起失去前缀复用;当已准备调用声明 `systemPromptUpdate: 'in-history'` 时,agent loop(智能体循环)会在同一请求序列延续期间把变化后的非空提示词追加到已缓存历史之后,因此直到该历史末尾的前缀仍可复用([决策规则](../agent-loop/README.zh.md#understand-the-implementation))。模型、persona 前缀、工具与前置指令一致时,不同源码路径、本地 Web URL 或 persona 后缀值不会改变可复用的第一方前缀。persona 前缀变化可能改变靠前的前缀。任何变更都可能从第一个变化的 token 起使复用失效;不保证提供方共享缓存或实际命中率。
155
+
156
+ ### 工具 schema
157
+
158
+ #### 模型看到什么
159
+
160
+ 对于已交付工具,模型会收到[生成工具 schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tools)中对每个 agent 可见的子集;限制与组装拦截完成后,按配置或字典序排列。扩展可以通过同一注册表贡献其他定义。段与 schema 提供方是独立的组装输入。限制不会移除段落注册:工具指导插件通过 `text({ scope })` 与 `ctx.tools.get(name, scope)` 返回空文本或选择适用片段。任意静态段落不会被自动改写。
161
+
162
+ #### Token 影响
163
+
164
+ schema token 在每次请求中重复。限制工具会为该 agent 移除其全部 schema 成本,但不会移除独立提示词段;重排序会改变缓存形状,但不改变语义内容。
165
+
166
+ #### KV Cache 影响
167
+
168
+ 只要可见 schema 集合、渲染与顺序不变,前缀就保持稳定。注册、限制或重排序可能从第一个变化的 schema token 起使复用失效。
169
+
170
+ ## 已知限制与延期工作
171
+
172
+ <a id="known-limitations-and-deferred-work"></a>
173
+
174
+
175
+ 这些限制说明提示词组装何时需要特别留意。它们是当前包约束,不是待办事项清单。
176
+
177
+ - **部署方编写的提示词文本只来自配置/组合**:此插件拥有全局 persona 前缀与后缀默认值;创建方插件可以注册 agent 作用域的遮蔽项;其他段来自拥有相应事实的插件。不存在终端用户提示词编辑 API。
178
+ - **插值文本不支持行内转义语法**:整段需要保留字面花括号时,使用 `interpolate: false`。
179
+ - **`toolOrder` 配置错误在提示词组装(首轮)时出现,而不是启动时**:只有形状违规会在配置加载时抛出。
180
+
181
+
182
+ <a id="dev-note"></a>
183
+ ### 开发备注
184
+
185
+ <details>
186
+ <summary>维护者的工作上下文——点击展开</summary>
187
+
188
+ 无。
189
+
190
+ </details>
package/lib/index.js ADDED
@@ -0,0 +1,406 @@
1
+ import { Service } from "@deepseek-ai/cordis";
2
+ import z from "@deepseek-ai/schemastery";
3
+ import { AnonymousEntries, NamedEntries, ScopedLayers, scopeTarget } from "@qlzt/dsh-scope";
4
+ //#region lib/types/index.js
5
+ /**
6
+ * Registry for ordered system sections, dynamic context, tool schemas, and prompt variables.
7
+ *
8
+ * @module @qlzt/dsh-system-prompt
9
+ */
10
+ const SECTION_ORDERS = {
11
+ HARNESS_IDENTITY: -1e3,
12
+ DEPLOYMENT_PERSONA_PREFIX: 0,
13
+ PLAN_POLICY: 500,
14
+ TEAM_POLICY: 600,
15
+ PTC_ONLY: 800,
16
+ FILE_REFERENCE: 900,
17
+ TOOL_BASH: 1e3,
18
+ TOOL_PWSH: 1010,
19
+ TOOL_READ: 1100,
20
+ TOOL_WRITE: 1200,
21
+ TOOL_EDIT: 1300,
22
+ TOOL_GLOB: 1400,
23
+ TOOL_GREP: 1500,
24
+ TOOL_JOBS: 1600,
25
+ TOOL_PTY: 1700,
26
+ TOOL_WEB_SEARCH: 2e3,
27
+ TOOL_WEB_FETCH: 2100,
28
+ TOOL_LSP: 2200,
29
+ TOOL_SESSION_QUERY: 2300,
30
+ TOOL_GOAL: 2400,
31
+ TOOL_WORKFLOW: 2600,
32
+ TOOL_RALPH: 2700,
33
+ TOOL_SUBAGENT: 2800,
34
+ TOOL_REPORT: 2900,
35
+ TOOL_COMPUTER_USE: 3e3,
36
+ MCP_SERVERS: 3100,
37
+ TOOLS_SDK: 5e3,
38
+ DELIVERABLE_FILE_REFERENCES: 9e3,
39
+ STRUCTURED_OUTPUT: 9900,
40
+ HARNESS_SOURCE: 1e4,
41
+ WEB_SURFACE: 10100,
42
+ DEPLOYMENT_PERSONA_SUFFIX: 10200
43
+ };
44
+ const CONTEXT_ORDERS = {
45
+ WORKING_DIRECTORY: 100,
46
+ SANDBOX_POLICY: 110,
47
+ APPROVAL_POLICY: 115,
48
+ SUBAGENT_DELEGATION: 120
49
+ };
50
+ /**
51
+ * The deployment persona prefix's section name. Exported because a
52
+ * composition can replace this slot — an agent preset shadows the
53
+ * deployment's persona with its own — and both sides naming the same section
54
+ * is what makes the replacement work rather than duplicate.
55
+ */
56
+ const PERSONA_PREFIX_SECTION = "deployment:persona-prefix";
57
+ /** Deployment persona suffix section name shared by global and scoped contributions. */
58
+ const PERSONA_SUFFIX_SECTION = "deployment:persona-suffix";
59
+ /** Valid variable names: how they are written between the braces. */
60
+ const VARIABLE_NAME = /^[a-z][a-z0-9_]*$/;
61
+ /** A complete `{{...}}` reference group at the scan position (validated after). */
62
+ const GROUP_AT = /^\{\{([^{}]*)\}\}/;
63
+ /** Reserved {@link Config.toolOrder} marker for unlisted tools. */
64
+ const TOOL_ORDER_REST = "<unlisted-tools>";
65
+ /**
66
+ * Validate duplicate names and the required {@link TOOL_ORDER_REST} marker.
67
+ * Registered names are checked later because plugins have not loaded yet.
68
+ */
69
+ function validateToolOrder(toolOrder) {
70
+ if (toolOrder === void 0) return void 0;
71
+ const seen = /* @__PURE__ */ new Set();
72
+ for (const name of toolOrder) {
73
+ if (seen.has(name)) throw new Error(`toolOrder lists "${name}" more than once`);
74
+ seen.add(name);
75
+ }
76
+ if (!seen.has("<unlisted-tools>")) throw new Error(`toolOrder must contain the "${TOOL_ORDER_REST}" rest entry (where unlisted tools are inserted)`);
77
+ return toolOrder;
78
+ }
79
+ /**
80
+ * Apply configured tool order, inserting unlisted tools lexicographically at
81
+ * {@link TOOL_ORDER_REST}. Unknown configured names fail; known but restricted
82
+ * names may be absent.
83
+ */
84
+ function orderTools(tools, toolOrder, knownNames) {
85
+ if (tools.find((tool) => tool.name === "<unlisted-tools>") !== void 0) throw new Error(`tool provider returned reserved tool name "${TOOL_ORDER_REST}" (reserved for toolOrder's rest entry)`);
86
+ if (toolOrder === void 0) return tools.sort(compareToolNames);
87
+ const unknown = toolOrder.filter((name) => name !== "<unlisted-tools>" && !knownNames.has(name));
88
+ if (unknown.length > 0) throw new Error(`toolOrder lists unregistered tool${unknown.length > 1 ? "s" : ""} ${unknown.map((name) => `"${name}"`).join(", ")}; known tools: ${[...knownNames].sort().join(", ") || "(none)"}`);
89
+ const listed = new Set(toolOrder);
90
+ const rest = tools.filter((tool) => !listed.has(tool.name)).sort(compareToolNames);
91
+ return toolOrder.flatMap((name) => name === "<unlisted-tools>" ? rest : tools.filter((tool) => tool.name === name));
92
+ }
93
+ /** Code-unit name comparison — locale-independent, so the order is identical on every machine. */
94
+ function compareNames(a, b) {
95
+ return a < b ? -1 : a > b ? 1 : 0;
96
+ }
97
+ /** Order prompt sections by their explicit placement, then deterministically by name. */
98
+ function comparePromptSections(a, b) {
99
+ return a.order - b.order || compareNames(a.name, b.name);
100
+ }
101
+ /** Order tool schemas lexicographically by name. */
102
+ function compareToolNames(a, b) {
103
+ return compareNames(a.name, b.name);
104
+ }
105
+ /**
106
+ * Interpolate strict `{{variable}}` references, drop empty sections, and join
107
+ * the rest with blank lines. Sections with `interpolate: false` retain literal
108
+ * text. Malformed, unknown, or undefined references in other sections throw;
109
+ * a lone `{{` without any later `}}` is literal prose, and substituted values
110
+ * are not scanned again.
111
+ * @param assembly - the assembly whose sections and variables to render.
112
+ * @returns the rendered prompt, or `''` when all sections are empty.
113
+ */
114
+ function renderPrompt(assembly) {
115
+ return assembly.sections.map((section) => section.interpolate === false ? section.text : interpolate(section, assembly.variables, "section")).filter((text) => text.length > 0).join("\n\n");
116
+ }
117
+ /**
118
+ * Render the complete dynamic context snapshot, preserving literal contributions.
119
+ * @param assembly - the assembly whose contexts and variables to render.
120
+ * @returns the current full snapshot, or `''` when no context is active.
121
+ */
122
+ function renderContextSnapshot(assembly) {
123
+ return joinContextSections(renderContextSections(assembly));
124
+ }
125
+ /**
126
+ * The model-facing snapshot text for an already-rendered section list.
127
+ *
128
+ * A caller that also needs the sections renders them once and joins here, so a
129
+ * request does not interpolate every context twice.
130
+ * @param sections - sections from {@link renderContextSections}.
131
+ * @returns the current full snapshot, or `''` when no context is active.
132
+ */
133
+ function joinContextSections(sections) {
134
+ const body = sections.map((section) => section.text).join("\n\n");
135
+ if (body.length === 0) return "";
136
+ return `Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\n${body}`;
137
+ }
138
+ /**
139
+ * The same snapshot, kept as the named contributions it was assembled from.
140
+ *
141
+ * {@link renderContextSnapshot} joins these for the model; a consumer that
142
+ * presents the snapshot uses them to attribute each part to the subsystem that
143
+ * contributed it, without re-splitting the joined prose. Contributions with
144
+ * `interpolate: false` preserve literal text instead of resolving prompt variables.
145
+ * @param assembly - the assembly whose contexts and variables to render.
146
+ * @returns one entry per contributing context that rendered to non-empty text.
147
+ */
148
+ function renderContextSections(assembly) {
149
+ return assembly.contexts.map((context) => ({
150
+ name: context.name,
151
+ text: context.interpolate === false ? context.text : interpolate(context, assembly.variables, "context")
152
+ })).filter((section) => section.text.length > 0);
153
+ }
154
+ /** Interpolate one section or context and attribute diagnostics to its owning input. */
155
+ function interpolate(input, variables, kind) {
156
+ const text = input.text;
157
+ let result = "";
158
+ let last = 0;
159
+ for (let open = text.indexOf("{{"); open >= 0; open = text.indexOf("{{", last)) {
160
+ const group = GROUP_AT.exec(text.slice(open));
161
+ if (group === null) {
162
+ if (text.indexOf("}}", open + 2) >= 0) throw new Error(`malformed prompt variable reference at "${text.slice(open, open + 16)}…" in ${kind} "${input.name}" (references are complete simple {{name}} groups)`);
163
+ result += text.slice(last, open + 2);
164
+ last = open + 2;
165
+ continue;
166
+ }
167
+ const name = group[0].slice(2, -2);
168
+ if (!VARIABLE_NAME.test(name)) throw new Error(`malformed prompt variable reference "{{${name}}}" in ${kind} "${input.name}" (variable names match ${String(VARIABLE_NAME)})`);
169
+ if (!Object.hasOwn(variables, name)) {
170
+ const known = Object.keys(variables);
171
+ throw new Error(`unknown prompt variable "{{${name}}}" in ${kind} "${input.name}"; registered variables: ${known.length > 0 ? known.join(", ") : "(none)"}`);
172
+ }
173
+ const value = variables[name];
174
+ if (value === void 0) throw new Error(`prompt variable "{{${name}}}" has no value for this assembly (${kind} "${input.name}")`);
175
+ result += text.slice(last, open) + value;
176
+ last = open + group[0].length;
177
+ }
178
+ return result + text.slice(last);
179
+ }
180
+ /** Resolve one registered runtime fact without assembling sections or tools. */
181
+ function resolveContext(entry, context) {
182
+ return {
183
+ name: entry.name,
184
+ text: typeof entry.text === "function" ? entry.text(context) : entry.text,
185
+ ...entry.interpolate !== void 0 ? { interpolate: entry.interpolate } : {}
186
+ };
187
+ }
188
+ /** All prompt registrations owned by one global or scoped layer. */
189
+ var PromptLayer = class {
190
+ sections;
191
+ contexts;
192
+ runtimeContextSuppressors = new AnonymousEntries();
193
+ toolProviders = new AnonymousEntries();
194
+ variables;
195
+ /**
196
+ * Create one prompt layer with diagnostics specific to its ownership scope.
197
+ * @param scope - the scoped owner, or `undefined` for global registrations.
198
+ */
199
+ constructor(scope) {
200
+ this.sections = new NamedEntries((name) => /* @__PURE__ */ new Error(scope === void 0 ? `prompt section "${name}" is already registered (for a per-agent override, register through that agent's \`agent.ctx\` instead)` : `prompt section "${name}" is already registered in this scope`));
201
+ this.contexts = new NamedEntries((name) => /* @__PURE__ */ new Error(scope === void 0 ? `prompt context "${name}" is already registered (for a per-agent override, register through that agent's \`agent.ctx\` instead)` : `prompt context "${name}" is already registered in this scope`));
202
+ this.variables = new NamedEntries((name) => /* @__PURE__ */ new Error(scope === void 0 ? `prompt variable "${name}" is already registered (for a per-agent value, register through that agent's \`agent.ctx\` instead)` : `prompt variable "${name}" is already registered in this scope`));
203
+ }
204
+ /** @returns whether this layer owns no prompt registrations. */
205
+ isEmpty() {
206
+ return this.sections.isEmpty() && this.contexts.isEmpty() && this.runtimeContextSuppressors.isEmpty() && this.toolProviders.isEmpty() && this.variables.isEmpty();
207
+ }
208
+ };
209
+ /** Registry service for the prompt inputs assembled before each model step. */
210
+ var SystemPrompt = class extends Service {
211
+ static Config = z.object({
212
+ includeHarnessIdentity: z.boolean().default(true),
213
+ includeRuntimeContext: z.boolean().default(true),
214
+ personaPrefix: z.string().default(""),
215
+ personaSuffix: z.string().default(""),
216
+ toolOrder: z.array(z.string()).default(void 0)
217
+ });
218
+ layers = new ScopedLayers((scope) => new PromptLayer(scope), () => {
219
+ this.ctx.emit("system-prompt/change");
220
+ });
221
+ toolOrder;
222
+ constructor(ctx, config) {
223
+ super(ctx, "systemPrompt");
224
+ this.toolOrder = validateToolOrder(config.toolOrder);
225
+ if (config.includeHarnessIdentity ?? true) this.section({
226
+ name: "harness:identity",
227
+ order: this.getSectionOrder("HARNESS_IDENTITY"),
228
+ text: "You are an AI agent powered by DeepSeek Harness."
229
+ });
230
+ this.section({
231
+ name: PERSONA_PREFIX_SECTION,
232
+ order: this.getSectionOrder("DEPLOYMENT_PERSONA_PREFIX"),
233
+ text: config.personaPrefix ?? ""
234
+ });
235
+ this.section({
236
+ name: PERSONA_SUFFIX_SECTION,
237
+ order: this.getSectionOrder("DEPLOYMENT_PERSONA_SUFFIX"),
238
+ text: config.personaSuffix ?? ""
239
+ });
240
+ if (!(config.includeRuntimeContext ?? true)) this.suppressRuntimeContext();
241
+ }
242
+ /**
243
+ * Register an ordered prompt section in the calling context's scope. A scoped
244
+ * section shadows a global section with the same name; duplicates within one
245
+ * layer and non-finite orders throw. Registration and disposal emit
246
+ * `system-prompt/change`.
247
+ * @param section - the section to register.
248
+ * @returns the exact Cordis effect disposer.
249
+ */
250
+ section(section) {
251
+ if (!Number.isFinite(section.order)) throw new TypeError(`prompt section "${section.name}" order must be a finite number`);
252
+ return this.layers.effect(this.ctx, (layer) => layer.sections.insert(section.name, section), { label: "systemPrompt.section()" });
253
+ }
254
+ /**
255
+ * Resolve the centrally owned placement of a repository prompt section.
256
+ * @param name - stable section placement name.
257
+ * @returns the section's numeric sort order.
258
+ */
259
+ getSectionOrder(name) {
260
+ return SECTION_ORDERS[name];
261
+ }
262
+ /**
263
+ * Resolve the centrally owned placement of a repository runtime context.
264
+ * @param name - stable context placement name.
265
+ * @returns the context's numeric sort order.
266
+ */
267
+ getContextOrder(name) {
268
+ return CONTEXT_ORDERS[name];
269
+ }
270
+ /**
271
+ * Register ordered dynamic context in the calling context's scope. Scoped
272
+ * entries shadow global entries with the same name.
273
+ * @param context - the context contribution to register.
274
+ * @returns the exact Cordis effect disposer.
275
+ */
276
+ context(context) {
277
+ if (!Number.isFinite(context.order)) throw new TypeError(`prompt context "${context.name}" order must be a finite number`);
278
+ return this.layers.effect(this.ctx, (layer) => layer.contexts.insert(context.name, context), { label: "systemPrompt.context()" });
279
+ }
280
+ /**
281
+ * Suppress optional dynamic runtime-context contributions in the calling
282
+ * context's scope without changing the services that own or enforce those
283
+ * facts. Multiple suppressors remain independently disposable.
284
+ * @returns the exact Cordis effect disposer.
285
+ */
286
+ suppressRuntimeContext() {
287
+ return this.layers.effect(this.ctx, (layer) => layer.runtimeContextSuppressors.append(true), { label: "systemPrompt.suppressRuntimeContext()" });
288
+ }
289
+ /**
290
+ * Register a tool-schema provider in the calling context's scope. Global and
291
+ * matching scoped providers both contribute; returning the reserved
292
+ * {@link TOOL_ORDER_REST} name makes assembly fail.
293
+ * @param provider - evaluated for each assembly with its context.
294
+ * @returns the exact Cordis effect disposer.
295
+ */
296
+ tools(provider) {
297
+ return this.layers.effect(this.ctx, (layer) => layer.toolProviders.append(provider), { label: "systemPrompt.tools()" });
298
+ }
299
+ /**
300
+ * Register a prompt variable in the calling context's scope. Scoped values
301
+ * shadow globals; invalid or duplicate names throw. A provider may return
302
+ * `undefined`, but rendering a section that references that value then fails.
303
+ * @param name - the `[a-z][a-z0-9_]*` reference name.
304
+ * @param provider - evaluated for each assembly.
305
+ * @returns the exact Cordis effect disposer.
306
+ */
307
+ variable(name, provider) {
308
+ if (!VARIABLE_NAME.test(name)) throw new Error(`invalid prompt variable name "${name}" (must match ${String(VARIABLE_NAME)})`);
309
+ return this.layers.effect(this.ctx, (layer) => layer.variables.insert(name, provider), { label: "systemPrompt.variable()" });
310
+ }
311
+ /**
312
+ * Refresh accepted registered runtime facts for request admission. Contexts
313
+ * added only by the assembly waterfall retain their accepted values. Current
314
+ * suppression removes optional contexts, and missing required registrations
315
+ * are restored in registry order. Sections, tools, and interpolation variables
316
+ * retain the accepted assembly; their providers and waterfall do not rerun.
317
+ * @param assembly - accepted assembly for this scope and step.
318
+ * @param context - the same scope and current plugin-defined assembly fields.
319
+ * @returns the accepted assembly with current runtime-context provider text.
320
+ */
321
+ refreshContext(assembly, context = {}) {
322
+ const providers = this.layers.merge(context.scope, (layer) => layer.contexts);
323
+ const suppressed = !this.layers.global.runtimeContextSuppressors.isEmpty() || this.layers.chainLayers(context.scope).some((layer) => !layer.runtimeContextSuppressors.isEmpty());
324
+ const required = new Map([...providers].filter(([, entry]) => entry.required === true));
325
+ const contexts = [];
326
+ for (const accepted of assembly.contexts) {
327
+ const provider = providers.get(accepted.name);
328
+ required.delete(accepted.name);
329
+ if (suppressed && provider?.required !== true) continue;
330
+ contexts.push(provider === void 0 ? accepted : resolveContext(provider, context));
331
+ }
332
+ for (const provider of required.values()) {
333
+ const index = contexts.findIndex((entry) => {
334
+ const next = providers.get(entry.name);
335
+ return next !== void 0 && next.order > provider.order;
336
+ });
337
+ contexts.splice(index < 0 ? contexts.length : index, 0, resolveContext(provider, context));
338
+ }
339
+ return {
340
+ ...assembly,
341
+ contexts
342
+ };
343
+ }
344
+ /**
345
+ * Assemble global and scoped providers, detach tool parameters, apply
346
+ * canonical ordering, then run the assembly waterfall. Scoped sections and
347
+ * variables shadow globals. The returned waterfall value is authoritative
348
+ * except that an effective complete section is restored afterwards as the
349
+ * sole prompt section.
350
+ * @param context - the optional scope and plugin-defined assembly fields.
351
+ * @returns the post-waterfall assembly with any complete prompt enforced.
352
+ */
353
+ async assemble(context = {}) {
354
+ const scope = context.scope;
355
+ const scopeLayers = this.layers.chainLayers(scope);
356
+ const runtimeContextSuppressed = !this.layers.global.runtimeContextSuppressors.isEmpty() || scopeLayers.some((layer) => !layer.runtimeContextSuppressors.isEmpty());
357
+ const variables = {};
358
+ for (const [name, provider] of this.layers.global.variables.entries()) variables[name] = provider(context);
359
+ for (const layer of scopeLayers) for (const [name, provider] of layer.variables.entries()) variables[name] = provider(context);
360
+ const sectionByName = this.layers.merge(scope, (layer) => layer.sections);
361
+ const contextByName = this.layers.merge(scope, (layer) => layer.contexts);
362
+ const providers = [...this.layers.global.toolProviders.values(), ...scopeLayers.flatMap((layer) => [...layer.toolProviders.values()])];
363
+ const collected = [];
364
+ const knownNames = /* @__PURE__ */ new Set();
365
+ for (const provider of providers) {
366
+ const result = provider(context);
367
+ const schemas = result.schemas.map(({ name, description, parameters, deferLoading }) => ({
368
+ name,
369
+ description,
370
+ parameters: structuredClone(parameters),
371
+ ...deferLoading === true ? { deferLoading } : {}
372
+ }));
373
+ const acceptedKnownNames = result.knownNames ?? schemas.map((tool) => tool.name);
374
+ collected.push(...schemas);
375
+ for (const name of acceptedKnownNames) knownNames.add(name);
376
+ }
377
+ const sectionDefinitions = [...sectionByName.values()].sort(comparePromptSections);
378
+ const completeSections = sectionDefinitions.filter((section) => section.complete === true);
379
+ if (completeSections.length > 1) throw new Error(`multiple complete prompt sections are active: ${completeSections.map((section) => JSON.stringify(section.name)).join(", ")}`);
380
+ let completeSection;
381
+ const assembly = {
382
+ sections: sectionDefinitions.map((section) => {
383
+ const assembled = {
384
+ name: section.name,
385
+ text: typeof section.text === "function" ? section.text(context) : section.text,
386
+ ...section.interpolate !== void 0 ? { interpolate: section.interpolate } : {}
387
+ };
388
+ if (section.complete === true) completeSection = { ...assembled };
389
+ return assembled;
390
+ }),
391
+ contexts: [...contextByName.values()].filter((entry) => !runtimeContextSuppressed || entry.required === true).sort((a, b) => a.order - b.order).map((entry) => resolveContext(entry, context)),
392
+ tools: orderTools(collected, this.toolOrder, knownNames),
393
+ variables
394
+ };
395
+ const transformed = await this.ctx.waterfall(scopeTarget(this, scope), "system-prompt/assemble", assembly, context, () => Promise.resolve(assembly));
396
+ if (completeSection === void 0 && !runtimeContextSuppressed) return transformed;
397
+ const requiredContexts = new Set([...contextByName.values()].filter((entry) => entry.required === true).map((entry) => entry.name));
398
+ return {
399
+ ...transformed,
400
+ sections: completeSection === void 0 ? transformed.sections : [completeSection],
401
+ contexts: runtimeContextSuppressed ? transformed.contexts.filter((entry) => requiredContexts.has(entry.name)) : transformed.contexts
402
+ };
403
+ }
404
+ };
405
+ //#endregion
406
+ export { PERSONA_PREFIX_SECTION, PERSONA_SUFFIX_SECTION, SystemPrompt, SystemPrompt as default, TOOL_ORDER_REST, joinContextSections, renderContextSections, renderContextSnapshot, renderPrompt };
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Registry for ordered system sections, dynamic context, tool schemas, and prompt variables.
3
+ *
4
+ * @module @qlzt/dsh-system-prompt
5
+ */
6
+ import { Context, Service } from '@deepseek-ai/cordis';
7
+ import z from '@deepseek-ai/schemastery';
8
+ import type { ScopeKey, Scoped } from '@qlzt/dsh-scope';
9
+ import type { ContextSnapshotSection, ToolSchema } from '@qlzt/dsh-llm';
10
+ declare module '@deepseek-ai/cordis' {
11
+ interface Context {
12
+ systemPrompt: SystemPrompt;
13
+ }
14
+ interface Events {
15
+ /**
16
+ * Expert waterfall over the assembled sections, contexts, tools, and variables.
17
+ * Scope-filtered dispatch (`@qlzt/dsh-scope`): scoped listeners
18
+ * receive only that scope's assemblies. The returned value is authoritative.
19
+ * A supplied signal controls only this explicit assembly request and must not
20
+ * be retained to control later turns. A registered complete section is
21
+ * restored after this waterfall, so listeners cannot add to or replace
22
+ * that scope's system prompt.
23
+ * @param assembly - the mutable assembly built from registered providers.
24
+ * @param context - the caller's per-assembly context.
25
+ * @mode waterfall
26
+ */
27
+ 'system-prompt/assemble'(this: Scoped<SystemPrompt>, assembly: PromptAssembly, context: AssembleContext, next: () => Promise<PromptAssembly>): Promise<PromptAssembly>;
28
+ /**
29
+ * Emitted when any prompt provider changes. This registry notification is
30
+ * unfiltered because a global change affects every scope.
31
+ * @mode emit
32
+ */
33
+ 'system-prompt/change'(): void;
34
+ }
35
+ }
36
+ /** Merge-extensible context for one prompt assembly. */
37
+ export interface AssembleContext {
38
+ /**
39
+ * Scope whose providers and waterfall listeners participate. When absent,
40
+ * only global providers and subject-less listeners participate.
41
+ */
42
+ scope?: ScopeKey;
43
+ /** Explicit control signal for the turn that requested this assembly, when any. */
44
+ signal?: AbortSignal;
45
+ }
46
+ /** One contributed section of the system prompt (registry input). */
47
+ export interface PromptSection {
48
+ /** Unique name — a duplicate registration throws (see {@link SystemPrompt.section}). */
49
+ readonly name: string;
50
+ /**
51
+ * Sections are concatenated in ascending order. Equal orders use code-unit
52
+ * name order.
53
+ */
54
+ readonly order: number;
55
+ /**
56
+ * Static text or a provider evaluated at each assembly with that assembly's
57
+ * {@link AssembleContext}. The text may reference `{{variable}}`s — they are
58
+ * interpolated later, by {@link renderPrompt}, unless `interpolate` is false.
59
+ */
60
+ readonly text: string | ((context: AssembleContext) => string);
61
+ /** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
62
+ readonly interpolate?: boolean;
63
+ /**
64
+ * Treat this contribution as the complete system prompt. Assembly still
65
+ * runs the cooperative waterfall so tools, contexts, and variables can be
66
+ * resolved, then restores this exact section as the sole prompt section.
67
+ * More than one effective complete section makes assembly fail.
68
+ */
69
+ readonly complete?: boolean;
70
+ }
71
+ /** Dynamic model context materialized as a durable user-role snapshot. */
72
+ export interface PromptContext {
73
+ /** Unique name — a duplicate registration throws (see {@link SystemPrompt.context}). */
74
+ readonly name: string;
75
+ /** Contexts are joined in ascending order. */
76
+ readonly order: number;
77
+ /** Static text or a provider evaluated at assembly and admission refresh. Empty text contributes nothing. */
78
+ readonly text: string | ((context: AssembleContext) => string);
79
+ /** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
80
+ readonly interpolate?: boolean;
81
+ /** Keep this operational context when optional runtime context is disabled. */
82
+ readonly required?: boolean;
83
+ }
84
+ /** One section of an assembly: {@link PromptSection} with its text resolved. */
85
+ export interface AssembledSection {
86
+ /** The contributing section's unique name. */
87
+ name: string;
88
+ /** The resolved (but not yet interpolated) section text. */
89
+ text: string;
90
+ /** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
91
+ interpolate?: boolean;
92
+ }
93
+ /** One resolved dynamic context contribution. */
94
+ export interface AssembledContext {
95
+ /** The contributing context's unique name. */
96
+ name: string;
97
+ /** The resolved text before variable interpolation. */
98
+ text: string;
99
+ /** Whether to interpolate prompt variables. Defaults to true; false preserves literal text. */
100
+ interpolate?: boolean;
101
+ }
102
+ /** Tool schemas visible in one assembly and their pre-restriction name set. */
103
+ export interface ToolProviderResult {
104
+ /** The schemas this provider contributes to THIS assembly. */
105
+ readonly schemas: readonly ToolSchema[];
106
+ /** The pre-restriction name universe for config validation (defaults to `schemas`' names). */
107
+ readonly knownNames?: readonly string[];
108
+ }
109
+ /**
110
+ * Merge-extensible assembled model input. Sections and contexts remain
111
+ * uninterpolated until rendered; tools are already in canonical order.
112
+ */
113
+ export interface PromptAssembly {
114
+ sections: AssembledSection[];
115
+ contexts: AssembledContext[];
116
+ tools: ToolSchema[];
117
+ variables: Record<string, string | undefined>;
118
+ }
119
+ declare const SECTION_ORDERS: {
120
+ readonly HARNESS_IDENTITY: -1000;
121
+ readonly DEPLOYMENT_PERSONA_PREFIX: 0;
122
+ readonly PLAN_POLICY: 500;
123
+ readonly TEAM_POLICY: 600;
124
+ readonly PTC_ONLY: 800;
125
+ readonly FILE_REFERENCE: 900;
126
+ readonly TOOL_BASH: 1000;
127
+ readonly TOOL_PWSH: 1010;
128
+ readonly TOOL_READ: 1100;
129
+ readonly TOOL_WRITE: 1200;
130
+ readonly TOOL_EDIT: 1300;
131
+ readonly TOOL_GLOB: 1400;
132
+ readonly TOOL_GREP: 1500;
133
+ readonly TOOL_JOBS: 1600;
134
+ readonly TOOL_PTY: 1700;
135
+ readonly TOOL_WEB_SEARCH: 2000;
136
+ readonly TOOL_WEB_FETCH: 2100;
137
+ readonly TOOL_LSP: 2200;
138
+ readonly TOOL_SESSION_QUERY: 2300;
139
+ readonly TOOL_GOAL: 2400;
140
+ readonly TOOL_WORKFLOW: 2600;
141
+ readonly TOOL_RALPH: 2700;
142
+ readonly TOOL_SUBAGENT: 2800;
143
+ readonly TOOL_REPORT: 2900;
144
+ readonly TOOL_COMPUTER_USE: 3000;
145
+ readonly MCP_SERVERS: 3100;
146
+ readonly TOOLS_SDK: 5000;
147
+ readonly DELIVERABLE_FILE_REFERENCES: 9000;
148
+ readonly STRUCTURED_OUTPUT: 9900;
149
+ readonly HARNESS_SOURCE: 10000;
150
+ readonly WEB_SURFACE: 10100;
151
+ readonly DEPLOYMENT_PERSONA_SUFFIX: 10200;
152
+ };
153
+ /** Name of a centrally allocated prompt-section position. */
154
+ export type PromptSectionOrderName = keyof typeof SECTION_ORDERS;
155
+ declare const CONTEXT_ORDERS: {
156
+ readonly WORKING_DIRECTORY: 100;
157
+ readonly SANDBOX_POLICY: 110;
158
+ readonly APPROVAL_POLICY: 115;
159
+ readonly SUBAGENT_DELEGATION: 120;
160
+ };
161
+ /** Name of a centrally allocated runtime-context position. */
162
+ export type PromptContextOrderName = keyof typeof CONTEXT_ORDERS;
163
+ /**
164
+ * The deployment persona prefix's section name. Exported because a
165
+ * composition can replace this slot — an agent preset shadows the
166
+ * deployment's persona with its own — and both sides naming the same section
167
+ * is what makes the replacement work rather than duplicate.
168
+ */
169
+ export declare const PERSONA_PREFIX_SECTION = "deployment:persona-prefix";
170
+ /** Deployment persona suffix section name shared by global and scoped contributions. */
171
+ export declare const PERSONA_SUFFIX_SECTION = "deployment:persona-suffix";
172
+ /** Reserved {@link Config.toolOrder} marker for unlisted tools. */
173
+ export declare const TOOL_ORDER_REST = "<unlisted-tools>";
174
+ /** Plugin config: the deployment-authored fragment of the system prompt (see {@link Config.personaPrefix} for its contract). */
175
+ export interface Config {
176
+ /** Include the fixed DeepSeek Harness identity before the deployment persona (default true). */
177
+ includeHarnessIdentity?: boolean;
178
+ /** Include optional runtime-context snapshots in model history (default true); required context remains. */
179
+ includeRuntimeContext?: boolean;
180
+ /**
181
+ * Deployment-wide persona prefix template before first-party guidance. A scoped section named
182
+ * `deployment:persona-prefix` shadows it; `{{variable}}` references are strict.
183
+ */
184
+ personaPrefix?: string;
185
+ /**
186
+ * Persona suffix template after first-party guidance. A scoped `deployment:persona-suffix`
187
+ * section shadows it; `{{variable}}` references are strict. Defaults to empty.
188
+ */
189
+ personaSuffix?: string;
190
+ /**
191
+ * Model-facing tool names in order, with {@link TOOL_ORDER_REST} exactly once.
192
+ * Invalid fields fail at load and unknown names fail at assembly; known names
193
+ * hidden in one scope may be absent there. Omitted means lexicographic order.
194
+ */
195
+ toolOrder?: string[];
196
+ }
197
+ /**
198
+ * Interpolate strict `{{variable}}` references, drop empty sections, and join
199
+ * the rest with blank lines. Sections with `interpolate: false` retain literal
200
+ * text. Malformed, unknown, or undefined references in other sections throw;
201
+ * a lone `{{` without any later `}}` is literal prose, and substituted values
202
+ * are not scanned again.
203
+ * @param assembly - the assembly whose sections and variables to render.
204
+ * @returns the rendered prompt, or `''` when all sections are empty.
205
+ */
206
+ export declare function renderPrompt(assembly: PromptAssembly): string;
207
+ /**
208
+ * Render the complete dynamic context snapshot, preserving literal contributions.
209
+ * @param assembly - the assembly whose contexts and variables to render.
210
+ * @returns the current full snapshot, or `''` when no context is active.
211
+ */
212
+ export declare function renderContextSnapshot(assembly: PromptAssembly): string;
213
+ /**
214
+ * The model-facing snapshot text for an already-rendered section list.
215
+ *
216
+ * A caller that also needs the sections renders them once and joins here, so a
217
+ * request does not interpolate every context twice.
218
+ * @param sections - sections from {@link renderContextSections}.
219
+ * @returns the current full snapshot, or `''` when no context is active.
220
+ */
221
+ export declare function joinContextSections(sections: readonly ContextSnapshotSection[]): string;
222
+ /**
223
+ * The same snapshot, kept as the named contributions it was assembled from.
224
+ *
225
+ * {@link renderContextSnapshot} joins these for the model; a consumer that
226
+ * presents the snapshot uses them to attribute each part to the subsystem that
227
+ * contributed it, without re-splitting the joined prose. Contributions with
228
+ * `interpolate: false` preserve literal text instead of resolving prompt variables.
229
+ * @param assembly - the assembly whose contexts and variables to render.
230
+ * @returns one entry per contributing context that rendered to non-empty text.
231
+ */
232
+ export declare function renderContextSections(assembly: PromptAssembly): ContextSnapshotSection[];
233
+ /** Registry service for the prompt inputs assembled before each model step. */
234
+ export declare class SystemPrompt extends Service {
235
+ static Config: z<Config>;
236
+ private readonly layers;
237
+ private readonly toolOrder;
238
+ constructor(ctx: Context, config: Config);
239
+ /**
240
+ * Register an ordered prompt section in the calling context's scope. A scoped
241
+ * section shadows a global section with the same name; duplicates within one
242
+ * layer and non-finite orders throw. Registration and disposal emit
243
+ * `system-prompt/change`.
244
+ * @param section - the section to register.
245
+ * @returns the exact Cordis effect disposer.
246
+ */
247
+ section(section: PromptSection): () => void;
248
+ /**
249
+ * Resolve the centrally owned placement of a repository prompt section.
250
+ * @param name - stable section placement name.
251
+ * @returns the section's numeric sort order.
252
+ */
253
+ getSectionOrder(name: PromptSectionOrderName): number;
254
+ /**
255
+ * Resolve the centrally owned placement of a repository runtime context.
256
+ * @param name - stable context placement name.
257
+ * @returns the context's numeric sort order.
258
+ */
259
+ getContextOrder(name: PromptContextOrderName): number;
260
+ /**
261
+ * Register ordered dynamic context in the calling context's scope. Scoped
262
+ * entries shadow global entries with the same name.
263
+ * @param context - the context contribution to register.
264
+ * @returns the exact Cordis effect disposer.
265
+ */
266
+ context(context: PromptContext): () => void;
267
+ /**
268
+ * Suppress optional dynamic runtime-context contributions in the calling
269
+ * context's scope without changing the services that own or enforce those
270
+ * facts. Multiple suppressors remain independently disposable.
271
+ * @returns the exact Cordis effect disposer.
272
+ */
273
+ suppressRuntimeContext(): () => void;
274
+ /**
275
+ * Register a tool-schema provider in the calling context's scope. Global and
276
+ * matching scoped providers both contribute; returning the reserved
277
+ * {@link TOOL_ORDER_REST} name makes assembly fail.
278
+ * @param provider - evaluated for each assembly with its context.
279
+ * @returns the exact Cordis effect disposer.
280
+ */
281
+ tools(provider: (context: AssembleContext) => ToolProviderResult): () => void;
282
+ /**
283
+ * Register a prompt variable in the calling context's scope. Scoped values
284
+ * shadow globals; invalid or duplicate names throw. A provider may return
285
+ * `undefined`, but rendering a section that references that value then fails.
286
+ * @param name - the `[a-z][a-z0-9_]*` reference name.
287
+ * @param provider - evaluated for each assembly.
288
+ * @returns the exact Cordis effect disposer.
289
+ */
290
+ variable(name: string, provider: (context: AssembleContext) => string | undefined): () => void;
291
+ /**
292
+ * Refresh accepted registered runtime facts for request admission. Contexts
293
+ * added only by the assembly waterfall retain their accepted values. Current
294
+ * suppression removes optional contexts, and missing required registrations
295
+ * are restored in registry order. Sections, tools, and interpolation variables
296
+ * retain the accepted assembly; their providers and waterfall do not rerun.
297
+ * @param assembly - accepted assembly for this scope and step.
298
+ * @param context - the same scope and current plugin-defined assembly fields.
299
+ * @returns the accepted assembly with current runtime-context provider text.
300
+ */
301
+ refreshContext(assembly: PromptAssembly, context?: AssembleContext): PromptAssembly;
302
+ /**
303
+ * Assemble global and scoped providers, detach tool parameters, apply
304
+ * canonical ordering, then run the assembly waterfall. Scoped sections and
305
+ * variables shadow globals. The returned waterfall value is authoritative
306
+ * except that an effective complete section is restored afterwards as the
307
+ * sole prompt section.
308
+ * @param context - the optional scope and plugin-defined assembly fields.
309
+ * @returns the post-waterfall assembly with any complete prompt enforced.
310
+ */
311
+ assemble(context?: AssembleContext): Promise<PromptAssembly>;
312
+ }
313
+ export default SystemPrompt;
314
+ //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@qlzt/dsh-system-prompt",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "System prompt assembly registry for the DeepSeek Harness",
4
+ "version": "0.2.1-alpha.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://gitee.com/zsping1989/deepseek-harness.git",
11
+ "directory": "packages/core/system-prompt"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./src/*": "./src/*",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "lib/index.js",
26
+ "lib/types/**/*.d.ts"
27
+ ],
28
+ "license": "MIT",
29
+ "peerDependencies": {
30
+ "@qlzt/dsh-llm": "0.2.1-alpha.2",
31
+ "@qlzt/dsh-scope": "0.2.1-alpha.2",
32
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
33
+ },
34
+ "dependencies": {
35
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.1"
36
+ },
37
+ "devDependencies": {
38
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
39
+ "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
40
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
41
+ },
42
+ "dsh": {}
43
+ }