dsh-plugin-prompt-tool 0.6.2 → 0.7.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.
- package/README.md +11 -9
- package/engine/anchor-match.mjs +38 -12
- package/engine/anchor-turn.mjs +68 -0
- package/engine/classify-task.mjs +35 -0
- package/engine/code-presentation.mjs +103 -0
- package/engine/compositions/library/anchor-turn.yml +10 -0
- package/engine/compositions/library/bootstrap-filesystem.yml +1 -1
- package/engine/compositions/library/code-presentation.yml +8 -0
- package/engine/compositions/library/command-goal.yml +11 -0
- package/engine/compositions/library/compaction.yml +1 -1
- package/engine/compositions/library/context-gate.yml +1 -2
- package/engine/compositions/library/cot-drip.yml +11 -0
- package/engine/compositions/library/custom-bash.yml +1 -1
- package/engine/compositions/library/delegation.yml +11 -7
- package/engine/compositions/library/deliberation-gate.yml +11 -0
- package/engine/compositions/library/persistent-shell.yml +25 -2
- package/engine/compositions/library/persona.yml +6 -2
- package/engine/compositions/library/planning.yml +1 -1
- package/engine/compositions/library/prompt-config-engine.yml +1 -1
- package/engine/compositions/library/run-code-env.yml +1 -1
- package/engine/compositions/library/skill-filesystem.yml +1 -1
- package/engine/compositions/library/skill-search.yml +1 -1
- package/engine/compositions/library/str-replace-editor.yml +2 -2
- package/engine/compositions/library/tool-ask-user.yml +1 -1
- package/engine/compositions/library/tool-bash.yml +1 -1
- package/engine/compositions/library/tool-bootstrap.yml +1 -3
- package/engine/compositions/library/tool-config-engine.yml +9 -0
- package/engine/compositions/library/tool-filter.yml +3 -7
- package/engine/compositions/library/tool-fs-search.yml +1 -1
- package/engine/compositions/library/tool-fs.yml +1 -1
- package/engine/compositions/library/tool-goal.yml +1 -9
- package/engine/compositions/library/tool-jobs.yml +1 -1
- package/engine/compositions/library/tool-pwsh.yml +1 -1
- package/engine/compositions/library/tool-todo.yml +1 -1
- package/engine/compositions/library/tool-web.yml +2 -2
- package/engine/compositions/source/local/code-presentation.yml +8 -0
- package/engine/compositions/source/local/context-gate.yml +0 -1
- package/engine/compositions/source/local/str-replace-editor.yml +10 -0
- package/engine/compositions/source/local/tool-bootstrap.yml +1 -3
- package/engine/compositions/source/local/tool-config-engine.yml +9 -0
- package/engine/compositions/source/local/tool-filter.yml +8 -0
- package/engine/cot-drip.mjs +137 -0
- package/engine/custom-bash.mjs +73 -21
- package/engine/deliberation-gate.mjs +127 -0
- package/engine/layers.mjs +223 -198
- package/engine/run-code-env.mjs +8 -2
- package/engine/schema.mjs +1 -1
- package/engine/strategies.mjs +25 -14
- package/engine/tool-bootstrap.mjs +185 -59
- package/engine/tool-config-engine.mjs +390 -0
- package/lib/client.js +3840 -2311
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +185 -79
- package/lib/index.mjs +1000 -356
- package/lib/preset-core.d.mts +1 -1
- package/lib/preset-core.mjs +29 -3
- package/lib/{prompt-configs-BgFw_bFf.mjs → prompt-configs-BnQKRHVb.mjs} +211 -136
- package/lib/prompt-configs-CN0sJTUL.d.mts +193 -0
- package/package.json +121 -105
- package/preset/anchored/preset.yml +262 -308
- package/preset/creative/preset.yml +34 -32
- package/preset/custom/preset.yml +0 -1
- package/preset/liangshen/preset.yml +53 -50
- package/preset/minimal/preset.yml +18 -17
- package/preset/ptc/preset.yml +35 -33
- package/preset/standard/preset.yml +34 -32
- package/templates/10-pre-step.yml +18 -15
- package/templates/14-first-turn-anchor.yml +13 -13
- package/templates/15-guide-auto.yml +13 -13
- package/templates/16-custom-fallback.yml +19 -18
- package/templates/18-placeholder-env-facts.yml +13 -2
- package/templates/30-runtime-context.yml +3 -0
- package/templates/40-agent-request.yml +9 -9
- package/templates/60-tool-pipeline.yml +9 -8
- package/templates/tools/10-shell-command.yml +20 -0
- package/templates/tools/20-http-fetch.yml +24 -0
- package/templates/tools/30-fs-read.yml +20 -0
- package/templates/tools/40-delegate-character.yml +15 -0
- package/templates/tools/50-delegate-worldbook.yml +15 -0
- package/templates/tools/60-delegate-session-var.yml +15 -0
- package/templates/tools/70-delegate-worldbook-upsert.yml +36 -0
- package/lib/prompt-configs-B4vH09wx.d.mts +0 -100
- package/templates/11-merged-a.yml +0 -11
- package/templates/13-anchor.yml +0 -19
- package/templates/17-instruction-hint.yml +0 -23
- package/templates/19-placeholder-skill-catalog.yml +0 -19
- package/templates/31-runtime-context-placeholder.yml +0 -18
package/engine/layers.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* layers — 非 pre-step 的五个官方层级接线。
|
|
3
|
-
* 所有注册都只作用于本插件提示词配置;单条失败 warnOnce 后继续。
|
|
4
|
-
*/
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* layers — 非 pre-step 的五个官方层级接线。
|
|
3
|
+
* 所有注册都只作用于本插件提示词配置;单条失败 warnOnce 后继续。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
6
|
import {
|
|
7
7
|
getService,
|
|
8
8
|
isDelegated,
|
|
@@ -11,60 +11,85 @@ import {
|
|
|
11
11
|
parseToolNames,
|
|
12
12
|
} from './shared.mjs'
|
|
13
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
|
-
|
|
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
|
+
/** persona 段标记(官方 deployment:persona + 旧名 persona)。 */
|
|
57
|
+
function isPersonaSection(config) {
|
|
58
|
+
const sectionName = config?.params?.sectionName
|
|
59
|
+
return sectionName === 'deployment:persona' || sectionName === 'persona'
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
/** system-section:注册静态 system prompt 段(支持官方 {{variable}} 渲染与 merged 拼接)。 */
|
|
57
63
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const systemPrompt = getService(ctx, 'systemPrompt')
|
|
65
|
+
if (systemPrompt === undefined || typeof systemPrompt.section !== 'function') {
|
|
66
|
+
if (configs.length > 0) warnOnce(`${name}: systemPrompt service unavailable — system-section configs skipped`)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
// 子代理 persona 分支卡(audience=subagent + 人设段标记):不独立注册(同名段冲突),
|
|
70
|
+
// 作为主 persona 段的子代理分支——装配时子代理使用子卡文本(替换主会话人设)。
|
|
71
|
+
const subPersona = configs
|
|
72
|
+
.filter((config) => config.audience === 'subagent' && isPersonaSection(config))
|
|
73
|
+
.sort((a, b) => a.order - b.order)[0]
|
|
74
|
+
const registerable = configs.filter((config) => !(config.audience === 'subagent' && isPersonaSection(config)))
|
|
75
|
+
for (const group of textLayerGroups(registerable)) {
|
|
76
|
+
const base = group[0]
|
|
77
|
+
try {
|
|
78
|
+
const groupText = group.map((config) => configText(config)).filter((item) => item.length > 0).join('\n\n')
|
|
79
|
+
if (groupText.length === 0) continue
|
|
80
|
+
const hasAudience = group.some((config) => config.audience != null)
|
|
81
|
+
// persona 主段 + 存在子代理分支:text 按 agent 分支(子代理 = 子卡文本,替换语义;
|
|
82
|
+
// 无子卡 = 继承主 persona)。官方 AssembleContext 运行时含 agent(assembleContextFor 注入)。
|
|
83
|
+
const isPersonaMain = base.audience == null && isPersonaSection(base)
|
|
84
|
+
const text = isPersonaMain && subPersona !== undefined
|
|
85
|
+
? (context) => isDelegated(context?.agent?.session) ? configText(subPersona) : groupText
|
|
86
|
+
: hasAudience
|
|
87
|
+
? (context) => group
|
|
88
|
+
.filter((config) => matchesAgentScope(config, context?.agent))
|
|
89
|
+
.map((config) => configText(config))
|
|
90
|
+
.filter((item) => item.length > 0)
|
|
91
|
+
.join('\n\n')
|
|
92
|
+
: groupText
|
|
68
93
|
keepDisposer(ctx, systemPrompt.section({
|
|
69
94
|
name: typeof base.params?.sectionName === 'string' && base.params.sectionName.length > 0 ? base.params.sectionName : base.id,
|
|
70
95
|
order: base.order,
|
|
@@ -81,144 +106,144 @@ function wireSystemSections(ctx, configs, warnOnce) {
|
|
|
81
106
|
}
|
|
82
107
|
}
|
|
83
108
|
}
|
|
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
|
-
}
|
|
109
|
+
|
|
110
|
+
/** runtime-context:注册动态运行时上下文(晋升后由 context-gate 差分投影;支持 merged 拼接与 placeholder 函数 provider)。 */
|
|
111
|
+
function wireRuntimeContexts(ctx, configs, warnOnce) {
|
|
112
|
+
const systemPrompt = getService(ctx, 'systemPrompt')
|
|
113
|
+
if (systemPrompt === undefined || typeof systemPrompt.context !== 'function') {
|
|
114
|
+
if (configs.length > 0) warnOnce(`${name}: systemPrompt service unavailable — runtime-context configs skipped`)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
const staticConfigs = configs.filter((config) => config.strategy !== 'placeholder')
|
|
118
|
+
for (const group of textLayerGroups(staticConfigs)) {
|
|
119
|
+
const base = group[0]
|
|
120
|
+
try {
|
|
121
|
+
const text = group.map((config) => configText(config)).filter((item) => item.length > 0).join('\n\n')
|
|
122
|
+
if (text.length === 0) continue
|
|
123
|
+
keepDisposer(ctx, systemPrompt.context({
|
|
124
|
+
name: typeof base.params?.contextName === 'string' && base.params.contextName.length > 0 ? base.params.contextName : base.id,
|
|
125
|
+
order: base.order,
|
|
126
|
+
text,
|
|
127
|
+
}), `${name}: context ${base.id}`)
|
|
128
|
+
} catch (error) {
|
|
129
|
+
warnOnce(`${name}: runtime-context config ${base.id} failed: ${String(error?.message ?? error)}`)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// placeholder:官方 context 接受函数 provider,在每次 assembly 时动态填充。
|
|
133
|
+
const placeholders = configs.filter((config) => config.strategy === 'placeholder')
|
|
134
|
+
.sort((a, b) => a.order - b.order)
|
|
135
|
+
for (const config of placeholders) {
|
|
136
|
+
try {
|
|
137
|
+
const resolver = config.resolve
|
|
138
|
+
keepDisposer(ctx, systemPrompt.context({
|
|
139
|
+
name: typeof config.params?.contextName === 'string' && config.params.contextName.length > 0 ? config.params.contextName : config.id,
|
|
140
|
+
order: config.order,
|
|
141
|
+
text: async (assembly) => {
|
|
142
|
+
const agent = assembly?.agent
|
|
143
|
+
const session = agent?.session
|
|
144
|
+
const resolved = await resolver({ ctx, agent, session, decision: { kind: 'ok', messages: [] }, messages: [] })
|
|
145
|
+
if (resolved === null || resolved === undefined) return ''
|
|
146
|
+
const variables = { ...config.variables, ...(resolved.variables !== null && typeof resolved.variables === 'object' ? resolved.variables : {}) }
|
|
147
|
+
if (config.texts.length > 0) return interpolateVariables(config.texts.join('\n\n'), variables, session)
|
|
148
|
+
return typeof resolved.text === 'string' ? interpolateVariables(resolved.text, variables, session) : ''
|
|
149
|
+
},
|
|
150
|
+
}), `${name}: context ${config.id}`)
|
|
151
|
+
} catch (error) {
|
|
152
|
+
warnOnce(`${name}: runtime-context placeholder ${config.id} failed: ${String(error?.message ?? error)}`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** agent-request:对冻结的 LlmCallConfig 做浅合并 / 整体替换。 */
|
|
158
|
+
function wireAgentRequests(ctx, configs, warnOnce) {
|
|
159
|
+
for (const config of configs) {
|
|
160
|
+
ctx.on('agent/request', async (payload, next) => {
|
|
161
|
+
const base = await next()
|
|
162
|
+
try {
|
|
163
|
+
if (!matchesAgentScope(config, payload?.agent)) return base
|
|
164
|
+
const patch = config.params?.patch !== null && typeof config.params?.patch === 'object' && !Array.isArray(config.params.patch)
|
|
165
|
+
? config.params.patch
|
|
166
|
+
: {}
|
|
167
|
+
if (config.params?.replace === true) return { ...patch }
|
|
168
|
+
return { ...base, ...patch }
|
|
169
|
+
} catch (error) {
|
|
170
|
+
warnOnce(`${name}: agent-request config ${config.id} failed: ${String(error?.message ?? error)}`)
|
|
171
|
+
return base
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** 把流替换为提示词配置文本的最小合法 chunk 序列。 */
|
|
178
|
+
async function* replacedStream(text) {
|
|
179
|
+
yield { type: 'block-start', index: 0, blockType: 'text' }
|
|
180
|
+
yield { type: 'text-delta', index: 0, text }
|
|
181
|
+
yield { type: 'block-end', index: 0, block: { type: 'text', text } }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** llm-stream:pass 透传;replace 用提示词配置文本替代整个模型流。 */
|
|
185
|
+
function wireLlmStreams(ctx, configs, warnOnce) {
|
|
186
|
+
for (const config of configs) {
|
|
187
|
+
ctx.on('llm/stream', (options, next) => {
|
|
188
|
+
try {
|
|
189
|
+
const mode = config.params?.mode ?? 'pass'
|
|
190
|
+
if (mode === 'replace' && config.texts.length > 0 && matchesModel(config.modelScope, options?.model)) {
|
|
191
|
+
return replacedStream(config.texts.join('\n\n'))
|
|
192
|
+
}
|
|
193
|
+
return next()
|
|
194
|
+
} catch (error) {
|
|
195
|
+
warnOnce(`${name}: llm-stream config ${config.id} failed: ${String(error?.message ?? error)}`)
|
|
196
|
+
return next()
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** tool-pipeline:pre-execute 判定、execute 包装、post-execute 结果替换/阻断。 */
|
|
203
|
+
function wireToolPipelines(ctx, configs, warnOnce) {
|
|
204
|
+
for (const config of configs) {
|
|
205
|
+
const names = parseToolNames(config.params?.toolNames)
|
|
206
|
+
const matchesTool = (exec) => names.length === 0 || names.includes(exec?.name)
|
|
207
|
+
ctx.on('tools/pre-execute', async (exec, next) => {
|
|
208
|
+
try {
|
|
209
|
+
if (!matchesTool(exec) || !matchesAgentScope(config, exec?.agent)) return next()
|
|
210
|
+
const decision = config.params?.preDecision ?? 'allow'
|
|
211
|
+
if (decision === 'allow') return next()
|
|
212
|
+
if (decision === 'deny') {
|
|
213
|
+
return { kind: 'deny', reason: String(config.params?.denyReason ?? `${config.name}: denied by prompt config`) }
|
|
214
|
+
}
|
|
215
|
+
if (decision === 'ask') return { kind: 'ask' }
|
|
216
|
+
return next()
|
|
217
|
+
} catch (error) {
|
|
218
|
+
warnOnce(`${name}: tool-pipeline(pre) config ${config.id} failed: ${String(error?.message ?? error)}`)
|
|
219
|
+
return next()
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
ctx.on('tools/post-execute', async (exec, result, next) => {
|
|
223
|
+
try {
|
|
224
|
+
if (!matchesTool(exec) || !matchesAgentScope(config, exec?.agent)) return next()
|
|
225
|
+
const action = config.params?.postAction ?? 'accept'
|
|
226
|
+
if (action === 'accept') return next()
|
|
227
|
+
if (action === 'replace' && config.texts.length > 0) {
|
|
228
|
+
return { kind: 'accept', content: [{ type: 'text', text: config.texts.join('\n\n') }] }
|
|
229
|
+
}
|
|
230
|
+
if (action === 'block') {
|
|
231
|
+
return { kind: 'block', feedback: [{ type: 'text', text: config.texts.length > 0 ? config.texts.join('\n\n') : `${config.name}: blocked by prompt config` }] }
|
|
232
|
+
}
|
|
233
|
+
return next()
|
|
234
|
+
} catch (error) {
|
|
235
|
+
warnOnce(`${name}: tool-pipeline(post) config ${config.id} failed: ${String(error?.message ?? error)}`)
|
|
236
|
+
return next()
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** 把非 pre-step 提示词配置接入其声明的官方层级通道。 */
|
|
243
|
+
export function wireLayers(ctx, configs, warnOnce) {
|
|
244
|
+
wireSystemSections(ctx, configs.filter((config) => config.layer === 'system-section'), warnOnce)
|
|
245
|
+
wireRuntimeContexts(ctx, configs.filter((config) => config.layer === 'runtime-context'), warnOnce)
|
|
246
|
+
wireAgentRequests(ctx, configs.filter((config) => config.layer === 'agent-request'), warnOnce)
|
|
247
|
+
wireLlmStreams(ctx, configs.filter((config) => config.layer === 'llm-stream'), warnOnce)
|
|
248
|
+
wireToolPipelines(ctx, configs.filter((config) => config.layer === 'tool-pipeline'), warnOnce)
|
|
249
|
+
}
|
package/engine/run-code-env.mjs
CHANGED
|
@@ -18,9 +18,14 @@
|
|
|
18
18
|
* native 模式自动空转。
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { validateConfig } from './shared.mjs'
|
|
22
|
+
|
|
21
23
|
/** Cordis 插件名,供 loader 诊断使用。 */
|
|
22
24
|
export const name = 'run-code-env'
|
|
23
25
|
|
|
26
|
+
/** Every config key this plugin accepts — anything else is a typo. */
|
|
27
|
+
const ALLOWED_KEYS = new Set(['enabled', 'envKeys'])
|
|
28
|
+
|
|
24
29
|
/** 需要 tools 视图与 systemPrompt 段。shellEnv 为机会型读取,不写进 inject。 */
|
|
25
30
|
export const inject = ['tools', 'systemPrompt']
|
|
26
31
|
|
|
@@ -166,7 +171,7 @@ export function patchRunCodeTool(tool, ctx, keys) {
|
|
|
166
171
|
|
|
167
172
|
/** 注册 PTC env 注入:只在目标 scope 出现 run_code 时生效。 */
|
|
168
173
|
export function apply(ctx, config) {
|
|
169
|
-
const source = config
|
|
174
|
+
const source = validateConfig(name, config, ALLOWED_KEYS)
|
|
170
175
|
const enabled = source.enabled !== false
|
|
171
176
|
const keys = normalizeEnvKeys(source.envKeys)
|
|
172
177
|
|
|
@@ -197,7 +202,8 @@ export function apply(ctx, config) {
|
|
|
197
202
|
if (enabled) {
|
|
198
203
|
ctx.systemPrompt.section({
|
|
199
204
|
name: 'tools:run-code-env',
|
|
200
|
-
|
|
205
|
+
// alpha.1 官方 SDK 提示段固定在 5000;对齐保留在 per-tool 指导之后。
|
|
206
|
+
order: 5000,
|
|
201
207
|
text: (context) => {
|
|
202
208
|
const schemas = ctx.tools.schemas(context?.scope)
|
|
203
209
|
if (!Array.isArray(schemas) || !schemas.some((schema) => schema?.name === 'run_code')) return ''
|
package/engine/schema.mjs
CHANGED
|
@@ -120,7 +120,7 @@ export const KNOWN_FILLS = new Set(['instruction-hint', 'env-facts', 'skill-cata
|
|
|
120
120
|
/** 层能力矩阵:每个字段只在对应注入层生效。客户端表单据此动态渲染。 */
|
|
121
121
|
export const LAYER_FIELD_POLICIES = {
|
|
122
122
|
'pre-step': { position: true, dedupe: true, promotion: true, audience: true, modelScope: true, merge: true, order: true, role: true, placeholder: true },
|
|
123
|
-
'system-section': { position: false, dedupe: false, promotion: false, audience:
|
|
123
|
+
'system-section': { position: false, dedupe: false, promotion: false, audience: true, modelScope: false, merge: true, order: true, role: false, placeholder: false },
|
|
124
124
|
'runtime-context': { position: false, dedupe: false, promotion: false, audience: false, modelScope: false, merge: true, order: true, role: false, placeholder: true },
|
|
125
125
|
'agent-request': { position: false, dedupe: false, promotion: false, audience: true, modelScope: true, merge: false, order: true, role: false, placeholder: false },
|
|
126
126
|
'llm-stream': { position: false, dedupe: false, promotion: false, audience: false, modelScope: true, merge: false, order: true, role: false, placeholder: false },
|
package/engine/strategies.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { extractText } from './shared.mjs'
|
|
9
9
|
import { MATCH_LOGIC, createAnchorMatcher } from './anchor-match.mjs'
|
|
10
|
+
import { createTaskClassifier } from './classify-task.mjs'
|
|
10
11
|
import { createInstructionHintResolver, createPlaceholderResolver } from './fillers.mjs'
|
|
11
12
|
|
|
12
13
|
const name = 'prompt-config-engine'
|
|
@@ -17,30 +18,30 @@ const name = 'prompt-config-engine'
|
|
|
17
18
|
*/
|
|
18
19
|
function createFirstTurnAnchorResolver(config) {
|
|
19
20
|
const useCustom = config.params?.useCustom === true
|
|
20
|
-
|
|
21
|
+
// 自定义文本统一读 text(与 guide-auto 同契约);firstTurnText 旧键兼容。
|
|
22
|
+
const customText = typeof config.params?.text === 'string'
|
|
23
|
+
? config.params.text
|
|
24
|
+
: (typeof config.params?.firstTurnText === 'string' ? config.params.firstTurnText : '')
|
|
21
25
|
const buildPattern = typeof config.params?.buildPattern === 'string' ? config.params.buildPattern : ''
|
|
22
26
|
const complexPattern = typeof config.params?.complexPattern === 'string' ? config.params.complexPattern : ''
|
|
23
27
|
const firstTurnBuild = typeof config.params?.firstTurnBuild === 'string' ? config.params.firstTurnBuild : ''
|
|
24
28
|
const firstTurnInspect = typeof config.params?.firstTurnInspect === 'string' ? config.params.firstTurnInspect : ''
|
|
25
29
|
const firstTurnDeep = typeof config.params?.firstTurnDeep === 'string' ? config.params.firstTurnDeep : ''
|
|
26
|
-
const
|
|
27
|
-
const complexRe = complexPattern.length > 0 ? new RegExp(complexPattern, 'i') : undefined
|
|
30
|
+
const classifier = createTaskClassifier({ buildPattern, complexPattern })
|
|
28
31
|
return ({ messages }) => {
|
|
29
32
|
if (useCustom) {
|
|
30
33
|
const text = customText.trim()
|
|
31
34
|
return text.length > 0 ? { text } : null
|
|
32
35
|
}
|
|
33
|
-
if (
|
|
36
|
+
if (!classifier.ready || (firstTurnBuild.length === 0 && firstTurnInspect.length === 0 && firstTurnDeep.length === 0)) {
|
|
34
37
|
return null
|
|
35
38
|
}
|
|
36
39
|
const userIndex = messages.findIndex((message) => message?.source?.kind === 'user')
|
|
37
40
|
if (userIndex < 0) return null
|
|
38
41
|
const taskText = extractText(messages[userIndex])
|
|
39
42
|
if (taskText.length === 0) return null
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
else if (buildRe.test(taskText)) anchor = firstTurnBuild
|
|
43
|
-
else anchor = firstTurnInspect
|
|
43
|
+
const kind = classifier.classify(taskText)
|
|
44
|
+
const anchor = kind === 'complex' ? firstTurnDeep : kind === 'build' ? firstTurnBuild : firstTurnInspect
|
|
44
45
|
return anchor.length > 0 ? { text: anchor } : null
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -48,14 +49,17 @@ function createFirstTurnAnchorResolver(config) {
|
|
|
48
49
|
/**
|
|
49
50
|
* guide-auto:晋升后每轮用户消息后的弱/深度引导。
|
|
50
51
|
* 正则与引导文本全部来自 config.params(由 preset.yml 单一配置源下发)。
|
|
52
|
+
* 复杂任务判定 fallback 复用锚定功能的 complexPattern(guideComplexPattern
|
|
53
|
+
* 冗余副本已移除)——锚定与引导是独立功能,仅分类器共用。
|
|
51
54
|
*/
|
|
52
55
|
function createGuideAutoResolver(config) {
|
|
53
56
|
const useCustom = config.params?.useCustom === true
|
|
54
57
|
const customText = typeof config.params?.text === 'string' ? config.params.text : ''
|
|
55
|
-
const
|
|
58
|
+
const complexPattern = typeof config.params?.complexPattern === 'string' ? config.params.complexPattern : ''
|
|
56
59
|
const guideWeak = typeof config.params?.guideWeak === 'string' ? config.params.guideWeak : ''
|
|
57
60
|
const guideDeep = typeof config.params?.guideDeep === 'string' ? config.params.guideDeep : ''
|
|
58
|
-
|
|
61
|
+
// 复杂判定 fallback 复用锚定分类器(buildPattern 不参与引导判定)。
|
|
62
|
+
const classifier = createTaskClassifier({ complexPattern })
|
|
59
63
|
return ({ messages }) => {
|
|
60
64
|
const userIndex = messages.findIndex((message) => message?.source?.kind === 'user')
|
|
61
65
|
if (userIndex < 0) return null
|
|
@@ -66,7 +70,7 @@ function createGuideAutoResolver(config) {
|
|
|
66
70
|
return guide.length > 0 ? { text: guide } : null
|
|
67
71
|
}
|
|
68
72
|
if (guideWeak.length === 0 && guideDeep.length === 0) return null
|
|
69
|
-
return { text: (text.length > 120 ||
|
|
73
|
+
return { text: (text.length > 120 || classifier.isComplex(text)) ? guideDeep : guideWeak }
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -81,8 +85,13 @@ function createCustomFallbackResolver(config) {
|
|
|
81
85
|
const firstTurnWord = typeof config.params?.firstTurnWord === 'string' && config.params.firstTurnWord.length > 0
|
|
82
86
|
? config.params.firstTurnWord
|
|
83
87
|
: 'we'
|
|
84
|
-
//
|
|
85
|
-
|
|
88
|
+
// 确认词集合:writePreset 派生的 anchorWords 优先(锚句信号词多词 prefix,任一命中即
|
|
89
|
+
// 确认——deep 档 Let…/自定义锚句首词都覆盖);旧产物无 anchorWords 时回退 firstTurnWord 单词。
|
|
90
|
+
const anchorWords = Array.isArray(config.params?.anchorWords) && config.params.anchorWords.length > 0
|
|
91
|
+
? config.params.anchorWords.map(String).filter((word) => word.length > 0)
|
|
92
|
+
: [firstTurnWord]
|
|
93
|
+
// 锚定匹配经 anchor-match 引擎(prefix 模式:首轮 reasoning 开头命中任一确认词)。
|
|
94
|
+
const anchor = createAnchorMatcher({ keys: anchorWords, mode: 'prefix' })
|
|
86
95
|
|
|
87
96
|
const anchorScanned = new Map()
|
|
88
97
|
|
|
@@ -140,7 +149,9 @@ function createWorldBookResolver(config) {
|
|
|
140
149
|
secondaryKeys: Array.isArray(config.params?.secondaryKeys) ? config.params.secondaryKeys : [],
|
|
141
150
|
caseSensitive: config.params?.caseSensitive === true,
|
|
142
151
|
wholeWords: config.params?.wholeWords === true,
|
|
143
|
-
|
|
152
|
+
// 三态透传:undefined=ST 语义自动检测(/regex/ 或含特殊字符键按正则);
|
|
153
|
+
// 存量配置显式 true/false 仍强制对应行为。
|
|
154
|
+
useRegex: config.params?.useRegex,
|
|
144
155
|
logic,
|
|
145
156
|
})
|
|
146
157
|
// ST 语义:constant 或无任何键的条目恒注入(always active)。
|