dsh-plugin-prompt-tool 2.1.0 → 3.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 +100 -233
- package/engine/actions/append-context.mjs +3 -24
- package/engine/actions/assembly.mjs +70 -94
- package/engine/actions/catalog.mjs +65 -28
- package/engine/actions/content.mjs +8 -152
- package/engine/actions/examples.mjs +8 -4
- package/engine/actions/inbox-prepend.mjs +4 -5
- package/engine/actions/index.mjs +17 -9
- package/engine/actions/inject-text.mjs +24 -4
- package/engine/actions/pre-step-filter.mjs +3 -4
- package/engine/actions/registration.mjs +20 -28
- package/engine/actions/request-params.mjs +21 -1
- package/engine/actions/shared.mjs +22 -13
- package/engine/actions/stream-output.mjs +26 -0
- package/engine/actions/tool-gate.mjs +31 -0
- package/engine/actions/tool-result.mjs +36 -0
- package/engine/actions/tool-visibility.mjs +248 -0
- package/engine/agent-capability.mjs +67 -0
- package/engine/anchor-match.mjs +14 -21
- package/engine/audience.d.mts +2 -0
- package/engine/audience.mjs +102 -0
- package/engine/branch.d.mts +4 -0
- package/engine/branch.mjs +61 -18
- package/engine/compaction-epoch.d.mts +25 -0
- package/engine/compaction-epoch.mjs +27 -4
- package/engine/compositions/source/local/filesystem-editor.yml +3 -3
- package/engine/compositions/source/local/instruction-hint.yml +2 -4
- package/engine/compositions/source/local/module-file-tools.yml +2 -0
- package/engine/compositions/source/local/module-import-tools.yml +4 -0
- package/engine/compositions/source/local/module-manifest-tools.yml +2 -0
- package/engine/compositions/source/local/module-rule-tools.yml +5 -0
- package/engine/compositions/source/local/module-variable-tools.yml +5 -0
- package/engine/compositions/source/local/present-as.yml +26 -0
- package/engine/compositions/source/local/tool-config-engine.yml +2 -3
- package/engine/compositions/source/local/tool-search.yml +11 -0
- package/engine/condition-facts.mjs +84 -0
- package/engine/conditions/anchor.mjs +63 -23
- package/engine/conditions/availability.mjs +1 -1
- package/engine/conditions/composite.mjs +3 -3
- package/engine/conditions/count.mjs +42 -11
- package/engine/conditions/evaluation.mjs +74 -19
- package/engine/conditions/examples.mjs +2 -1
- package/engine/conditions/index.d.mts +15 -0
- package/engine/conditions/index.mjs +23 -9
- package/engine/conditions/operators.mjs +4 -0
- package/engine/conditions/phase.mjs +9 -3
- package/engine/conditions/preset.mjs +1 -1
- package/engine/conditions/session.mjs +8 -2
- package/engine/conditions/subject.mjs +144 -65
- package/engine/conditions/text.mjs +26 -8
- package/engine/conditions/variable.mjs +83 -0
- package/engine/context-activation.d.mts +11 -0
- package/engine/{st-world-book.mjs → context-activation.mjs} +104 -64
- package/engine/delivery.d.mts +2 -0
- package/engine/delivery.mjs +93 -0
- package/engine/discovery.d.mts +38 -0
- package/engine/discovery.mjs +146 -0
- package/engine/env-safety.mjs +14 -0
- package/engine/executor.d.mts +28 -0
- package/engine/executor.mjs +290 -205
- package/engine/fillers.mjs +39 -8
- package/engine/fixed-registration.mjs +12 -0
- package/engine/history.mjs +71 -0
- package/engine/instruction-blocks.d.mts +13 -0
- package/engine/instruction-blocks.mjs +21 -0
- package/engine/instruction-files.d.mts +5 -0
- package/engine/instruction-files.mjs +97 -0
- package/engine/instruction-hint.mjs +69 -103
- package/engine/interpolate.d.mts +7 -0
- package/engine/interpolate.mjs +44 -13
- package/engine/layers.mjs +163 -227
- package/engine/machine-paths.mjs +40 -0
- package/engine/module-file-tools.mjs +8 -0
- package/engine/module-import-tools.mjs +10 -0
- package/engine/module-manifest-tools.mjs +8 -0
- package/engine/module-rule-tools.mjs +10 -0
- package/engine/module-variable-tools.mjs +10 -0
- package/engine/order.d.mts +14 -0
- package/engine/order.mjs +0 -3
- package/engine/path-safety.d.mts +2 -0
- package/engine/path-safety.mjs +58 -0
- package/engine/pre-step-source.d.mts +69 -0
- package/engine/pre-step-source.mjs +103 -0
- package/engine/present-as.mjs +77 -0
- package/engine/promotion-pool.d.mts +9 -0
- package/engine/promotion-pool.mjs +69 -0
- package/engine/record.mjs +2 -0
- package/engine/rule-engine.mjs +3 -2
- package/engine/rule-facts.d.mts +11 -0
- package/engine/rule-facts.mjs +60 -0
- package/engine/rule-runtime.d.mts +46 -0
- package/engine/rule-runtime.mjs +98 -13
- package/engine/rule-spec.d.mts +106 -0
- package/engine/rule-spec.mjs +301 -27
- package/engine/run-code-env.mjs +73 -19
- package/engine/schema.mjs +165 -243
- package/engine/sdk-strip.mjs +2 -2
- package/engine/service-names.d.mts +10 -0
- package/engine/service-names.mjs +10 -0
- package/engine/shared.d.mts +21 -0
- package/engine/shared.mjs +46 -13
- package/engine/skill-search.mjs +125 -53
- package/engine/source-kind.d.mts +9 -0
- package/engine/source-kind.mjs +16 -0
- package/engine/strategy-ids.mjs +5 -0
- package/engine/template-evaluation.d.mts +16 -0
- package/engine/template-evaluation.mjs +178 -0
- package/engine/template-frame.d.mts +16 -0
- package/engine/template-frame.mjs +69 -0
- package/engine/tool-config-engine.mjs +38 -34
- package/engine/tool-definition.d.mts +4 -0
- package/engine/tool-definition.mjs +5 -1
- package/engine/tool-git-bash.mjs +5 -1
- package/engine/tool-scope.mjs +22 -0
- package/engine/{dev-tool-search.mjs → tool-search.mjs} +63 -65
- package/engine/trigger.mjs +15 -270
- package/engine/vendor/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/engine/vendor/yaml/dist/nodes/Alias.js +25 -23
- package/engine/vendor/yaml/package.json +1 -1
- package/lib/client.js +8312 -6724
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +16 -2120
- package/lib/index.mjs +26914 -5816
- package/modules/ponytail/module.yml +83 -203
- package/modules/ptc-mode/module.yml +45 -0
- package/modules/soul/assets/core-hard.md +6 -0
- package/modules/soul/assets/criteria.md +12 -0
- package/modules/soul/assets/echo.md +12 -0
- package/modules/soul/assets/layers.md +11 -0
- package/modules/soul/assets/ledger.md +9 -0
- package/modules/soul/assets/manifest.yml +20 -0
- package/modules/soul/assets/reference/00-layers.md +27 -0
- package/modules/soul/assets/reference/01-criteria.md +32 -0
- package/modules/soul/assets/reference/02-echo.md +158 -0
- package/modules/soul/assets/reference/03-vital.md +183 -0
- package/modules/soul/assets/reference/04-manifest.json +106 -0
- package/modules/soul/assets/reference/05-doctor.md +67 -0
- package/modules/soul/assets/reference/06-reference.md +189 -0
- package/modules/soul/assets/reference/07-bundle.json +431 -0
- package/modules/soul/assets/reference/08-/350/270/251/345/235/221/345/256/236/345/275/225.md +59 -0
- package/modules/soul/assets/reference/09-dev-workflow.md +109 -0
- package/modules/soul/assets/reference/10-echo-anatomy.md +100 -0
- package/modules/soul/assets/reference/11-self.md +80 -0
- package/modules/soul/assets/reference/12-ledger.md +159 -0
- package/modules/soul/assets/reference/README.md +10 -0
- package/modules/soul/assets/templates/agents-template.md +6 -0
- package/modules/soul/assets/vital.md +9 -0
- package/modules/soul/module.yml +311 -0
- package/modules/superpowers/module.yml +122 -0
- package/modules/tool-surface/module.yml +51 -47
- package/package.json +37 -40
- package/skills/dsh-module/SKILL.md +67 -147
- package/skills/dsh-module/reference.md +113 -86
- package/templates/10-pre-step.yml +2 -3
- package/templates/14-first-turn-anchor.yml +10 -14
- package/templates/15-guide-auto.yml +32 -28
- package/templates/16-custom-fallback.yml +33 -21
- package/templates/20-system-section.yml +1 -4
- package/templates/50-llm-stream.yml +4 -10
- package/templates/60-tool-pipeline.yml +12 -10
- package/templates/63-append-context.yml +4 -6
- package/templates/65-turn-stop.yml +2 -2
- package/templates/67-inbox-prepend.yml +2 -2
- package/templates/70-subagent-maintenance.yml +9 -10
- package/templates/75-subagent-end.yml +2 -2
- package/templates/80-tool-surface.yml +41 -57
- package/templates/tools/40-delegate-module-import.yml +22 -0
- package/templates/tools/50-delegate-module-rules.yml +14 -0
- package/templates/tools/60-delegate-module-variables.yml +17 -0
- package/templates/tools/70-delegate-module-rules-edit.yml +31 -0
- package/engine/actions/decision.mjs +0 -54
- package/engine/actions/guard.mjs +0 -80
- package/engine/actions/sdk-strip.mjs +0 -30
- package/engine/character-tools.mjs +0 -8
- package/engine/classify-task.mjs +0 -53
- package/engine/compositions/source/local/character-tools.yml +0 -5
- package/engine/compositions/source/local/dev-tool-search.yml +0 -11
- package/engine/compositions/source/local/session-var-tools.yml +0 -5
- package/engine/compositions/source/local/subagent-tool-policy.yml +0 -21
- package/engine/compositions/source/local/world-book-tools.yml +0 -5
- package/engine/condition.mjs +0 -96
- package/engine/declared-triggers.mjs +0 -93
- package/engine/session-var-tools.mjs +0 -8
- package/engine/session-vars.mjs +0 -54
- package/engine/st-macros.d.mts +0 -22
- package/engine/st-macros.mjs +0 -208
- package/engine/st-render.mjs +0 -109
- package/engine/st-world-book.d.mts +0 -10
- package/engine/subagent-tool-policy-core.mjs +0 -400
- package/engine/subagent-tool-policy.mjs +0 -352
- package/engine/trigger-spec.mjs +0 -173
- package/engine/world-book-tools.mjs +0 -8
- package/lib/migrate-rules.d.mts +0 -1
- package/lib/migrate-rules.mjs +0 -610
- package/lib/module-package-DqJZaE71.mjs +0 -2
- package/lib/module-package-q2gDRu9H.mjs +0 -2869
- package/lib/rule-builder-DMrJten6.mjs +0 -14767
- package/templates/61-deny-tools-presentation.yml +0 -27
- package/templates/62-guard-deny-tools.yml +0 -20
- package/templates/policies/legacy-defaults.yml +0 -34
- package/templates/tools/40-delegate-character.yml +0 -15
- package/templates/tools/50-delegate-worldbook.yml +0 -15
- package/templates/tools/60-delegate-session-var.yml +0 -15
- package/templates/tools/70-delegate-worldbook-upsert.yml +0 -36
|
@@ -1,124 +1,68 @@
|
|
|
1
|
-
import { NAME_LIST, createMask, labelOf, requireString } from './shared.mjs'
|
|
2
|
-
import { sessionEvents } from '../shared.mjs'
|
|
3
|
-
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 语义对齐原 `tool-bootstrap.mjs` 的 `unlockedFor(session)`:某个发现工具(如
|
|
8
|
-
* `dev_tool_search`)上一次调用时把要解锁的工具名写进了自己的调用参数,本函数把它读回来
|
|
9
|
-
* 填入 allow,于是**解锁能跨请求保留**——没有它,解锁是一次性的(当次请求用完即被裁掉)。
|
|
2
|
+
* assembly —— 只改装配结果的 sections / contexts。
|
|
10
3
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - `arguments` 是模型产出的任意字符串,`JSON.parse` 必须 try/catch;解析出的必须是对象;
|
|
14
|
-
* - 只取 `key` 指向的**字符串数组**里的字符串项,非字符串项逐项丢弃,不展开对象、不 eval;
|
|
15
|
-
* - 任一步不成立就跳过该条事件,其余事件照常累积;全程无异常(读事件本身除外,由调用方兜)。
|
|
4
|
+
* 工具可见面(目录、SDK 正文、执行闸)已收进 `tool-visibility`:同一个意图不再分散到
|
|
5
|
+
* 多个动作。`target.tools` 在这里显式拒绝,避免旧声明「编译通过但静默不生效」。
|
|
16
6
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
7
|
+
* 语义保持原样:同层 sections 的 keep 与 remove 互斥;contexts 的 clear 与 add/remove 互斥;
|
|
8
|
+
* 异常一律返回未改动的装配(装配失败不得让会话失去系统段或上下文)。
|
|
19
9
|
*/
|
|
20
|
-
function fillUnlocked(into, session, tool, key, warnOnce, plugin, label) {
|
|
21
|
-
into.clear()
|
|
22
|
-
let events
|
|
23
|
-
try {
|
|
24
|
-
events = sessionEvents(session)
|
|
25
|
-
} catch (error) {
|
|
26
|
-
warnOnce(`${plugin}: assembly action ${label}: reading session events for allowFrom failed: ${String(error?.message ?? error)}`)
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
for (const event of events) {
|
|
30
|
-
if (event?.type !== 'tool/call' || event?.data?.name !== tool) continue
|
|
31
|
-
const raw = event.data.arguments
|
|
32
|
-
const parsed = typeof raw === 'string' ? safeParse(raw) : raw
|
|
33
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) continue
|
|
34
|
-
const value = parsed[key]
|
|
35
|
-
if (!Array.isArray(value)) continue
|
|
36
|
-
for (const item of value) if (typeof item === 'string' && item.length > 0) into.add(item)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
10
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return undefined
|
|
45
|
-
}
|
|
46
|
-
}
|
|
11
|
+
import { NAME_LIST, requireString } from './shared.mjs'
|
|
12
|
+
import { interpolateVariables } from '../interpolate.mjs'
|
|
13
|
+
|
|
14
|
+
const record = value => value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
47
15
|
|
|
48
16
|
export function prepareAssembly(action, plugin) {
|
|
49
17
|
const target = action.target
|
|
50
|
-
if (
|
|
51
|
-
throw new TypeError(`${plugin}: assembly requires a target object — {
|
|
18
|
+
if (!record(target)) {
|
|
19
|
+
throw new TypeError(`${plugin}: assembly requires a target object — { sections } / { contexts }`)
|
|
52
20
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
let allowExtra
|
|
56
|
-
if (allowFrom !== undefined) {
|
|
57
|
-
if (allowFrom === null || typeof allowFrom !== 'object' || Array.isArray(allowFrom)) {
|
|
58
|
-
throw new TypeError(`${plugin}: assembly.tools.allowFrom must be an object — { tool, key }`)
|
|
59
|
-
}
|
|
60
|
-
requireString(allowFrom.tool, 'assembly.tools.allowFrom.tool', plugin)
|
|
61
|
-
requireString(allowFrom.key, 'assembly.tools.allowFrom.key', plugin)
|
|
62
|
-
// deny 是黑名单、动态项只做加法,同声明的语义不可解释(且 allowFrom 已经覆盖不了 deny
|
|
63
|
-
// 的名字到底该不该出现),挂载期直接拒,而不是运行期猜。
|
|
64
|
-
if (Array.isArray(toolsSource.deny) && toolsSource.deny.length > 0) {
|
|
65
|
-
throw new TypeError(`${plugin}: assembly.tools cannot combine deny with allowFrom — 动态项只做加法,与黑名单语义冲突`)
|
|
66
|
-
}
|
|
67
|
-
allowExtra = new Set()
|
|
21
|
+
if (target.tools !== undefined) {
|
|
22
|
+
throw new TypeError(`${plugin}: assembly.target.tools 已移除 — 工具可见面改用 tool-visibility 动作(allow/deny/discovery)`)
|
|
68
23
|
}
|
|
69
|
-
const tools = toolsSource === undefined ? undefined : createMask(toolsSource, 'assembly.tools', plugin, allowExtra)
|
|
70
24
|
const sectionsAdd = target.sections?.add
|
|
71
25
|
const sectionsRemove = NAME_LIST.parse(target.sections?.remove, plugin, 'assembly.sections.remove')
|
|
72
|
-
// 白名单(2026-09-22 用户拍板「扩动作库」):`personaSectionsOnly` 一类的语义是
|
|
73
|
-
// `filter(白名单)`,而 remove 是黑名单——未列名的第三方/晚到段不会被它删掉。
|
|
74
|
-
// 两种语义**互斥**:同时声明即挂载期 fail loud,而不是静默二选一。
|
|
75
26
|
const sectionsKeep = NAME_LIST.parse(target.sections?.keep, plugin, 'assembly.sections.keep')
|
|
76
|
-
|
|
77
|
-
|
|
27
|
+
const sectionsAppend = target.sections?.append
|
|
28
|
+
if (sectionsAppend !== undefined) {
|
|
29
|
+
if (!Array.isArray(sectionsAppend)) throw new TypeError(`${plugin}: assembly.sections.append must be an array of { name, text, once? }`)
|
|
30
|
+
for (const entry of sectionsAppend) {
|
|
31
|
+
if (!record(entry)) throw new TypeError(`${plugin}: assembly.sections.append entries must be objects`)
|
|
32
|
+
for (const key of Object.keys(entry)) {
|
|
33
|
+
if (!['name', 'text', 'once'].includes(key)) throw new TypeError(`${plugin}: assembly.sections.append[] unknown field ${JSON.stringify(key)}`)
|
|
34
|
+
}
|
|
35
|
+
requireString(entry.name, 'assembly.sections.append[].name', plugin)
|
|
36
|
+
requireString(entry.text, 'assembly.sections.append[].text', plugin)
|
|
37
|
+
if (entry.once !== undefined && typeof entry.once !== 'boolean') throw new TypeError(`${plugin}: assembly.sections.append[].once must be boolean`)
|
|
38
|
+
if (sectionsRemove?.has(entry.name)) throw new TypeError(`${plugin}: assembly.sections.append target ${JSON.stringify(entry.name)} cannot also be removed`)
|
|
39
|
+
}
|
|
78
40
|
}
|
|
79
41
|
const contextsAdd = target.contexts?.add
|
|
80
42
|
const contextsRemove = NAME_LIST.parse(target.contexts?.remove, plugin, 'assembly.contexts.remove')
|
|
43
|
+
if (target.contexts?.clear !== undefined && typeof target.contexts.clear !== 'boolean') {
|
|
44
|
+
throw new TypeError(`${plugin}: assembly.contexts.clear must be boolean`)
|
|
45
|
+
}
|
|
81
46
|
const contextsClear = target.contexts?.clear === true
|
|
82
47
|
for (const [key, list] of [['sections.add', sectionsAdd], ['contexts.add', contextsAdd]]) {
|
|
83
48
|
if (list === undefined) continue
|
|
84
49
|
if (!Array.isArray(list)) throw new TypeError(`${plugin}: assembly.${key} must be an array of { name, text }`)
|
|
85
50
|
for (const entry of list) {
|
|
86
|
-
if (entry
|
|
51
|
+
if (!record(entry)) throw new TypeError(`${plugin}: assembly.${key} entries must be objects`)
|
|
87
52
|
requireString(entry.name, `assembly.${key}[].name`, plugin)
|
|
88
53
|
requireString(entry.text, `assembly.${key}[].text`, plugin)
|
|
89
54
|
}
|
|
90
55
|
}
|
|
56
|
+
if (contextsClear && (contextsAdd !== undefined || contextsRemove !== undefined)) {
|
|
57
|
+
throw new TypeError(`${plugin}: assembly.contexts.clear cannot combine with add or remove — clear 已清空全部条目,同时声明语义不明`)
|
|
58
|
+
}
|
|
59
|
+
if (sectionsKeep !== undefined && sectionsRemove !== undefined) {
|
|
60
|
+
throw new TypeError(`${plugin}: assembly.sections cannot combine keep with remove — 一种是白名单、一种是黑名单,同时声明语义不明`)
|
|
61
|
+
}
|
|
91
62
|
return (_ctx, { warnOnce, on, collect, take }) => collect(on('system-prompt/assemble', async (assembly, context, next) => {
|
|
92
|
-
// Downstream errors propagate untouched; only this action's own logic is guarded.
|
|
93
63
|
const assembled = await next()
|
|
94
64
|
try {
|
|
95
|
-
if (typeof action.match === 'function' && action.match(context, assembled) !== true) return assembled
|
|
96
65
|
let result = assembled
|
|
97
|
-
if (tools !== undefined && Array.isArray(result.tools)) {
|
|
98
|
-
// 每轮重算解锁名单:本轮装配要反映**本会话至今**的全部解锁调用。
|
|
99
|
-
if (allowExtra !== undefined) {
|
|
100
|
-
fillUnlocked(allowExtra, context?.session ?? context?.agent?.session, allowFrom.tool, allowFrom.key, warnOnce, plugin, labelOf(action))
|
|
101
|
-
}
|
|
102
|
-
// keep 名单兜底(2026-09-22 用户拍板,**可选**):判据与原 `tool-bootstrap` 逐条对齐——
|
|
103
|
-
// 原模块是 `missing = required.filter(t => !available.has(t))`
|
|
104
|
-
// 且 `missing.length > 0` 即放弃裁剪,也就是「**任意一个** 名单工具缺失」而不是
|
|
105
|
-
// 「一个都不存在」(对拍实测:目录 `[bash,read]` + 名单 `[bash,str_replace_editor]` 时
|
|
106
|
-
// 原模块给完整目录,只判"全缺"会裁成 `[bash]`)。
|
|
107
|
-
// 声明写 `requireMatch: true` 才启用;**不写就照名单裁**(与 `tool-filter.mjs` 一致)。
|
|
108
|
-
const available = new Set(result.tools.map((tool) => tool?.name).filter((name) => typeof name === 'string'))
|
|
109
|
-
// 必需名单 = 静态 allow ∪ 本轮解锁项:解锁项同样是「本该存在」的工具,缺了就 fail-open。
|
|
110
|
-
const required = tools.allow === undefined && allowExtra === undefined
|
|
111
|
-
? undefined
|
|
112
|
-
: new Set([...(tools.allow ?? []), ...(allowExtra ?? [])])
|
|
113
|
-
const missingRequired = tools.requireMatch && required !== undefined
|
|
114
|
-
&& [...required].some((name) => !available.has(name))
|
|
115
|
-
if (missingRequired) {
|
|
116
|
-
warnOnce(`${plugin}: assembly action ${labelOf(action)}: a requireMatch tool is absent — exposing the full tool set (fail-open)`)
|
|
117
|
-
} else {
|
|
118
|
-
const kept = result.tools.filter((tool) => !tools.blocks(tool?.name))
|
|
119
|
-
if (kept.length !== result.tools.length) result = { ...result, tools: kept }
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
66
|
if (Array.isArray(result.sections) && (sectionsAdd !== undefined || sectionsRemove !== undefined || sectionsKeep !== undefined)) {
|
|
123
67
|
const kept = sectionsKeep !== undefined
|
|
124
68
|
? result.sections.filter((section) => sectionsKeep.has(section?.name))
|
|
@@ -129,6 +73,38 @@ export function prepareAssembly(action, plugin) {
|
|
|
129
73
|
result = { ...result, sections: sectionsAdd === undefined ? kept : [...kept, ...sectionsAdd] }
|
|
130
74
|
}
|
|
131
75
|
}
|
|
76
|
+
if (Array.isArray(result.sections) && sectionsAppend !== undefined && sectionsAppend.length > 0) {
|
|
77
|
+
const seen = new Set()
|
|
78
|
+
let nextSections = result.sections
|
|
79
|
+
for (const entry of sectionsAppend) {
|
|
80
|
+
const index = nextSections.findIndex((section) => section?.name === entry.name)
|
|
81
|
+
if (index < 0) {
|
|
82
|
+
warnOnce(`${plugin}: assembly.sections.append target ${JSON.stringify(entry.name)} not found; skipped`)
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
85
|
+
const section = nextSections[index]
|
|
86
|
+
if (typeof section?.text !== 'string') {
|
|
87
|
+
warnOnce(`${plugin}: assembly.sections.append target ${JSON.stringify(entry.name)} has no text; skipped`)
|
|
88
|
+
continue
|
|
89
|
+
}
|
|
90
|
+
const rendered = interpolateVariables(entry.text, {}, context?.agent?.session)
|
|
91
|
+
if (rendered.trim().length === 0 || /\{\{[^}]+\}\}/.test(rendered)) {
|
|
92
|
+
warnOnce(`${plugin}: assembly.sections.append target ${JSON.stringify(entry.name)} rendered empty; skipped`)
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
if (entry.once !== false) {
|
|
96
|
+
const key = `${entry.name}\u0000${rendered}`
|
|
97
|
+
// once 覆盖同一装配内的重复动作/条目;已有贡献者追加过同一段正文时也不重复。
|
|
98
|
+
if (seen.has(key) || section.text.includes(rendered)) continue
|
|
99
|
+
seen.add(key)
|
|
100
|
+
}
|
|
101
|
+
const text = section.text.length === 0 ? rendered : `${section.text}\n${rendered}`
|
|
102
|
+
if (text === section.text) continue
|
|
103
|
+
if (nextSections === result.sections) nextSections = [...result.sections]
|
|
104
|
+
nextSections[index] = { ...section, text }
|
|
105
|
+
}
|
|
106
|
+
if (nextSections !== result.sections) result = { ...result, sections: nextSections }
|
|
107
|
+
}
|
|
132
108
|
if (Array.isArray(result.contexts)) {
|
|
133
109
|
if (contextsClear) {
|
|
134
110
|
if (result.contexts.length > 0) result = { ...result, contexts: [] }
|
|
@@ -141,9 +117,9 @@ export function prepareAssembly(action, plugin) {
|
|
|
141
117
|
}
|
|
142
118
|
}
|
|
143
119
|
}
|
|
144
|
-
return result !== assembled
|
|
120
|
+
return result !== assembled ? result : assembled
|
|
145
121
|
} catch (error) {
|
|
146
|
-
warnOnce(`${plugin}: assembly action
|
|
122
|
+
warnOnce(`${plugin}: assembly action failed, keeping the assembled sections/contexts: ${String(error?.message ?? error)}`)
|
|
147
123
|
return assembled
|
|
148
124
|
}
|
|
149
125
|
}))
|
|
@@ -6,69 +6,97 @@ export const ACTION_DEGRADE = Object.freeze({
|
|
|
6
6
|
})
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* `events`
|
|
11
|
-
* `
|
|
9
|
+
* 十类动作的声明:**合法事件通道** + **降级语义** + 触发时机 + **声明字段**。
|
|
10
|
+
* `events` 是动作允许注册的官方事件:经 `on(...)` 注册的动作(`channelBinder`)注册到声明外的
|
|
11
|
+
* 事件在挂载期 fail loud;`inject-text` 不经 `channelBinder`,它的真实通道由
|
|
12
|
+
* `actionExecutionPoint`(派生自 `schema.mjs` 每层的 `channel`)决定——下面那行 `events`
|
|
13
|
+
* 对它只是**文档面**(零校验、零消费),判断层是否可注入不要以它为准;
|
|
14
|
+
* `services` 是它允许触碰的宿主服务方法(同一纪律的文档面);
|
|
15
|
+
* `fields` 是该类动作自己的声明键(白名单写在这里**一份**,`rule-spec.mjs` 的未知键
|
|
16
|
+
* 检查从这里派生)。通用键 `id/kind/channelOrder/waterfallPosition/maxPerTurn` 由
|
|
17
|
+
* {@link ACTION_FIELDS} 统一补上;`if`/`then`/`else` 刻意不在名单里——带 `kind`
|
|
18
|
+
* 的动作写分支键会被编译期点名拒绝(`expandActions` 只认无 `kind` 的分支节点)。
|
|
12
19
|
*/
|
|
13
20
|
export const ACTION_KINDS = Object.freeze({
|
|
14
21
|
'inject-text': {
|
|
15
22
|
title: '注入文本',
|
|
16
|
-
|
|
23
|
+
fields: ['config', 'options'],
|
|
24
|
+
events: ['agent/pre-step', 'session/event', 'system-prompt/assemble', 'agent/turn-stopping', 'subagent/start', 'subagent/end'],
|
|
17
25
|
services: ['systemPrompt.section', 'systemPrompt.context', 'systemPrompt.variable'],
|
|
18
26
|
degrade: ACTION_DEGRADE.keep,
|
|
19
27
|
timing: '按配置声明的 layer 落到该层官方通道;pre-step 在 agent/pre-step,system-section/runtime-context 在注册期 + 官方 assembly。',
|
|
20
|
-
note: '
|
|
28
|
+
note: '复用既有注入通道(见 schema 的 `injectionLayers`),不新开通道;工具链的裁决与追加上下文走 `tool-gate` / `tool-result` / `append-context`,请求参数走 `request-params`,模型流走 `stream-output`。显式空正文不注册;`session/event` 是 pre-step 执行器自带的会话态旁听,不是第二条注入通道。',
|
|
21
29
|
},
|
|
22
30
|
assembly: {
|
|
23
31
|
title: '改装配',
|
|
32
|
+
fields: ['target', 'match'],
|
|
24
33
|
events: ['system-prompt/assemble'],
|
|
25
34
|
services: [],
|
|
26
35
|
degrade: ACTION_DEGRADE.exposeAll,
|
|
27
36
|
timing: 'system-prompt/assemble 的下游结算之后(`await next()` 之后改,装配事实已就绪)。',
|
|
28
37
|
note: '失败时返回未改动的装配 = 对 tools 即暴露完整目录;不改 global,只作用于本次 assembly。',
|
|
29
38
|
},
|
|
30
|
-
|
|
31
|
-
title: '
|
|
32
|
-
|
|
39
|
+
'tool-gate': {
|
|
40
|
+
title: '工具裁决',
|
|
41
|
+
fields: ['decision', 'reason'],
|
|
42
|
+
events: ['tools/pre-execute'],
|
|
33
43
|
services: [],
|
|
34
44
|
degrade: ACTION_DEGRADE.keep,
|
|
35
|
-
timing: 'pre 相在工具执行之前返回 allow|deny|ask
|
|
36
|
-
note: '异常与条件未命中一律 `next()
|
|
45
|
+
timing: 'pre 相在工具执行之前返回 allow|deny|ask。',
|
|
46
|
+
note: '异常与条件未命中一律 `next()`(放行),绝不让裁决 bug 卡死调用;判据只看本相真正读的字段。',
|
|
37
47
|
},
|
|
38
|
-
'
|
|
39
|
-
title: '
|
|
40
|
-
|
|
48
|
+
'tool-result': {
|
|
49
|
+
title: '工具结果处理',
|
|
50
|
+
fields: ['action', 'text'],
|
|
51
|
+
events: ['tools/post-execute'],
|
|
41
52
|
services: [],
|
|
42
53
|
degrade: ACTION_DEGRADE.keep,
|
|
43
|
-
timing: '
|
|
44
|
-
note: '
|
|
54
|
+
timing: 'post 相在结果结算之后返回 accept|replace|block。',
|
|
55
|
+
note: '异常一律 `next()`(接受);`text` 仅 replace / block 使用,accept 时被忽略。',
|
|
45
56
|
},
|
|
46
|
-
|
|
47
|
-
title: '
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
'append-context': {
|
|
58
|
+
title: '追加上下文',
|
|
59
|
+
fields: ['text', 'match'],
|
|
60
|
+
events: ['tools/post-execute'],
|
|
61
|
+
services: [],
|
|
62
|
+
degrade: ACTION_DEGRADE.keep,
|
|
63
|
+
timing: '在 post-execute 追加 durable user 通知。',
|
|
64
|
+
note: '无正文时不追加;绝不伪造 assistant 角色。',
|
|
53
65
|
},
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
|
|
67
|
+
'tool-visibility': {
|
|
68
|
+
title: '工具可见面',
|
|
69
|
+
fields: ['allow', 'deny', 'requireMatch', 'discovery'],
|
|
56
70
|
events: ['system-prompt/assemble'],
|
|
57
|
-
services: [],
|
|
71
|
+
services: ['tools.schemas', 'tools.restrict', 'tools.guard', 'tools.register'],
|
|
58
72
|
degrade: ACTION_DEGRADE.keep,
|
|
59
|
-
timing: 'system-prompt/assemble
|
|
60
|
-
note: '
|
|
73
|
+
timing: 'system-prompt/assemble 的下游结算之后:按当前条件重算目录、SDK 正文与执行闸;条件翻转时撤销上一轮注册的闸。',
|
|
74
|
+
note: '一份声明拥有工具的可见面:allow/deny 名单、requireMatch、发现工具。名单语义对齐官方 '
|
|
75
|
+
+ 'ToolRestriction 的 and/or(同时给时先按 allow 保留、再按 deny 剔除)。deny 走 guard(本层与晚到工具)'
|
|
76
|
+
+ '与 restrict(继承的全局工具);allow 只有 restrict 能覆盖全局工具,本层注册无法在执行层「只保留」——'
|
|
77
|
+
+ '这是官方 restrict 的边界,不是本动作的取舍。',
|
|
61
78
|
},
|
|
62
79
|
'request-params': {
|
|
63
80
|
title: '改模型请求参数',
|
|
81
|
+
fields: ['patch', 'unset', 'replace'],
|
|
64
82
|
events: ['agent/request'],
|
|
65
83
|
services: [],
|
|
66
84
|
degrade: ACTION_DEGRADE.keep,
|
|
67
85
|
timing: 'agent/request 的下游结算之后(`await next()` 拿到已冻结的 LlmCallConfig 再改写)。',
|
|
68
|
-
note: '
|
|
86
|
+
note: '请求改写的唯一入口;删键仅删本声明指定的那个值。',
|
|
87
|
+
},
|
|
88
|
+
'stream-output': {
|
|
89
|
+
title: '控制模型输出流',
|
|
90
|
+
fields: ['mode', 'text'],
|
|
91
|
+
events: ['llm/stream'],
|
|
92
|
+
services: [],
|
|
93
|
+
degrade: ACTION_DEGRADE.keep,
|
|
94
|
+
timing: 'llm/stream 下游调用前决定透传或替换。',
|
|
95
|
+
note: '替换输出为合法文本 chunk,不改冻结的请求;空正文透传。',
|
|
69
96
|
},
|
|
70
97
|
'inbox-prepend': {
|
|
71
98
|
title: '前置收件箱消息',
|
|
99
|
+
fields: ['target', 'text', 'match'],
|
|
72
100
|
events: ['agent/inbox/inserted'],
|
|
73
101
|
services: ['agent.inbox.prepend'],
|
|
74
102
|
degrade: ACTION_DEGRADE.silent,
|
|
@@ -78,6 +106,7 @@ export const ACTION_KINDS = Object.freeze({
|
|
|
78
106
|
},
|
|
79
107
|
'pre-step-filter': {
|
|
80
108
|
title: '过滤 pre-step 注入消息',
|
|
109
|
+
fields: ['sources', 'keepKinds', 'blockPlugins'],
|
|
81
110
|
events: ['agent/pre-step'],
|
|
82
111
|
services: [],
|
|
83
112
|
degrade: ACTION_DEGRADE.exposeAll,
|
|
@@ -90,3 +119,11 @@ export const ACTION_KINDS = Object.freeze({
|
|
|
90
119
|
+ '**永不吞上下文**:异常一律返回未过滤的 decision(`expose-all`)。',
|
|
91
120
|
},
|
|
92
121
|
})
|
|
122
|
+
|
|
123
|
+
/** 十类动作共用键:身份、同点定位与每轮预算。 */
|
|
124
|
+
const COMMON_ACTION_FIELDS = ['id', 'kind', 'channelOrder', 'waterfallPosition', 'maxPerTurn']
|
|
125
|
+
|
|
126
|
+
/** 每类动作的允许键集合(通用键 + `ACTION_KINDS[kind].fields`)。 */
|
|
127
|
+
export const ACTION_FIELDS = Object.freeze(Object.fromEntries(
|
|
128
|
+
Object.entries(ACTION_KINDS).map(([kind, meta]) => [kind, new Set([...COMMON_ACTION_FIELDS, ...meta.fields])]),
|
|
129
|
+
))
|
|
@@ -1,159 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* strategies — 引擎内容策略绑定(config.resolve)。
|
|
3
|
-
* 内容策略: static / placeholder /
|
|
3
|
+
* 内容策略: static / placeholder / context-activation。
|
|
4
4
|
* 策略参数全部来自 config.params(由 preset.yml 单一配置源下发),引擎只负责组装。
|
|
5
5
|
* 仍支持 strategyDir 懒加载自定义模板策略。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { extractText } from '../shared.mjs'
|
|
9
|
-
import { MATCH_LOGIC, createAnchorMatcher } from '../anchor-match.mjs'
|
|
10
|
-
import { createTaskClassifier } from '../classify-task.mjs'
|
|
11
8
|
import { createPlaceholderResolver } from '../fillers.mjs'
|
|
12
|
-
import { createAnchorState } from '../conditions/anchor.mjs'
|
|
13
9
|
|
|
14
10
|
const name = 'prompt-config-engine'
|
|
15
11
|
|
|
16
|
-
/**
|
|
17
|
-
* first-turn-anchor:首条真实用户消息后的一次性任务锚点。
|
|
18
|
-
* 正则与锚句文本全部来自 config.params(由 preset.yml 单一配置源下发)。
|
|
19
|
-
*/
|
|
20
|
-
function createFirstTurnAnchorResolver(config) {
|
|
21
|
-
const useCustom = config.params?.useCustom === true
|
|
22
|
-
const customText = typeof config.params?.text === 'string' ? config.params.text : ''
|
|
23
|
-
const buildPattern = typeof config.params?.buildPattern === 'string' ? config.params.buildPattern : ''
|
|
24
|
-
const complexPattern = typeof config.params?.complexPattern === 'string' ? config.params.complexPattern : ''
|
|
25
|
-
const firstTurnBuild = typeof config.params?.firstTurnBuild === 'string' ? config.params.firstTurnBuild : ''
|
|
26
|
-
const firstTurnInspect = typeof config.params?.firstTurnInspect === 'string' ? config.params.firstTurnInspect : ''
|
|
27
|
-
const firstTurnDeep = typeof config.params?.firstTurnDeep === 'string' ? config.params.firstTurnDeep : ''
|
|
28
|
-
const classifier = createTaskClassifier({ buildPattern, complexPattern })
|
|
29
|
-
return ({ messages }) => {
|
|
30
|
-
if (useCustom) {
|
|
31
|
-
const text = customText.trim()
|
|
32
|
-
return text.length > 0 ? { text } : null
|
|
33
|
-
}
|
|
34
|
-
if (!classifier.ready || (firstTurnBuild.length === 0 && firstTurnInspect.length === 0 && firstTurnDeep.length === 0)) {
|
|
35
|
-
return null
|
|
36
|
-
}
|
|
37
|
-
const userIndex = messages.findIndex((message) => message?.source?.kind === 'user')
|
|
38
|
-
if (userIndex < 0) return null
|
|
39
|
-
const taskText = extractText(messages[userIndex])
|
|
40
|
-
if (taskText.length === 0) return null
|
|
41
|
-
const kind = classifier.classify(taskText)
|
|
42
|
-
const anchor = kind === 'complex' ? firstTurnDeep : kind === 'build' ? firstTurnBuild : firstTurnInspect
|
|
43
|
-
return anchor.length > 0 ? { text: anchor } : null
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* guide-auto:晋升后每轮用户消息后的弱/深度引导。
|
|
49
|
-
* 正则与引导文本全部来自 config.params(由 preset.yml 单一配置源下发)。
|
|
50
|
-
* 复杂任务判定 fallback 复用锚定功能的 complexPattern(guideComplexPattern
|
|
51
|
-
* 冗余副本已移除)——锚定与引导是独立功能,仅分类器共用。
|
|
52
|
-
*/
|
|
53
|
-
function createGuideAutoResolver(config) {
|
|
54
|
-
const useCustom = config.params?.useCustom === true
|
|
55
|
-
const customText = typeof config.params?.text === 'string' ? config.params.text : ''
|
|
56
|
-
const complexPattern = typeof config.params?.complexPattern === 'string' ? config.params.complexPattern : ''
|
|
57
|
-
const guideWeak = typeof config.params?.guideWeak === 'string' ? config.params.guideWeak : ''
|
|
58
|
-
const guideDeep = typeof config.params?.guideDeep === 'string' ? config.params.guideDeep : ''
|
|
59
|
-
const rawMinChars = config.params?.complexMinChars
|
|
60
|
-
const complexMinChars = rawMinChars == null || rawMinChars === '' ? undefined : rawMinChars
|
|
61
|
-
if (complexMinChars !== undefined && (!Number.isSafeInteger(complexMinChars) || complexMinChars < 0)) throw new TypeError(`${name}: guide-auto complexMinChars must be a non-negative integer`)
|
|
62
|
-
// 复杂判定 fallback 复用锚定分类器(buildPattern 不参与引导判定)。
|
|
63
|
-
const classifier = createTaskClassifier({ complexPattern })
|
|
64
|
-
return ({ messages }) => {
|
|
65
|
-
const userIndex = messages.findIndex((message) => message?.source?.kind === 'user')
|
|
66
|
-
if (userIndex < 0) return null
|
|
67
|
-
const text = extractText(messages[userIndex])
|
|
68
|
-
if (text.length === 0) return null
|
|
69
|
-
if (useCustom) {
|
|
70
|
-
const guide = customText.trim()
|
|
71
|
-
return guide.length > 0 ? { text: guide } : null
|
|
72
|
-
}
|
|
73
|
-
if (guideWeak.length === 0 && guideDeep.length === 0) return null
|
|
74
|
-
return { text: ((complexMinChars !== undefined && text.length > complexMinChars) || classifier.isComplex(text)) ? guideDeep : guideWeak }
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* anchor-notice 只生成显式正文与来源说明;资格由规则条件单独负责。
|
|
80
|
-
*/
|
|
81
|
-
function createAnchorNoticeResolver(config) {
|
|
82
|
-
const promptText = config.texts.length > 0
|
|
83
|
-
? config.texts.join('\n\n')
|
|
84
|
-
: (typeof config.params?.text === 'string' && config.params.text.length > 0 ? config.params.text : undefined)
|
|
85
|
-
// 确认词无内置默认(默认值归模板/预设):空 = 不做词确认,只走未命中兜底注入。
|
|
86
|
-
const firstTurnWord = typeof config.params?.firstTurnWord === 'string' ? config.params.firstTurnWord : ''
|
|
87
|
-
// 确认词集合:writePreset 派生的 anchorWords 优先(锚句信号词多词 prefix,任一命中即
|
|
88
|
-
// 确认——deep 档 Let…/自定义锚句首词都覆盖);无 anchorWords 时用显式确认词。
|
|
89
|
-
const anchorWords = Array.isArray(config.params?.anchorWords) && config.params.anchorWords.length > 0
|
|
90
|
-
? config.params.anchorWords.map(String).filter((word) => word.length > 0)
|
|
91
|
-
: (firstTurnWord.length > 0 ? [firstTurnWord] : [])
|
|
92
|
-
const inspect = createAnchorState({ keys: anchorWords })
|
|
93
|
-
|
|
94
|
-
return ({ agent }) => {
|
|
95
|
-
if (promptText === undefined) return null
|
|
96
|
-
const { confirmed } = inspect(agent.session)
|
|
97
|
-
return {
|
|
98
|
-
text: promptText,
|
|
99
|
-
source: {
|
|
100
|
-
// v4 要求 kind 是生产者名:裸 `plugin` 是退役包装,会被会话格式校验拒绝。
|
|
101
|
-
kind: `plugin:${config.id}`,
|
|
102
|
-
plugin: config.id,
|
|
103
|
-
form: 'notice',
|
|
104
|
-
summary: confirmed
|
|
105
|
-
? (firstTurnWord.length > 0 ? `prompt-tool 提示词(「${firstTurnWord}」锚定确认后注入)` : 'prompt-tool 提示词(无确认词,锚定就绪后注入)')
|
|
106
|
-
: (firstTurnWord.length > 0 ? `prompt-tool 提示词(「${firstTurnWord}」未确认,兜底注入)` : 'prompt-tool 提示词(无确认词,兜底注入)'),
|
|
107
|
-
},
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* world-book:世界书条目(角色卡 lorebook)。constant=true 恒注入(不扫 keys);
|
|
114
|
-
* selective 条目扫描当前消息批文本,keys/secondaryKeys 按 selectiveLogic 组合
|
|
115
|
-
* (any=任一命中 / all=副键全中 / not=副键全不中)。匹配经 anchor-match 引擎。
|
|
116
|
-
*/
|
|
117
|
-
function createWorldBookResolver(config) {
|
|
118
|
-
if (config.params?.stWorldBook) return ({ stWorldBookSelected }) => stWorldBookSelected === true ? { text: config.texts.join('\n\n') } : null
|
|
119
|
-
const constant = config.params?.constant === true
|
|
120
|
-
// selectiveLogic:ST world_info_logic 0=AND_ANY 1=NOT_ALL 2=NOT_ANY 3=AND_ALL。
|
|
121
|
-
const rawLogic = config.params?.selectiveLogic
|
|
122
|
-
const logic = rawLogic === 3
|
|
123
|
-
? MATCH_LOGIC.ALL
|
|
124
|
-
: (rawLogic === 1
|
|
125
|
-
? MATCH_LOGIC.NOT
|
|
126
|
-
: (rawLogic === 2 ? MATCH_LOGIC.NOT_ANY : MATCH_LOGIC.ANY))
|
|
127
|
-
const matcher = createAnchorMatcher({
|
|
128
|
-
keys: Array.isArray(config.params?.keys) ? config.params.keys : [],
|
|
129
|
-
secondaryKeys: Array.isArray(config.params?.secondaryKeys) ? config.params.secondaryKeys : [],
|
|
130
|
-
caseSensitive: config.params?.caseSensitive === true,
|
|
131
|
-
wholeWords: config.params?.wholeWords === true,
|
|
132
|
-
// 三态透传:undefined=ST 语义自动检测(/regex/ 或含特殊字符键按正则);
|
|
133
|
-
// 存量配置显式 true/false 仍强制对应行为。
|
|
134
|
-
useRegex: config.params?.useRegex,
|
|
135
|
-
logic,
|
|
136
|
-
})
|
|
137
|
-
// ST 语义:constant 或无任何键的条目恒注入(always active)。
|
|
138
|
-
const hasAnyKey = (Array.isArray(config.params?.keys) ? config.params.keys : [])
|
|
139
|
-
.some((key) => String(key).trim().length > 0)
|
|
140
|
-
|| (Array.isArray(config.params?.secondaryKeys) ? config.params.secondaryKeys : [])
|
|
141
|
-
.some((key) => String(key).trim().length > 0)
|
|
142
|
-
const promptText = config.texts.length > 0
|
|
143
|
-
? config.texts.join('\n\n')
|
|
144
|
-
: (typeof config.params?.text === 'string' && config.params.text.length > 0 ? config.params.text : undefined)
|
|
145
|
-
return ({ messages }) => {
|
|
146
|
-
if (promptText === undefined) return null
|
|
147
|
-
if (constant || !hasAnyKey) return { text: promptText }
|
|
148
|
-
const haystack = (Array.isArray(messages) ? messages : [])
|
|
149
|
-
.map((message) => extractText(message))
|
|
150
|
-
.filter((text) => text.length > 0)
|
|
151
|
-
.join('\n')
|
|
152
|
-
if (haystack.length === 0) return null
|
|
153
|
-
return matcher.scan(haystack).active ? { text: promptText } : null
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
12
|
/**
|
|
158
13
|
* 为归一化后的提示词配置绑定策略 resolve(策略状态随提示词配置对象,不随 apply)。
|
|
159
14
|
* strategyDir:外部策略模块目录(相对本文件 URL)。未声明时只允许内置策略。
|
|
@@ -161,14 +16,13 @@ function createWorldBookResolver(config) {
|
|
|
161
16
|
export function bindResolver(config, strategyDir) {
|
|
162
17
|
switch (config.strategy) {
|
|
163
18
|
case 'placeholder': return createPlaceholderResolver(config)
|
|
164
|
-
case '
|
|
165
|
-
case 'guide-auto': return createGuideAutoResolver(config)
|
|
19
|
+
case 'guide-auto':
|
|
166
20
|
case 'custom-fallback':
|
|
167
|
-
|
|
21
|
+
case 'first-turn-anchor':
|
|
168
22
|
case 'anchor-notice':
|
|
169
|
-
|
|
170
|
-
case '
|
|
171
|
-
return
|
|
23
|
+
throw new TypeError(`${name}: retired strategy ${config.strategy}; use rule conditions and static content`)
|
|
24
|
+
case 'context-activation':
|
|
25
|
+
return ({ contextActivationSelected }) => contextActivationSelected === true ? { text: config.texts.join('\n\n') } : null
|
|
172
26
|
case 'static': {
|
|
173
27
|
const texts = config.texts
|
|
174
28
|
const patch = config.templatePatch ?? {}
|
|
@@ -198,6 +52,8 @@ export function bindResolver(config, strategyDir) {
|
|
|
198
52
|
if (typeof make !== 'function') {
|
|
199
53
|
throw new TypeError(`${name}: strategy module ${moduleUrl.href} must export createResolver(config)`)
|
|
200
54
|
}
|
|
55
|
+
// 每次求值重建工厂产物(不缓存 closure):缓存会同时改变闭包状态跨调用、文件热刷新、
|
|
56
|
+
// 跨会话串扰三项可观察行为——模块本身仍按上面缓存一次,只省 import。
|
|
201
57
|
return make(config)(args)
|
|
202
58
|
}
|
|
203
59
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export const ACTION_EXAMPLES = {
|
|
2
2
|
'inject-text': { config: { id: 'notice', layer: 'pre-step', text: '' } },
|
|
3
3
|
assembly: { target: {} },
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'
|
|
4
|
+
// 工具链拆成两个动作:pre 相的 tool-gate 与 post 相的 tool-result。种子取引擎缺省,
|
|
5
|
+
// 中性值不替用户选业务值:`reason: ''` 与缺键同义,都落引擎缺省原因(用 `||`)。
|
|
6
|
+
'tool-gate': { decision: 'allow', reason: '' },
|
|
7
|
+
'tool-result': { action: 'accept', text: '' },
|
|
8
|
+
'append-context': { text: '' },
|
|
9
|
+
'stream-output': { mode: 'pass', text: '' },
|
|
10
|
+
// 种子给一个合法名单:空名单无法表达「剔哪些工具」(createMask 会拒绝)。
|
|
11
|
+
'tool-visibility': { deny: ['web_search'] },
|
|
8
12
|
'request-params': { patch: {} },
|
|
9
13
|
'inbox-prepend': { target: 'next-turn', text: '' },
|
|
10
14
|
'pre-step-filter': { blockPlugins: [] },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { labelOf, requireString } from './shared.mjs'
|
|
2
2
|
import { newMessageId } from '../shared.mjs'
|
|
3
|
+
import { isPluginKind, pluginKind } from '../source-kind.mjs'
|
|
3
4
|
|
|
4
5
|
export function prepareInboxPrepend(action, plugin) {
|
|
5
6
|
const label = labelOf(action)
|
|
@@ -17,17 +18,15 @@ export function prepareInboxPrepend(action, plugin) {
|
|
|
17
18
|
// (对拍实测:声明路径多插 2 条)。
|
|
18
19
|
if (agent?.session === undefined) return
|
|
19
20
|
if (typeof agent?.inbox?.prepend !== 'function') return
|
|
20
|
-
// v4
|
|
21
|
-
|
|
22
|
-
if (typeof message?.source?.kind === 'string' && message.source.kind.startsWith('plugin:')) return
|
|
23
|
-
if (message?.source?.kind === 'plugin') return
|
|
21
|
+
// 插件来源(v4 或旧日志)的判定收在 source-kind;漏掉会让防自触发闸静默失效。
|
|
22
|
+
if (isPluginKind(message?.source?.kind)) return
|
|
24
23
|
if (typeof action.match === 'function' && action.match(agent, message) !== true) return
|
|
25
24
|
if (!take({ agent })) return
|
|
26
25
|
agent.inbox.prepend(target, {
|
|
27
26
|
id: newMessageId(`action-${label}`),
|
|
28
27
|
role: 'user',
|
|
29
28
|
content: [{ type: 'text', text }],
|
|
30
|
-
source: { kind:
|
|
29
|
+
source: { kind: pluginKind(label), plugin: label, form: 'notice', summary: `${label} ${target}` },
|
|
31
30
|
})
|
|
32
31
|
} catch (error) {
|
|
33
32
|
warnOnce(`${plugin}: inbox-prepend action ${label} failed: ${String(error?.message ?? error)}`)
|