dsh-plugin-prompt-tool 0.8.1 → 2.0.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 +84 -67
- package/engine/actions/append-context.mjs +49 -0
- package/engine/actions/assembly.mjs +150 -0
- package/engine/actions/catalog.mjs +92 -0
- package/engine/actions/content.mjs +205 -0
- package/engine/actions/decision.mjs +54 -0
- package/engine/actions/examples.mjs +11 -0
- package/engine/actions/guard.mjs +80 -0
- package/engine/actions/inbox-prepend.mjs +37 -0
- package/engine/actions/index.mjs +100 -0
- package/engine/actions/inject-text.mjs +22 -0
- package/engine/actions/pre-step-filter.mjs +55 -0
- package/engine/actions/registration.mjs +168 -0
- package/engine/actions/request-params.mjs +32 -0
- package/engine/actions/sdk-strip.mjs +30 -0
- package/engine/actions/shared.mjs +71 -0
- package/engine/actions.mjs +2 -931
- package/engine/branch.mjs +44 -0
- package/engine/character-tools.mjs +3 -6
- package/engine/compaction-epoch.mjs +31 -13
- package/engine/compositions/library/agent-instructions.yml +1 -1
- package/engine/compositions/library/command-goal.yml +1 -1
- package/engine/compositions/library/compaction.yml +1 -1
- package/engine/compositions/library/delegation-ptc.yml +1 -1
- package/engine/compositions/library/delegation.yml +1 -1
- package/engine/compositions/library/persistent-shell.yml +1 -1
- package/engine/compositions/library/planning.yml +1 -1
- package/engine/compositions/library/skill-filesystem-cordis.yml +1 -1
- package/engine/compositions/library/skill-filesystem.yml +1 -1
- package/engine/compositions/library/tool-ask-user.yml +1 -1
- package/engine/compositions/library/tool-bash.yml +1 -1
- package/engine/compositions/library/tool-cordis.yml +1 -1
- 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 -1
- package/engine/compositions/library/tool-jobs.yml +1 -1
- package/engine/compositions/library/tool-plugin-manager-disabled.yml +1 -1
- package/engine/compositions/library/tool-plugin-manager.yml +1 -1
- package/engine/compositions/library/tool-present.yml +1 -1
- package/engine/compositions/library/tool-presentation.yml +1 -1
- package/engine/compositions/library/tool-pwsh.yml +1 -1
- package/engine/compositions/library/tool-skill.yml +1 -1
- package/engine/compositions/library/tool-todo.yml +1 -1
- package/engine/compositions/library/tool-web.yml +1 -1
- package/engine/compositions/source/local/dev-tool-search.yml +11 -0
- package/engine/compositions/source/local/instruction-hint.yml +22 -1
- package/engine/compositions/source/local/rule-engine.yml +6 -0
- package/engine/condition.mjs +16 -3
- package/engine/conditions/anchor.mjs +32 -0
- package/engine/conditions/availability.mjs +47 -0
- package/engine/conditions/composite.mjs +83 -0
- package/engine/conditions/count.mjs +140 -0
- package/engine/conditions/evaluation.mjs +41 -0
- package/engine/conditions/examples.mjs +11 -0
- package/engine/conditions/index.mjs +85 -0
- package/engine/conditions/names.mjs +21 -0
- package/engine/conditions/phase.mjs +54 -0
- package/engine/conditions/preset.mjs +94 -0
- package/engine/conditions/scope.mjs +18 -0
- package/engine/conditions/session.mjs +22 -0
- package/engine/conditions/source.mjs +63 -0
- package/engine/conditions/subject.mjs +121 -0
- package/engine/conditions/text.mjs +44 -0
- package/engine/conditions/values.mjs +32 -0
- package/engine/dev-tool-search.mjs +213 -0
- package/engine/executor.mjs +93 -31
- package/engine/fillers.mjs +23 -16
- package/engine/instruction-hint.mjs +39 -23
- package/engine/layers.mjs +171 -77
- package/engine/order.mjs +20 -0
- package/engine/predicates.mjs +2 -753
- package/engine/rule-engine.mjs +40 -0
- package/engine/rule-runtime.mjs +110 -0
- package/engine/rule-spec.mjs +187 -0
- package/engine/schema.mjs +32 -11
- package/engine/session-var-tools.mjs +3 -6
- package/engine/shared.mjs +1 -1
- package/engine/st-render.mjs +8 -4
- package/engine/strategies.mjs +2 -235
- package/engine/subagent-tool-policy-core.mjs +7 -7
- package/engine/subagent-tool-policy.mjs +45 -20
- package/engine/tool-config-engine.mjs +53 -32
- package/engine/trigger-editor-meta.mjs +2 -21
- package/engine/trigger-spec.mjs +49 -133
- package/engine/world-book-tools.mjs +3 -6
- package/lib/client.js +10444 -10886
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +265 -361
- package/lib/index.mjs +1559 -2026
- package/lib/migrate-rules.d.mts +1 -0
- package/lib/migrate-rules.mjs +39 -0
- package/lib/module-package-CHPFweqK.mjs +2 -0
- package/lib/module-package-Cnd1_mwx.mjs +2872 -0
- package/lib/{paths-CY5psBqE.d.mts → paths-CyP0IkaM.d.mts} +388 -231
- package/lib/preset-transfer.d.mts +2 -2
- package/lib/preset-transfer.mjs +3 -2
- package/lib/{preset-package-BnJqrU4R.mjs → rules-migration-DJ6vASB5.mjs} +13825 -15283
- package/modules/ponytail/module.yml +406 -0
- package/modules/skill-surface/module.yml +74 -0
- package/modules/tool-surface/module.yml +67 -0
- package/package.json +50 -80
- package/templates/10-pre-step.yml +26 -22
- package/templates/14-first-turn-anchor.yml +25 -21
- package/templates/15-guide-auto.yml +34 -19
- package/templates/16-custom-fallback.yml +27 -15
- package/templates/18-placeholder-env-facts.yml +23 -14
- package/templates/20-system-section.yml +20 -11
- package/templates/30-runtime-context.yml +13 -7
- package/templates/40-agent-request.yml +11 -8
- package/templates/50-llm-stream.yml +14 -6
- package/templates/60-tool-pipeline.yml +15 -9
- package/templates/65-turn-stop.yml +19 -12
- package/templates/66-subagent-start.yml +12 -7
- package/templates/70-subagent-maintenance.yml +35 -29
- package/templates/75-subagent-end.yml +10 -5
- package/templates/80-tool-surface.yml +66 -0
- package/templates/policies/legacy-defaults.yml +34 -0
- package/templates/tools/50-delegate-worldbook.yml +1 -1
- package/engine/compositions/source/local/prompt-config-engine.yml +0 -7
- package/engine/prompt-config-engine.mjs +0 -104
- package/preset/pt-cordis/preset.yml +0 -43
- package/preset/pt-cordis/skills/cordis-composition-reference/SKILL.md +0 -29
- package/preset/pt-cordis/skills/cordis-composition-reference/references/packages.md +0 -501
- package/preset/pt-cordis/skills/cordis-plugin-development/SKILL.md +0 -38
- package/preset/pt-cordis/skills/cordis-plugin-development/references/host-plugin.md +0 -60
- package/preset/pt-cordis/skills/cordis-plugin-development/references/mcp-bundle.md +0 -5
- package/preset/pt-cordis/skills/cordis-plugin-development/references/ui-plugin.md +0 -13
- package/preset/pt-cordis/skills/cordis-plugin-development/references/verification.md +0 -5
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/client.js +0 -22
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/cordis.patch.yml +0 -3
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/index.js +0 -2
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/package.json +0 -15
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/cordis.patch.yml +0 -8
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/package.json +0 -7
- package/preset/pt-cordis/skills/editing-cordis-compositions/SKILL.md +0 -84
- package/preset/pt-custom/preset.yml +0 -15
- package/preset/pt-minimal/preset.yml +0 -27
- package/preset/pt-ptc/preset.yml +0 -45
- package/preset/pt-standard/preset.yml +0 -43
package/engine/fillers.mjs
CHANGED
|
@@ -8,13 +8,20 @@ import { createInstructionHintResolver } from './instruction-hint.mjs'
|
|
|
8
8
|
|
|
9
9
|
const name = 'prompt-config-engine'
|
|
10
10
|
|
|
11
|
+
function explicitTemplate(config) {
|
|
12
|
+
const texts = Array.isArray(config.texts) ? config.texts : typeof config.text === 'string' ? [config.text] : []
|
|
13
|
+
return texts.length > 0 ? texts.join('\n\n') : typeof config.params?.text === 'string' ? config.params.text : ''
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* env-facts:机器事实动态填充器。
|
|
13
18
|
* params.envKeys 逗号分隔环境变量白名单:取值归模板/预设,留空 = 不附加宿主环境
|
|
14
19
|
* 变量(CWD/WORKSPACE 仍由会话事实提供)。
|
|
15
|
-
*
|
|
20
|
+
* 只提供 facts 变量;正文必须由 text/params.text 显式模板声明。
|
|
16
21
|
*/
|
|
17
22
|
function createEnvFactsResolver(config) {
|
|
23
|
+
const template = explicitTemplate(config)
|
|
24
|
+
if (template.trim().length === 0) return () => null
|
|
18
25
|
const keys = parseToolNames(typeof config.params?.envKeys === 'string' ? config.params.envKeys : '')
|
|
19
26
|
return ({ agent }) => {
|
|
20
27
|
const session = agent?.session
|
|
@@ -30,8 +37,8 @@ function createEnvFactsResolver(config) {
|
|
|
30
37
|
}
|
|
31
38
|
facts.WORKSPACE = process.env.DSH_WORKSPACE ?? cwd
|
|
32
39
|
facts.CWD = cwd
|
|
33
|
-
const
|
|
34
|
-
return { text:
|
|
40
|
+
const envFacts = Object.entries(facts).map(([key, value]) => `- ${key}=${value}`).join('\n')
|
|
41
|
+
return { text: template, variables: { ...facts, ENV_FACTS: envFacts } }
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
|
|
@@ -42,21 +49,21 @@ function createEnvFactsResolver(config) {
|
|
|
42
49
|
* 跳过本配置并告警一次,绝不伤及会话。
|
|
43
50
|
*/
|
|
44
51
|
function createSkillCatalogResolver(config) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
52
|
+
const template = explicitTemplate(config)
|
|
53
|
+
if (template.trim().length === 0) return () => null
|
|
54
|
+
const rawLimit = config.params?.limit
|
|
55
|
+
const limit = rawLimit == null || rawLimit === '' ? undefined : rawLimit
|
|
56
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 0)) throw new TypeError(`${name}: skill-catalog limit must be a non-negative integer`)
|
|
57
|
+
const fields = parseToolNames(typeof config.params?.fields === 'string' ? config.params.fields : '')
|
|
49
58
|
.filter((field) => ['name', 'description', 'whenToUse'].includes(field))
|
|
59
|
+
const whenToUseLabel = typeof config.params?.whenToUseLabel === 'string' ? config.params.whenToUseLabel : ''
|
|
60
|
+
const unnamedLabel = typeof config.params?.unnamedLabel === 'string' ? config.params.unnamedLabel : ''
|
|
50
61
|
const providers = parseToolNames(config.params?.providers)
|
|
51
62
|
const emptyBehavior = config.params?.emptyBehavior === 'text' ? 'text' : 'skip'
|
|
52
63
|
// 空结果提示文案归模板/预设:留空且 emptyBehavior=text 时按 skip 处理(不注入空消息)。
|
|
53
64
|
const emptyText = typeof config.params?.emptyText === 'string' ? config.params.emptyText : ''
|
|
54
65
|
let warned = false
|
|
55
|
-
//
|
|
56
|
-
// 参数传入;换成 shared 的 createWarnOnce(ctx, name) 必须改工厂签名,而唯一
|
|
57
|
-
// 调用点 engine/strategies.mjs 的 createPlaceholderResolver(config) 不在本支
|
|
58
|
-
// 写区。为替换而扩大改动面违反 PLAN,故此处按原语义手写(一次性 + logger
|
|
59
|
-
// 不可用时不抛)。
|
|
66
|
+
// ctx 直到 resolve 时才可用;每个填充器实例只告警一次,不让日志失败伤及会话。
|
|
60
67
|
const warnOnceLocal = (ctx, message) => {
|
|
61
68
|
if (warned) return
|
|
62
69
|
warned = true
|
|
@@ -93,10 +100,10 @@ function createSkillCatalogResolver(config) {
|
|
|
93
100
|
const value = skill?.name
|
|
94
101
|
if (typeof value === 'string' && value.length > 0) return value
|
|
95
102
|
if (typeof skill?.locator === 'string' && skill.locator.length > 0) return skill.locator
|
|
96
|
-
return String(skill?.id ??
|
|
103
|
+
return String(skill?.id ?? unnamedLabel)
|
|
97
104
|
}
|
|
98
105
|
const firstLine = (value) => typeof value === 'string' ? value.split('\n')[0].trim() : ''
|
|
99
|
-
const rows = (limit === 0 ? scoped : scoped.slice(0, limit)).map((skill) => {
|
|
106
|
+
const rows = (limit === undefined || limit === 0 ? scoped : scoped.slice(0, limit)).map((skill) => {
|
|
100
107
|
const parts = []
|
|
101
108
|
if (fields.includes('name')) parts.push(nameOf(skill))
|
|
102
109
|
if (fields.includes('description')) {
|
|
@@ -105,14 +112,14 @@ function createSkillCatalogResolver(config) {
|
|
|
105
112
|
}
|
|
106
113
|
if (fields.includes('whenToUse')) {
|
|
107
114
|
const whenToUse = firstLine(skill?.whenToUse)
|
|
108
|
-
if (whenToUse.length > 0) parts.push(
|
|
115
|
+
if (whenToUse.length > 0) parts.push(`${whenToUseLabel}${whenToUse}`)
|
|
109
116
|
}
|
|
110
117
|
return parts.length === 0 ? '' : `- ${parts.join(': ')}`
|
|
111
118
|
}).filter((line) => line.length > 0)
|
|
112
119
|
const skillsText = rows.join('\n')
|
|
113
120
|
const skillsNames = scoped.map((skill) => nameOf(skill)).join(', ')
|
|
114
121
|
return {
|
|
115
|
-
text:
|
|
122
|
+
text: template,
|
|
116
123
|
variables: {
|
|
117
124
|
SKILL_COUNT: String(total),
|
|
118
125
|
SKILL_NAMES: skillsNames,
|
|
@@ -14,12 +14,15 @@
|
|
|
14
14
|
* - `enabled`: boolean,缺省 false(组合源须显式写 true)。
|
|
15
15
|
* - `promoteOn`: 'either'(缺省)| 'tool-call' | 'assistant-message'。
|
|
16
16
|
* - `includeSubagents`: boolean,缺省 false(子代理首次请求即视为已晋升)。
|
|
17
|
+
* - `messageTemplate` / `suffixTemplate`: 显式转换模板;正文模板为空时不注册转换。
|
|
18
|
+
* - `projectTemplate` / `globalTemplate`: 显式文件探测提示模板,缺省均为空。
|
|
17
19
|
*/
|
|
18
20
|
|
|
19
21
|
import { randomUUID } from 'node:crypto'
|
|
20
22
|
import { readFileSync } from 'node:fs'
|
|
21
23
|
import { createEpochPromotion } from './compaction-epoch.mjs'
|
|
22
|
-
import {
|
|
24
|
+
import { interpolateStatic } from './interpolate.mjs'
|
|
25
|
+
import { booleanOption, createWarnOnce, getService, parsePromoteOn, validateConfig } from './shared.mjs'
|
|
23
26
|
|
|
24
27
|
export const name = 'instruction-hint'
|
|
25
28
|
|
|
@@ -29,7 +32,7 @@ export const inject = []
|
|
|
29
32
|
/** 项目目录链中按优先级探测的指令文件名。 */
|
|
30
33
|
export const PROJECT_INSTRUCTION_CANDIDATES = ['AGENTS.md', 'CLAUDE.md', 'AGENTS.local.md', 'CLAUDE.local.md']
|
|
31
34
|
export const USER_GLOBAL_INSTRUCTION_CANDIDATE = 'AGENTS.md'
|
|
32
|
-
const
|
|
35
|
+
const templateOf = (templates, key) => typeof templates?.[key] === 'string' ? templates[key] : ''
|
|
33
36
|
|
|
34
37
|
/** Join one path segment onto a directory (platform-agnostic string join). */
|
|
35
38
|
export function joinPath(dir, segment) {
|
|
@@ -108,22 +111,25 @@ export async function collectInstructionFiles(fs, cwd, signal, home) {
|
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
/**
|
|
111
|
-
*
|
|
114
|
+
* 用显式模板格式化探测结果;无文件或无正文模板时返回空字符串。
|
|
112
115
|
* scope 限定只报告某一来源:all(默认)/ global($DSH_HOME/AGENTS.md)/ project(cwd→项目根链)。
|
|
113
116
|
*/
|
|
114
|
-
export function buildInstructionHintText({ root, projectFiles = [], userGlobalFiles = [], userGlobalHome } = {}, scope = 'all') {
|
|
117
|
+
export function buildInstructionHintText({ root, projectFiles = [], userGlobalFiles = [], userGlobalHome } = {}, scope = 'all', templates = {}) {
|
|
115
118
|
const sections = []
|
|
116
119
|
if (scope !== 'global' && projectFiles.length > 0) {
|
|
117
|
-
|
|
120
|
+
const text = interpolateStatic(templateOf(templates, 'projectTemplate'), { FILES: projectFiles.join(', '), ROOT: root ?? '' })
|
|
121
|
+
if (text.trim().length > 0) sections.push(text)
|
|
118
122
|
}
|
|
119
123
|
if (scope !== 'project' && userGlobalFiles.length > 0) {
|
|
120
124
|
const paths = typeof userGlobalHome === 'string' && userGlobalHome.length > 0
|
|
121
125
|
? userGlobalFiles.map((name) => joinPath(userGlobalHome, name))
|
|
122
126
|
: userGlobalFiles
|
|
123
|
-
|
|
127
|
+
const text = interpolateStatic(templateOf(templates, 'globalTemplate'), { FILES: paths.join(', '), ROOT: root ?? '' })
|
|
128
|
+
if (text.trim().length > 0) sections.push(text)
|
|
124
129
|
}
|
|
125
130
|
if (sections.length === 0) return ''
|
|
126
|
-
|
|
131
|
+
const suffix = templateOf(templates, 'suffixTemplate')
|
|
132
|
+
if (suffix.trim().length > 0) sections.push(suffix)
|
|
127
133
|
return sections.join(' ')
|
|
128
134
|
}
|
|
129
135
|
|
|
@@ -137,13 +143,13 @@ function hintScope(value) {
|
|
|
137
143
|
* prompt-config placeholder fill=instruction-hint 的 resolver。
|
|
138
144
|
* params.text(自定义提示)优先;params.file(文件卡绑定的单个指令文件)次之——
|
|
139
145
|
* 运行时读该文件正文并加 `Instructions from:` 头注入;最后按 params.scope 探测来源
|
|
140
|
-
* (all / global / project
|
|
146
|
+
* (all / global / project)按显式 projectTemplate/globalTemplate 生成提示。
|
|
147
|
+
* 未配置提示模板时返回 null;引擎不提供默认引导文案。
|
|
141
148
|
* 文件缺失、不可读或空内容时返回 null,不注入任何消息。
|
|
142
149
|
*/
|
|
143
150
|
export function createInstructionHintResolver(config = {}) {
|
|
144
|
-
const customText = typeof config?.params?.text === 'string'
|
|
145
|
-
? config.
|
|
146
|
-
: ''
|
|
151
|
+
const customText = typeof config?.params?.text === 'string' ? config.params.text.trim()
|
|
152
|
+
: Array.isArray(config.texts) ? config.texts.join('\n\n').trim() : ''
|
|
147
153
|
const scope = hintScope(config?.params?.scope)
|
|
148
154
|
const file = typeof config?.params?.file === 'string' && config.params.file.trim().length > 0
|
|
149
155
|
? config.params.file.trim()
|
|
@@ -171,11 +177,12 @@ export function createInstructionHintResolver(config = {}) {
|
|
|
171
177
|
? { id, text: `Instructions from: ${fileLabel}\n\n${content}`, source: { kind: 'instruction-file', form: 'instructions' } }
|
|
172
178
|
: null
|
|
173
179
|
}
|
|
180
|
+
if (templateOf(config.params, 'projectTemplate').trim().length === 0 && templateOf(config.params, 'globalTemplate').trim().length === 0) return null
|
|
174
181
|
const fs = ctx.get('fs')
|
|
175
182
|
if (fs === undefined) return null
|
|
176
183
|
const cwd = session.header?.cwd ?? process.cwd()
|
|
177
184
|
const found = await collectInstructionFiles(fs, cwd, agent.signal)
|
|
178
|
-
const text = buildInstructionHintText(found, scope)
|
|
185
|
+
const text = buildInstructionHintText(found, scope, config.params)
|
|
179
186
|
return text.length > 0
|
|
180
187
|
? { id, text, source: { kind: 'instruction-hint', form: 'hint' } }
|
|
181
188
|
: null
|
|
@@ -199,36 +206,41 @@ export function extractInstructionPaths(message) {
|
|
|
199
206
|
}
|
|
200
207
|
|
|
201
208
|
/** 构造一次性非命令式 hint 消息;替换已有消息时保留其 id。 */
|
|
202
|
-
export function buildInstructionHint(original, paths, sourceName = 'instruction-hint') {
|
|
209
|
+
export function buildInstructionHint(original, paths, sourceName = 'instruction-hint', templates = {}) {
|
|
210
|
+
const template = templateOf(templates, 'messageTemplate')
|
|
211
|
+
if (template.trim().length === 0) return original
|
|
212
|
+
const text = interpolateStatic(template, { FILES: paths.join(', '), SUFFIX: templateOf(templates, 'suffixTemplate') })
|
|
213
|
+
if (text.trim().length === 0) return original
|
|
203
214
|
return {
|
|
204
215
|
id: typeof original?.id === 'string' && original.id !== '' ? original.id : `instruction-hint-${randomUUID()}`,
|
|
205
216
|
role: 'user',
|
|
206
217
|
content: [{
|
|
207
218
|
type: 'text',
|
|
208
|
-
text
|
|
209
|
-
+ `Reference documents exist: ${paths.join(', ')}. ${REFERENCE_HINT_SUFFIX}`
|
|
210
|
-
+ '\n</system-reminder>',
|
|
219
|
+
text,
|
|
211
220
|
}],
|
|
212
221
|
source: { kind: 'instruction-hint', form: 'hint', plugin: sourceName },
|
|
213
222
|
}
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
/** 将 agent-instructions 全文替换为一次性 hint,后续全文消息丢弃。 */
|
|
217
|
-
export function instructionHintMessages(messages, state, sourceName = 'instruction-hint') {
|
|
226
|
+
export function instructionHintMessages(messages, state, sourceName = 'instruction-hint', templates = {}) {
|
|
227
|
+
if (templateOf(templates, 'messageTemplate').trim().length === 0) return messages
|
|
218
228
|
const kept = []
|
|
219
229
|
for (const message of messages) {
|
|
220
230
|
if (message?.source?.kind !== 'agent-instructions') {
|
|
221
231
|
kept.push(message)
|
|
222
232
|
continue
|
|
223
233
|
}
|
|
224
|
-
if (state.instructionHinted) continue
|
|
225
234
|
const paths = extractInstructionPaths(message)
|
|
226
235
|
if (paths.length === 0) {
|
|
227
|
-
kept.push(message)
|
|
236
|
+
if (!state.instructionHinted) kept.push(message)
|
|
228
237
|
continue
|
|
229
238
|
}
|
|
239
|
+
const hint = buildInstructionHint(message, paths, sourceName, templates)
|
|
240
|
+
if (hint === message) { kept.push(message); continue }
|
|
241
|
+
if (state.instructionHinted) continue
|
|
230
242
|
state.instructionHinted = true
|
|
231
|
-
kept.push(
|
|
243
|
+
kept.push(hint)
|
|
232
244
|
}
|
|
233
245
|
return kept
|
|
234
246
|
}
|
|
@@ -236,7 +248,7 @@ export function instructionHintMessages(messages, state, sourceName = 'instructi
|
|
|
236
248
|
// ── plugin 形态 ─────────────────────────────────────────────────────────────
|
|
237
249
|
|
|
238
250
|
/** Every config key this plugin accepts — anything else is a typo. */
|
|
239
|
-
const ALLOWED_KEYS = new Set(['enabled', 'promoteOn', 'includeSubagents'])
|
|
251
|
+
const ALLOWED_KEYS = new Set(['enabled', 'promoteOn', 'includeSubagents', 'projectTemplate', 'globalTemplate', 'suffixTemplate', 'messageTemplate'])
|
|
240
252
|
|
|
241
253
|
/** The visible surface, not the append-only log, owns hint lifetime. */
|
|
242
254
|
function hasVisibleInstructionHint(session) {
|
|
@@ -258,7 +270,7 @@ export function apply(ctx, config) {
|
|
|
258
270
|
const includeSubagents = booleanOption(name, source.includeSubagents, 'includeSubagents', false)
|
|
259
271
|
// 开关语义:未声明 = 关闭(组合源为本模块显式写 enabled: true)。
|
|
260
272
|
const enabled = booleanOption(name, source.enabled, 'enabled', false)
|
|
261
|
-
if (!enabled) return
|
|
273
|
+
if (!enabled || templateOf(source, 'messageTemplate').trim().length === 0) return
|
|
262
274
|
|
|
263
275
|
const promotion = createEpochPromotion(promoteEvents, { includeSubagents })
|
|
264
276
|
const warnOnce = createWarnOnce(ctx, name)
|
|
@@ -272,10 +284,14 @@ export function apply(ctx, config) {
|
|
|
272
284
|
if (!promotion.status(agent).promoted) return decision
|
|
273
285
|
if (!Array.isArray(decision.messages)) return decision
|
|
274
286
|
if (agent?.session === undefined) return decision
|
|
287
|
+
const coordinator = getService(ctx, 'promptToolPreStep')
|
|
288
|
+
const messages = typeof coordinator?.filterInstructions === 'function'
|
|
289
|
+
? coordinator.filterInstructions(agent, decision.messages)
|
|
290
|
+
: decision.messages
|
|
275
291
|
// 1 换 1 的转换不能按长度判断(长度相同仍可能已转换),
|
|
276
292
|
// instructionHintMessages 本身保留非目标消息,直接采用结果。
|
|
277
293
|
const hintState = { instructionHinted: hasVisibleInstructionHint(agent.session) }
|
|
278
|
-
return { ...decision, messages: instructionHintMessages(
|
|
294
|
+
return { ...decision, messages: instructionHintMessages(messages, hintState, name, source) }
|
|
279
295
|
} catch (error) {
|
|
280
296
|
// 转换失败不阻断会话:保留原消息。
|
|
281
297
|
warnOnce(`${name}: instruction hint conversion failed, keeping messages: ${String((error && error.message) || error)}`)
|