dsh-plugin-prompt-tool 0.4.2 → 0.6.0

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.
Files changed (191) hide show
  1. package/README.md +131 -222
  2. package/engine/anchor-match.mjs +117 -0
  3. package/engine/compaction-epoch.mjs +139 -0
  4. package/engine/compositions/library/bootstrap-filesystem.yml +19 -0
  5. package/engine/compositions/library/compaction.yml +52 -0
  6. package/engine/compositions/library/context-gate.yml +40 -0
  7. package/engine/compositions/library/custom-bash.yml +14 -0
  8. package/engine/compositions/library/delegation.yml +85 -0
  9. package/engine/compositions/library/official-agent-instructions.yml +17 -0
  10. package/engine/compositions/library/official-persistent-shell.yml +56 -0
  11. package/engine/compositions/library/official-skill-filesystem-cordis.yml +10 -0
  12. package/engine/compositions/library/official-tool-bash.yml +6 -0
  13. package/engine/compositions/library/official-tool-cordis.yml +13 -0
  14. package/engine/compositions/library/official-tool-presentation.yml +7 -0
  15. package/engine/compositions/library/official-tool-skill.yml +14 -0
  16. package/engine/compositions/library/persistent-shell.yml +36 -0
  17. package/engine/compositions/library/persona.yml +9 -0
  18. package/engine/compositions/library/planning.yml +41 -0
  19. package/engine/compositions/library/prompt-config-engine.yml +16 -0
  20. package/engine/compositions/library/run-code-env.yml +15 -0
  21. package/engine/compositions/library/skill-filesystem.yml +13 -0
  22. package/engine/compositions/library/skill-search.yml +14 -0
  23. package/engine/compositions/library/str-replace-editor.yml +10 -0
  24. package/engine/compositions/library/tool-ask-user.yml +8 -0
  25. package/engine/compositions/library/tool-bash.yml +16 -0
  26. package/engine/compositions/library/tool-bootstrap.yml +16 -0
  27. package/engine/compositions/library/tool-filter.yml +12 -0
  28. package/engine/compositions/library/tool-fs-search.yml +18 -0
  29. package/engine/compositions/library/tool-fs.yml +10 -0
  30. package/engine/compositions/library/tool-goal.yml +19 -0
  31. package/engine/compositions/library/tool-jobs.yml +23 -0
  32. package/engine/compositions/library/tool-pwsh.yml +12 -0
  33. package/engine/compositions/library/tool-todo.yml +11 -0
  34. package/engine/compositions/library/tool-web.yml +9 -0
  35. package/engine/compositions/source/local/context-gate.yml +37 -0
  36. package/engine/compositions/source/local/custom-bash.yml +11 -0
  37. package/engine/compositions/source/local/prompt-config-engine.yml +13 -0
  38. package/engine/compositions/source/local/run-code-env.yml +12 -0
  39. package/engine/compositions/source/local/skill-search.yml +11 -0
  40. package/engine/compositions/source/local/tool-bootstrap.yml +13 -0
  41. package/engine/context-gate.mjs +305 -0
  42. package/{preset → engine}/custom-bash.mjs +243 -243
  43. package/engine/executor.mjs +278 -0
  44. package/engine/fillers.mjs +273 -0
  45. package/engine/interpolate.mjs +66 -0
  46. package/engine/layers.mjs +224 -0
  47. package/engine/prompt-config-engine.mjs +49 -0
  48. package/engine/run-code-env.mjs +208 -0
  49. package/engine/schema.mjs +312 -0
  50. package/engine/session-vars.mjs +52 -0
  51. package/{preset → engine}/shared.mjs +35 -0
  52. package/engine/strategies.mjs +213 -0
  53. package/{preset → engine}/tool-bootstrap.mjs +350 -282
  54. package/engine/tool-filter.mjs +80 -0
  55. package/engine/vendor/yaml/LICENSE +13 -0
  56. package/engine/vendor/yaml/dist/compose/compose-collection.js +88 -0
  57. package/engine/vendor/yaml/dist/compose/compose-doc.js +43 -0
  58. package/engine/vendor/yaml/dist/compose/compose-node.js +109 -0
  59. package/engine/vendor/yaml/dist/compose/compose-scalar.js +86 -0
  60. package/engine/vendor/yaml/dist/compose/composer.js +219 -0
  61. package/engine/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
  62. package/engine/vendor/yaml/dist/compose/resolve-block-scalar.js +198 -0
  63. package/engine/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
  64. package/engine/vendor/yaml/dist/compose/resolve-end.js +37 -0
  65. package/engine/vendor/yaml/dist/compose/resolve-flow-collection.js +207 -0
  66. package/engine/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
  67. package/engine/vendor/yaml/dist/compose/resolve-props.js +146 -0
  68. package/engine/vendor/yaml/dist/compose/util-contains-newline.js +34 -0
  69. package/engine/vendor/yaml/dist/compose/util-empty-scalar-position.js +26 -0
  70. package/engine/vendor/yaml/dist/compose/util-flow-indent-check.js +15 -0
  71. package/engine/vendor/yaml/dist/compose/util-map-includes.js +13 -0
  72. package/engine/vendor/yaml/dist/doc/Document.js +335 -0
  73. package/engine/vendor/yaml/dist/doc/anchors.js +71 -0
  74. package/engine/vendor/yaml/dist/doc/applyReviver.js +55 -0
  75. package/engine/vendor/yaml/dist/doc/createNode.js +88 -0
  76. package/engine/vendor/yaml/dist/doc/directives.js +176 -0
  77. package/engine/vendor/yaml/dist/errors.js +57 -0
  78. package/engine/vendor/yaml/dist/index.js +17 -0
  79. package/engine/vendor/yaml/dist/log.js +11 -0
  80. package/engine/vendor/yaml/dist/nodes/Alias.js +116 -0
  81. package/engine/vendor/yaml/dist/nodes/Collection.js +147 -0
  82. package/engine/vendor/yaml/dist/nodes/Node.js +38 -0
  83. package/engine/vendor/yaml/dist/nodes/Pair.js +36 -0
  84. package/engine/vendor/yaml/dist/nodes/Scalar.js +24 -0
  85. package/engine/vendor/yaml/dist/nodes/YAMLMap.js +144 -0
  86. package/engine/vendor/yaml/dist/nodes/YAMLSeq.js +113 -0
  87. package/engine/vendor/yaml/dist/nodes/addPairToJSMap.js +63 -0
  88. package/engine/vendor/yaml/dist/nodes/identity.js +36 -0
  89. package/engine/vendor/yaml/dist/nodes/toJS.js +37 -0
  90. package/engine/vendor/yaml/dist/parse/cst-scalar.js +214 -0
  91. package/engine/vendor/yaml/dist/parse/cst-stringify.js +61 -0
  92. package/engine/vendor/yaml/dist/parse/cst-visit.js +97 -0
  93. package/engine/vendor/yaml/dist/parse/cst.js +98 -0
  94. package/engine/vendor/yaml/dist/parse/lexer.js +721 -0
  95. package/engine/vendor/yaml/dist/parse/line-counter.js +39 -0
  96. package/engine/vendor/yaml/dist/parse/parser.js +975 -0
  97. package/engine/vendor/yaml/dist/public-api.js +102 -0
  98. package/engine/vendor/yaml/dist/schema/Schema.js +37 -0
  99. package/engine/vendor/yaml/dist/schema/common/map.js +17 -0
  100. package/engine/vendor/yaml/dist/schema/common/null.js +15 -0
  101. package/engine/vendor/yaml/dist/schema/common/seq.js +17 -0
  102. package/engine/vendor/yaml/dist/schema/common/string.js +14 -0
  103. package/engine/vendor/yaml/dist/schema/core/bool.js +19 -0
  104. package/engine/vendor/yaml/dist/schema/core/float.js +43 -0
  105. package/engine/vendor/yaml/dist/schema/core/int.js +38 -0
  106. package/engine/vendor/yaml/dist/schema/core/schema.js +23 -0
  107. package/engine/vendor/yaml/dist/schema/json/schema.js +62 -0
  108. package/engine/vendor/yaml/dist/schema/tags.js +96 -0
  109. package/engine/vendor/yaml/dist/schema/yaml-1.1/binary.js +58 -0
  110. package/engine/vendor/yaml/dist/schema/yaml-1.1/bool.js +26 -0
  111. package/engine/vendor/yaml/dist/schema/yaml-1.1/float.js +46 -0
  112. package/engine/vendor/yaml/dist/schema/yaml-1.1/int.js +71 -0
  113. package/engine/vendor/yaml/dist/schema/yaml-1.1/merge.js +67 -0
  114. package/engine/vendor/yaml/dist/schema/yaml-1.1/omap.js +74 -0
  115. package/engine/vendor/yaml/dist/schema/yaml-1.1/pairs.js +78 -0
  116. package/engine/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
  117. package/engine/vendor/yaml/dist/schema/yaml-1.1/set.js +93 -0
  118. package/engine/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +101 -0
  119. package/engine/vendor/yaml/dist/stringify/foldFlowLines.js +146 -0
  120. package/engine/vendor/yaml/dist/stringify/stringify.js +129 -0
  121. package/engine/vendor/yaml/dist/stringify/stringifyCollection.js +153 -0
  122. package/engine/vendor/yaml/dist/stringify/stringifyComment.js +20 -0
  123. package/engine/vendor/yaml/dist/stringify/stringifyDocument.js +85 -0
  124. package/engine/vendor/yaml/dist/stringify/stringifyNumber.js +25 -0
  125. package/engine/vendor/yaml/dist/stringify/stringifyPair.js +150 -0
  126. package/engine/vendor/yaml/dist/stringify/stringifyString.js +336 -0
  127. package/engine/vendor/yaml/dist/util.js +11 -0
  128. package/engine/vendor/yaml/dist/visit.js +233 -0
  129. package/engine/vendor/yaml/index.js +5 -0
  130. package/engine/vendor/yaml/package.json +11 -0
  131. package/lib/client.js +4724 -775
  132. package/lib/client.js.map +1 -1
  133. package/lib/index.d.mts +530 -51
  134. package/lib/index.mjs +4164 -740
  135. package/lib/preset-core.d.mts +7 -37
  136. package/lib/preset-core.mjs +43 -285
  137. package/lib/prompt-configs-B4vH09wx.d.mts +100 -0
  138. package/lib/prompt-configs-ThS4iXPg.mjs +771 -0
  139. package/package.json +36 -29
  140. package/preset/anchored/preset.yml +342 -0
  141. package/preset/creative/preset.yml +65 -0
  142. package/preset/creative/skills/cordis-plugin-development/SKILL.md +420 -0
  143. package/preset/creative/skills/editing-cordis-compositions/SKILL.md +165 -0
  144. package/preset/custom/preset.yml +13 -0
  145. package/preset/liangshen/preset.yml +72 -0
  146. package/preset/minimal/preset.yml +44 -0
  147. package/preset/ptc/preset.yml +56 -0
  148. package/preset/standard/preset.yml +55 -0
  149. package/skills/manifest.json +7 -0
  150. package/skills/sandboxmod/SKILL.md +49 -49
  151. package/skills/web ui/SKILL.md +42 -0
  152. package/templates/10-pre-step.yml +28 -0
  153. package/templates/11-merged-a.yml +11 -0
  154. package/templates/13-anchor.yml +19 -0
  155. package/templates/14-first-turn-anchor.yml +27 -0
  156. package/templates/15-guide-auto.yml +25 -0
  157. package/templates/16-custom-fallback.yml +21 -0
  158. package/templates/17-instruction-hint.yml +23 -0
  159. package/templates/18-placeholder-env-facts.yml +11 -0
  160. package/templates/19-placeholder-skill-catalog.yml +19 -0
  161. package/templates/20-system-section.yml +19 -0
  162. package/templates/30-runtime-context.yml +10 -0
  163. package/templates/31-runtime-context-placeholder.yml +18 -0
  164. package/templates/40-agent-request.yml +11 -0
  165. package/templates/50-llm-stream.yml +9 -0
  166. package/templates/60-tool-pipeline.yml +12 -0
  167. package/AGENTS.md +0 -4
  168. package/plan.md +0 -312
  169. package/preset/agent.cordis.yml +0 -443
  170. package/preset/compaction-epoch.mjs +0 -81
  171. package/preset/context-gate.mjs +0 -165
  172. package/preset/instruction-hint.mjs +0 -217
  173. package/preset/near-anchor.mjs +0 -101
  174. package/preset/preset.yml +0 -3
  175. package/preset/prompt-injector.mjs +0 -112
  176. package/preset/router-first-turn.mjs +0 -73
  177. package/preset/router-guide.mjs +0 -79
  178. package/preset.md +0 -115
  179. package/upstream/dsh-anchored-standard/LICENSE +0 -22
  180. package/upstream/dsh-anchored-standard/NOTICE +0 -19
  181. package/upstream/dsh-anchored-standard/REVISION +0 -1
  182. package/upstream/dsh-anchored-standard/preset/agent.cordis.yml +0 -440
  183. package/upstream/dsh-anchored-standard/preset/compaction-epoch.mjs +0 -81
  184. package/upstream/dsh-anchored-standard/preset/context-gate.mjs +0 -202
  185. package/upstream/dsh-anchored-standard/preset/custom-bash.mjs +0 -219
  186. package/upstream/dsh-anchored-standard/preset/dev-tool-search.mjs +0 -131
  187. package/upstream/dsh-anchored-standard/preset/instruction-hint.mjs +0 -231
  188. package/upstream/dsh-anchored-standard/preset/preset.yml +0 -3
  189. package/upstream/dsh-anchored-standard/preset/skill-search.mjs +0 -142
  190. package/upstream/dsh-anchored-standard/preset/tool-bootstrap.mjs +0 -301
  191. /package/{preset → engine}/skill-search.mjs +0 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * layers — 非 pre-step 的五个官方层级接线。
3
+ * 所有注册都只作用于本插件提示词配置;单条失败 warnOnce 后继续。
4
+ */
5
+
6
+ import {
7
+ getService,
8
+ isDelegated,
9
+ keepDisposer,
10
+ matchesModel,
11
+ parseToolNames,
12
+ } from './shared.mjs'
13
+ import { interpolateVariables, interpolateStatic } from './interpolate.mjs'
14
+
15
+ const name = 'prompt-config-engine'
16
+
17
+ /** 单条文本型配置的完整文本:texts 数组按空行拼接(单一文本字段)。 */
18
+ function configText(config) {
19
+ return config.texts
20
+ .map((item) => interpolateStatic(item, config.variables))
21
+ .filter((item) => item.length > 0)
22
+ .join('\n\n')
23
+ }
24
+
25
+ /** agent/request 与 tools/* 层的作用域过滤。 */
26
+ function matchesAgentScope(config, agent) {
27
+ if (agent === undefined) return true
28
+ const delegated = isDelegated(agent.session)
29
+ if (config.audience === 'main' && delegated) return false
30
+ if (config.audience === 'subagent' && !delegated) return false
31
+ return matchesModel(config.modelScope, agent.options?.model)
32
+ }
33
+
34
+ /** 文本型层分组:merged 模式按位置分组,否则每条独立。 */
35
+ function textLayerGroups(configs) {
36
+ const sorted = [...configs].sort((a, b) => a.order - b.order)
37
+ const groups = []
38
+ const index = new Map()
39
+ for (const config of sorted) {
40
+ const key = config.mergeMode === 'merged' ? `merged:${config.position ?? ''}` : undefined
41
+ if (key === undefined) {
42
+ groups.push([config])
43
+ continue
44
+ }
45
+ let at = index.get(key)
46
+ if (at === undefined) {
47
+ at = groups.length
48
+ index.set(key, at)
49
+ groups.push([])
50
+ }
51
+ groups[at].push(config)
52
+ }
53
+ return groups
54
+ }
55
+
56
+ /** system-section:注册静态 system prompt 段(支持官方 {{variable}} 渲染与 merged 拼接)。 */
57
+ function wireSystemSections(ctx, configs, warnOnce) {
58
+ const systemPrompt = getService(ctx, 'systemPrompt')
59
+ if (systemPrompt === undefined || typeof systemPrompt.section !== 'function') {
60
+ if (configs.length > 0) warnOnce(`${name}: systemPrompt service unavailable — system-section configs skipped`)
61
+ return
62
+ }
63
+ for (const group of textLayerGroups(configs)) {
64
+ const base = group[0]
65
+ try {
66
+ const text = group.map((config) => configText(config)).filter((item) => item.length > 0).join('\n\n')
67
+ if (text.length === 0) continue
68
+ keepDisposer(ctx, systemPrompt.section({
69
+ name: typeof base.params?.sectionName === 'string' && base.params.sectionName.length > 0 ? base.params.sectionName : base.id,
70
+ order: base.order,
71
+ text,
72
+ ...(base.params?.complete === true ? { complete: true } : {}),
73
+ }), `${name}: section ${base.id}`)
74
+ // persona 语义透传:suppressRuntimeContext 抑制该 scope 的动态 runtime-context
75
+ // 快照(官方 dsh-persona includeRuntimeContext:false 等价;多段重复调用幂等)。
76
+ if (base.params?.suppressRuntimeContext === true) {
77
+ keepDisposer(ctx, systemPrompt.suppressRuntimeContext(), `${name}: suppressRuntimeContext ${base.id}`)
78
+ }
79
+ } catch (error) {
80
+ warnOnce(`${name}: system-section config ${base.id} failed: ${String(error?.message ?? error)}`)
81
+ }
82
+ }
83
+ }
84
+
85
+ /** runtime-context:注册动态运行时上下文(晋升后由 context-gate 差分投影;支持 merged 拼接与 placeholder 函数 provider)。 */
86
+ function wireRuntimeContexts(ctx, configs, warnOnce) {
87
+ const systemPrompt = getService(ctx, 'systemPrompt')
88
+ if (systemPrompt === undefined || typeof systemPrompt.context !== 'function') {
89
+ if (configs.length > 0) warnOnce(`${name}: systemPrompt service unavailable — runtime-context configs skipped`)
90
+ return
91
+ }
92
+ const staticConfigs = configs.filter((config) => config.strategy !== 'placeholder')
93
+ for (const group of textLayerGroups(staticConfigs)) {
94
+ const base = group[0]
95
+ try {
96
+ const text = group.map((config) => configText(config)).filter((item) => item.length > 0).join('\n\n')
97
+ if (text.length === 0) continue
98
+ keepDisposer(ctx, systemPrompt.context({
99
+ name: typeof base.params?.contextName === 'string' && base.params.contextName.length > 0 ? base.params.contextName : base.id,
100
+ order: base.order,
101
+ text,
102
+ }), `${name}: context ${base.id}`)
103
+ } catch (error) {
104
+ warnOnce(`${name}: runtime-context config ${base.id} failed: ${String(error?.message ?? error)}`)
105
+ }
106
+ }
107
+ // placeholder:官方 context 接受函数 provider,在每次 assembly 时动态填充。
108
+ const placeholders = configs.filter((config) => config.strategy === 'placeholder')
109
+ .sort((a, b) => a.order - b.order)
110
+ for (const config of placeholders) {
111
+ try {
112
+ const resolver = config.resolve
113
+ keepDisposer(ctx, systemPrompt.context({
114
+ name: typeof config.params?.contextName === 'string' && config.params.contextName.length > 0 ? config.params.contextName : config.id,
115
+ order: config.order,
116
+ text: async (assembly) => {
117
+ const agent = assembly?.agent
118
+ const session = agent?.session
119
+ const resolved = await resolver({ ctx, agent, session, decision: { kind: 'ok', messages: [] }, messages: [] })
120
+ if (resolved === null || resolved === undefined) return ''
121
+ const variables = { ...config.variables, ...(resolved.variables !== null && typeof resolved.variables === 'object' ? resolved.variables : {}) }
122
+ if (config.texts.length > 0) return interpolateVariables(config.texts.join('\n\n'), variables, session)
123
+ return typeof resolved.text === 'string' ? interpolateVariables(resolved.text, variables, session) : ''
124
+ },
125
+ }), `${name}: context ${config.id}`)
126
+ } catch (error) {
127
+ warnOnce(`${name}: runtime-context placeholder ${config.id} failed: ${String(error?.message ?? error)}`)
128
+ }
129
+ }
130
+ }
131
+
132
+ /** agent-request:对冻结的 LlmCallConfig 做浅合并 / 整体替换。 */
133
+ function wireAgentRequests(ctx, configs, warnOnce) {
134
+ for (const config of configs) {
135
+ ctx.on('agent/request', async (payload, next) => {
136
+ const base = await next()
137
+ try {
138
+ if (!matchesAgentScope(config, payload?.agent)) return base
139
+ const patch = config.params?.patch !== null && typeof config.params?.patch === 'object' && !Array.isArray(config.params.patch)
140
+ ? config.params.patch
141
+ : {}
142
+ if (config.params?.replace === true) return { ...patch }
143
+ return { ...base, ...patch }
144
+ } catch (error) {
145
+ warnOnce(`${name}: agent-request config ${config.id} failed: ${String(error?.message ?? error)}`)
146
+ return base
147
+ }
148
+ })
149
+ }
150
+ }
151
+
152
+ /** 把流替换为提示词配置文本的最小合法 chunk 序列。 */
153
+ async function* replacedStream(text) {
154
+ yield { type: 'block-start', index: 0, blockType: 'text' }
155
+ yield { type: 'text-delta', index: 0, text }
156
+ yield { type: 'block-end', index: 0, block: { type: 'text', text } }
157
+ }
158
+
159
+ /** llm-stream:pass 透传;replace 用提示词配置文本替代整个模型流。 */
160
+ function wireLlmStreams(ctx, configs, warnOnce) {
161
+ for (const config of configs) {
162
+ ctx.on('llm/stream', (options, next) => {
163
+ try {
164
+ const mode = config.params?.mode ?? 'pass'
165
+ if (mode === 'replace' && config.texts.length > 0 && matchesModel(config.modelScope, options?.model)) {
166
+ return replacedStream(config.texts.join('\n\n'))
167
+ }
168
+ return next()
169
+ } catch (error) {
170
+ warnOnce(`${name}: llm-stream config ${config.id} failed: ${String(error?.message ?? error)}`)
171
+ return next()
172
+ }
173
+ })
174
+ }
175
+ }
176
+
177
+ /** tool-pipeline:pre-execute 判定、execute 包装、post-execute 结果替换/阻断。 */
178
+ function wireToolPipelines(ctx, configs, warnOnce) {
179
+ for (const config of configs) {
180
+ const names = parseToolNames(config.params?.toolNames)
181
+ const matchesTool = (exec) => names.length === 0 || names.includes(exec?.name)
182
+ ctx.on('tools/pre-execute', async (exec, next) => {
183
+ try {
184
+ if (!matchesTool(exec) || !matchesAgentScope(config, exec?.agent)) return next()
185
+ const decision = config.params?.preDecision ?? 'allow'
186
+ if (decision === 'allow') return next()
187
+ if (decision === 'deny') {
188
+ return { kind: 'deny', reason: String(config.params?.denyReason ?? `${config.name}: denied by prompt config`) }
189
+ }
190
+ if (decision === 'ask') return { kind: 'ask' }
191
+ return next()
192
+ } catch (error) {
193
+ warnOnce(`${name}: tool-pipeline(pre) config ${config.id} failed: ${String(error?.message ?? error)}`)
194
+ return next()
195
+ }
196
+ })
197
+ ctx.on('tools/post-execute', async (exec, result, next) => {
198
+ try {
199
+ if (!matchesTool(exec) || !matchesAgentScope(config, exec?.agent)) return next()
200
+ const action = config.params?.postAction ?? 'accept'
201
+ if (action === 'accept') return next()
202
+ if (action === 'replace' && config.texts.length > 0) {
203
+ return { kind: 'accept', content: [{ type: 'text', text: config.texts.join('\n\n') }] }
204
+ }
205
+ if (action === 'block') {
206
+ return { kind: 'block', feedback: [{ type: 'text', text: config.texts.length > 0 ? config.texts.join('\n\n') : `${config.name}: blocked by prompt config` }] }
207
+ }
208
+ return next()
209
+ } catch (error) {
210
+ warnOnce(`${name}: tool-pipeline(post) config ${config.id} failed: ${String(error?.message ?? error)}`)
211
+ return next()
212
+ }
213
+ })
214
+ }
215
+ }
216
+
217
+ /** 把非 pre-step 提示词配置接入其声明的官方层级通道。 */
218
+ export function wireLayers(ctx, configs, warnOnce) {
219
+ wireSystemSections(ctx, configs.filter((config) => config.layer === 'system-section'), warnOnce)
220
+ wireRuntimeContexts(ctx, configs.filter((config) => config.layer === 'runtime-context'), warnOnce)
221
+ wireAgentRequests(ctx, configs.filter((config) => config.layer === 'agent-request'), warnOnce)
222
+ wireLlmStreams(ctx, configs.filter((config) => config.layer === 'llm-stream'), warnOnce)
223
+ wireToolPipelines(ctx, configs.filter((config) => config.layer === 'tool-pipeline'), warnOnce)
224
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * prompt-config-engine — prompt-tool 的唯一提示词注入执行器(装配入口)。
3
+ *
4
+ * 本文件只做装配:
5
+ * - schema.mjs 提示词配置加载/归一化/权威校验
6
+ * - strategies.mjs 内置策略绑定 + 模板专属策略懒加载(strategyDir)
7
+ * - fillers.mjs placeholder 动态填充器(instruction-hint / env-facts / skill-catalog)
8
+ * - layers.mjs 非 pre-step 五个官方层级接线
9
+ * - executor.mjs pre-step 消息批执行器(过滤/去重/合并/落位)
10
+ *
11
+ * agent.cordis.yml 引擎行:
12
+ * - id: prompt-config-engine
13
+ * name: ./engine/prompt-config-engine.mjs
14
+ * config:
15
+ * configsDir: ../prompt-configs # 提示词配置模块目录(相对本文件)
16
+ * strategyDir: ../strategies # 模板专属策略目录(可选,相对本文件)
17
+ *
18
+ * 铁律:任一提示词配置失败只跳过该提示词配置并 warnOnce;配置错误挂载时 fail loud。
19
+ */
20
+
21
+ import { createPromptConfigs, loadPromptConfigFiles, parsePromptConfigYaml } from './schema.mjs'
22
+ import { applyPromptConfigs } from './executor.mjs'
23
+
24
+ /** Cordis plugin name used by loader diagnostics. */
25
+ export const name = 'prompt-config-engine'
26
+
27
+ /**
28
+ * 等待全部可注入层级的宿主服务。preset 行在 agent 组合内解析这些服务
29
+ * (persona 依赖 systemPrompt,工具行依赖 tools,agent loop 依赖 llm)。
30
+ */
31
+ export const inject = ['systemPrompt', 'tools', 'llm']
32
+
33
+ export { parsePromptConfigYaml, loadPromptConfigFiles, createPromptConfigs, applyPromptConfigs }
34
+
35
+ /**
36
+ * 引擎插件入口:config.configsDir 为提示词配置模块目录(相对本文件的 URL),
37
+ * config.strategyDir 为模板专属策略目录(可选)。引擎扫描目录内每个
38
+ * *.yml / *.yaml / *.json 并装配为提示词配置。
39
+ */
40
+ export function apply(ctx, config) {
41
+ const dirName = typeof config?.configsDir === 'string' && config.configsDir.length > 0
42
+ ? config.configsDir
43
+ : './prompt-configs'
44
+ const dirUrl = new URL(dirName.endsWith('/') ? dirName : `${dirName}/`, import.meta.url)
45
+ const strategyDir = typeof config?.strategyDir === 'string' && config.strategyDir.length > 0
46
+ ? config.strategyDir
47
+ : undefined
48
+ applyPromptConfigs(ctx, createPromptConfigs(loadPromptConfigFiles(dirUrl), { strategyDir }), { prepend: true })
49
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * run-code-env — prompt-tool 的 PTC 环境注入配置。
3
+ *
4
+ * 官方 Code Mode 的 worker 线程刻意以 `env: {}` 启动(比普通 shell 的
5
+ * credential scrub 更严格),因此 run_code 程序里的 `process.env` 恒为空。
6
+ * 本插件不试图推翻该安全设计,而是在每个 run_code 程序执行前注入一个
7
+ * 冻结的 `env` 全局:
8
+ *
9
+ * const env = Object.freeze(JSON.parse(<curated json>))
10
+ *
11
+ * `env` 的内容只来自三处,且全部经过白名单/敏感名过滤:
12
+ * 1. 宿主 process.env 中列在 `envKeys` 里的键;
13
+ * 2. `ctx.shellEnv` 受管快照(DSH_HOME / DSH_SESSION_ID / 插件贡献的 DSH_*);
14
+ * 3. 当前会话工作区(DSH_WORKSPACE)。
15
+ *
16
+ * 模型侧始终可写 `env.PATH`、`env.HOME`、`env.DSH_HOME` 等;不要读
17
+ * `process.env`(恒为 {})。本插件只在存在 run_code 传输的 scope 生效,
18
+ * native 模式自动空转。
19
+ */
20
+
21
+ /** Cordis 插件名,供 loader 诊断使用。 */
22
+ export const name = 'run-code-env'
23
+
24
+ /** 需要 tools 视图与 systemPrompt 段。shellEnv 为机会型读取,不写进 inject。 */
25
+ export const inject = ['tools', 'systemPrompt']
26
+
27
+ /** 默认暴露的系统环境变量(无凭据形态)。 */
28
+ export const DEFAULT_ENV_KEYS = [
29
+ 'PATH',
30
+ 'PATHEXT',
31
+ 'HOME',
32
+ 'USERPROFILE',
33
+ 'USERNAME',
34
+ 'COMPUTERNAME',
35
+ 'OS',
36
+ 'TEMP',
37
+ 'TMP',
38
+ 'SystemRoot',
39
+ 'ProgramFiles',
40
+ 'ProgramFiles(x86)',
41
+ 'LOCALAPPDATA',
42
+ 'APPDATA',
43
+ ]
44
+
45
+ /** 任何疑似凭据的名字一律不放行。 */
46
+ export const SENSITIVE_ENV_RE = /KEY|PASSWORD|SECRET|TOKEN/i
47
+
48
+ /** run_code 定义对象只 patch 一次(跨 HMR 安全)。 */
49
+ const PATCHED = new WeakSet()
50
+
51
+ /**
52
+ * 校验 envKeys:非空字符串数组;缺省/空数组回退默认白名单。
53
+ * 单个键为敏感名时在 buildEnv 阶段过滤,这里只校验形状。
54
+ */
55
+ export function normalizeEnvKeys(value) {
56
+ if (value === undefined) return [...DEFAULT_ENV_KEYS]
57
+ if (!Array.isArray(value) || value.length === 0) return [...DEFAULT_ENV_KEYS]
58
+ const keys = value.map((key) => (typeof key === 'string' ? key.trim() : ''))
59
+ .filter((key) => key.length > 0)
60
+ return keys.length > 0 ? [...new Set(keys)] : [...DEFAULT_ENV_KEYS]
61
+ }
62
+
63
+ /**
64
+ * 在 process.env 里按平台语义查找一个键;Windows 大小写不敏感。
65
+ * @param env - 宿主环境对象(通常是 process.env)
66
+ * @param key - 期望键名
67
+ */
68
+ function lookupEnv(env, key) {
69
+ if (env === undefined || env === null) return undefined
70
+ if (process.platform === 'win32') {
71
+ const wanted = key.toUpperCase()
72
+ const match = Object.keys(env).find((candidate) => candidate.toUpperCase() === wanted)
73
+ return match === undefined ? undefined : env[match]
74
+ }
75
+ return env[key]
76
+ }
77
+
78
+ /**
79
+ * 构造本次 run_code 注入的 env 对象。所有来源统一脱敏。
80
+ * @param ctx - Cordis context(机会型读取 shellEnv)
81
+ * @param keys - 白名单键
82
+ * @param agent - 当前执行 agent(可空)
83
+ */
84
+ export function buildEnv(ctx, keys, agent) {
85
+ const env = {}
86
+ const hostEnv = typeof process === 'undefined' ? undefined : process.env
87
+
88
+ for (const key of keys) {
89
+ if (SENSITIVE_ENV_RE.test(key)) continue
90
+ const value = lookupEnv(hostEnv, key)
91
+ if (value !== undefined && value !== null) env[key] = String(value)
92
+ }
93
+
94
+ // 受管 DSH_* 快照:shellEnv 不存在或 collect 失败时静默降级。
95
+ try {
96
+ const shellEnv = ctx?.get?.('shellEnv')
97
+ if (shellEnv !== undefined && typeof shellEnv.collect === 'function') {
98
+ const execution = agent === undefined ? {} : { agent }
99
+ const managed = shellEnv.collect(execution) ?? {}
100
+ for (const [key, value] of Object.entries(managed)) {
101
+ if (typeof value === 'string' && !SENSITIVE_ENV_RE.test(key)) env[key] = value
102
+ }
103
+ }
104
+ } catch {
105
+ // shellEnv 缺失/失败只意味着没有受管变量,不阻断 PTC。
106
+ }
107
+
108
+ // DSH_HOME 兜底:启动器/宿主进程通常显式设置了 DSH_HOME(本机为
109
+ // D:\AI\DeepSeek harness\.dsh)。shellEnv 不可用时退回到该值;shellEnv
110
+ // 已给出受管值时这里不会覆盖。
111
+ if (env.DSH_HOME === undefined) {
112
+ const ambientHome = lookupEnv(hostEnv, 'DSH_HOME')
113
+ if (ambientHome !== undefined && ambientHome !== null && !SENSITIVE_ENV_RE.test('DSH_HOME')) {
114
+ env.DSH_HOME = String(ambientHome)
115
+ }
116
+ }
117
+
118
+ const cwd = agent?.session?.header?.cwd
119
+ if (typeof cwd === 'string' && cwd.length > 0 && !SENSITIVE_ENV_RE.test('DSH_WORKSPACE')) {
120
+ env.DSH_WORKSPACE = cwd
121
+ }
122
+
123
+ return env
124
+ }
125
+
126
+ /** 生成注入前缀:JSON 双序列化后作为 JS 字符串字面量安全嵌入。 */
127
+ export function injectEnvPrefix(code, env) {
128
+ const payload = JSON.stringify(env ?? {}) ?? '{}'
129
+ const literal = JSON.stringify(payload)
130
+ .replace(/\u2028/g, '\\u2028')
131
+ .replace(/\u2029/g, '\\u2029')
132
+ return `const env = Object.freeze(JSON.parse(${literal}));\n${code}`
133
+ }
134
+
135
+ /** 渲染 PTC 提示段;native(无 run_code)返回空串。 */
136
+ function envSectionText(ctx, keys) {
137
+ const names = [...keys]
138
+ .filter((key) => !SENSITIVE_ENV_RE.test(key))
139
+ const sample = names.includes('HOME') ? 'HOME' : names[0]
140
+ return [
141
+ '## run_code environment',
142
+ '',
143
+ 'Inside every run_code program a frozen global `env` is available; `process.env` is always empty by design.',
144
+ `Available keys: ${names.join(', ') || '(none)'}`,
145
+ 'Managed DSH_* facts (for example DSH_HOME, DSH_SESSION_ID) and DSH_WORKSPACE are also present when the harness provides them.',
146
+ sample ? `Example: \`const home = env.${sample}\`` : 'Example: const cwd = env.DSH_WORKSPACE',
147
+ 'Read values from `env.*`; never try to read `process.env`.',
148
+ ].join('\n')
149
+ }
150
+
151
+ /** 给 run_code 的 ToolDefinition 打补丁:execute 前注入 env 前缀。 */
152
+ export function patchRunCodeTool(tool, ctx, keys) {
153
+ if (tool === undefined || tool.name !== 'run_code') return false
154
+ if (PATCHED.has(tool)) return false
155
+ PATCHED.add(tool)
156
+
157
+ const originalExecute = tool.execute.bind(tool)
158
+ tool.execute = async (rawArgs, exec) => {
159
+ const args = rawArgs ?? {}
160
+ const env = buildEnv(ctx, keys, exec?.agent)
161
+ const code = injectEnvPrefix(typeof args.code === 'string' ? args.code : '', env)
162
+ return originalExecute({ ...args, code }, exec)
163
+ }
164
+ return true
165
+ }
166
+
167
+ /** 注册 PTC env 注入:只在目标 scope 出现 run_code 时生效。 */
168
+ export function apply(ctx, config) {
169
+ const source = config === undefined ? {} : config
170
+ const enabled = source.enabled !== false
171
+ const keys = normalizeEnvKeys(source.envKeys)
172
+
173
+ const tryPatch = (scope) => {
174
+ if (!enabled) return
175
+ try {
176
+ patchRunCodeTool(ctx.tools.get('run_code', scope), ctx, keys)
177
+ } catch {
178
+ // run_code 尚未物化或 registry 不可用:下一次 assemble/execute 再试。
179
+ }
180
+ }
181
+
182
+ // 动态工具变化后,新 scope 的 run_code transport 可能刚创建。
183
+ ctx.on('tools/change', () => tryPatch())
184
+
185
+ // 组装前先确保当前 scope 的 run_code 已 patch(此时 SDK 会引用它)。
186
+ ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
187
+ tryPatch(context?.scope)
188
+ return next()
189
+ })
190
+
191
+ // 第一条 run_code 调用是最终兜底:dispatch 前按调用 agent 补 patch。
192
+ ctx.on('tools/execute', async (exec, next) => {
193
+ if (enabled && exec?.name === 'run_code') tryPatch(exec.agent)
194
+ return next()
195
+ })
196
+
197
+ if (enabled) {
198
+ ctx.systemPrompt.section({
199
+ name: 'tools:run-code-env',
200
+ order: 160,
201
+ text: (context) => {
202
+ const schemas = ctx.tools.schemas(context?.scope)
203
+ if (!Array.isArray(schemas) || !schemas.some((schema) => schema?.name === 'run_code')) return ''
204
+ return envSectionText(ctx, keys)
205
+ },
206
+ })
207
+ }
208
+ }