dsh-plugin-prompt-tool 2.0.1 → 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 +5 -7
- 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 +23 -15
- 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 +62 -19
- 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 +4 -7
- 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 +4 -4
- package/engine/conditions/count.mjs +43 -13
- 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 +10 -5
- 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 +165 -230
- 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 +302 -28
- 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 +47 -26
- 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 +39 -35
- 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 +37 -762
- package/lib/index.mjs +26919 -5791
- 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 +40 -41
- package/skills/dsh-module/SKILL.md +119 -0
- package/skills/dsh-module/reference.md +185 -0
- 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 +20 -0
- package/templates/64-pre-step-filter.yml +23 -0
- package/templates/65-turn-stop.yml +2 -2
- package/templates/67-inbox-prepend.yml +22 -0
- 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/library/agent-instructions.yml +0 -9
- package/engine/compositions/library/command-goal.yml +0 -7
- package/engine/compositions/library/compaction.yml +0 -22
- package/engine/compositions/library/delegation-ptc.yml +0 -58
- package/engine/compositions/library/delegation.yml +0 -56
- package/engine/compositions/library/persistent-shell.yml +0 -50
- package/engine/compositions/library/planning.yml +0 -26
- package/engine/compositions/library/skill-filesystem-cordis.yml +0 -10
- package/engine/compositions/library/skill-filesystem.yml +0 -7
- package/engine/compositions/library/tool-ask-user.yml +0 -7
- package/engine/compositions/library/tool-bash.yml +0 -8
- package/engine/compositions/library/tool-cordis.yml +0 -7
- package/engine/compositions/library/tool-fs-search.yml +0 -9
- package/engine/compositions/library/tool-fs.yml +0 -7
- package/engine/compositions/library/tool-goal.yml +0 -7
- package/engine/compositions/library/tool-jobs.yml +0 -7
- package/engine/compositions/library/tool-plugin-manager-disabled.yml +0 -8
- package/engine/compositions/library/tool-plugin-manager.yml +0 -8
- package/engine/compositions/library/tool-present.yml +0 -7
- package/engine/compositions/library/tool-presentation.yml +0 -9
- package/engine/compositions/library/tool-pwsh.yml +0 -8
- package/engine/compositions/library/tool-skill.yml +0 -7
- package/engine/compositions/library/tool-todo.yml +0 -9
- package/engine/compositions/library/tool-web.yml +0 -10
- package/engine/compositions/source/local/character-tools.yml +0 -5
- package/engine/compositions/source/local/declared-triggers.yml +0 -12
- 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 -39
- package/lib/module-package-CHPFweqK.mjs +0 -2
- package/lib/module-package-Cnd1_mwx.mjs +0 -2872
- package/lib/paths-CyP0IkaM.d.mts +0 -1390
- package/lib/preset-transfer.d.mts +0 -2
- package/lib/preset-transfer.mjs +0 -3
- package/lib/rules-migration-DJ6vASB5.mjs +0 -15334
- 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,400 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* subagent-tool-policy-core — 子代理实例级工具策略纯模块(单一 seam)。
|
|
3
|
-
*
|
|
4
|
-
* Host bridge、运行时 shadow 工具与测试都只经过本模块的四个接口:
|
|
5
|
-
* validateSubagentToolPolicy — 策略 schema/唯一性/引用/子集校验(返回错误列表);
|
|
6
|
-
* compileSubagentToolPolicy — 校验后编译(正则编译、稳定排序、Set 归一);
|
|
7
|
-
* resolveSubagentToolPolicy — 实例选择与有效工具集解析(selector 优先级、
|
|
8
|
-
* expansion/deny 合并、ceiling 硬上限);
|
|
9
|
-
* buildSubagentToolParameters — 模型可见的扩展参数 Schema 片段。
|
|
10
|
-
*
|
|
11
|
-
* 不变量:
|
|
12
|
-
* - ceiling.deny 永远优先,任何 selector / additional_tools 都不能恢复;
|
|
13
|
-
* - 有效工具集 = (profile.allow ∪ 已验证扩权) ∩ ceiling.allow ∩ availableToolNames
|
|
14
|
-
* − profile.deny − restrict_tools − ceiling.deny;
|
|
15
|
-
* - selector 优先级 tool_profile > character_id > task_type > 自动分类 > default;
|
|
16
|
-
* - 纯模块:不 import dsh-tools、不写文件,可在 .engine 与 host 两侧加载。
|
|
17
|
-
*/
|
|
18
|
-
import { createOrderedTaskClassifier } from './classify-task.mjs'
|
|
19
|
-
|
|
20
|
-
/** 解析字符串列表(数组或逗号/空格分隔字符串),非字符串项报错。 */
|
|
21
|
-
function stringList(value, path, errors, { allowEmpty = true } = {}) {
|
|
22
|
-
if (value === undefined || value === null) return []
|
|
23
|
-
const raw = Array.isArray(value) ? value : typeof value === 'string' ? value.split(/[,\s]+/) : undefined
|
|
24
|
-
if (raw === undefined) {
|
|
25
|
-
errors.push(`${path} must be an array of strings`)
|
|
26
|
-
return []
|
|
27
|
-
}
|
|
28
|
-
const out = []
|
|
29
|
-
for (const item of raw) {
|
|
30
|
-
if (typeof item !== 'string' || item.trim().length === 0) {
|
|
31
|
-
errors.push(`${path} must contain only non-empty strings`)
|
|
32
|
-
continue
|
|
33
|
-
}
|
|
34
|
-
if (!out.includes(item.trim())) out.push(item.trim())
|
|
35
|
-
}
|
|
36
|
-
return out
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** 布尔可选字段。 */
|
|
40
|
-
function booleanOption(value, fallback) {
|
|
41
|
-
return typeof value === 'boolean' ? value : fallback
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** 校验策略;返回错误列表(空 = 合法)。 */
|
|
45
|
-
export function validateSubagentToolPolicy(raw) {
|
|
46
|
-
const errors = []
|
|
47
|
-
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
48
|
-
return ['subagentToolPolicy must be an object']
|
|
49
|
-
}
|
|
50
|
-
const defaultProfile = raw.defaultProfile
|
|
51
|
-
if (typeof defaultProfile !== 'string' || defaultProfile.length === 0) {
|
|
52
|
-
errors.push('subagentToolPolicy.defaultProfile is required')
|
|
53
|
-
}
|
|
54
|
-
const ceiling = raw.ceiling
|
|
55
|
-
if (ceiling === null || typeof ceiling !== 'object' || Array.isArray(ceiling)) {
|
|
56
|
-
errors.push('subagentToolPolicy.ceiling is required')
|
|
57
|
-
}
|
|
58
|
-
const ceilingAllow = ceiling !== null && typeof ceiling === 'object' && !Array.isArray(ceiling)
|
|
59
|
-
? stringList(ceiling.allow, 'ceiling.allow', errors)
|
|
60
|
-
: []
|
|
61
|
-
if (ceilingAllow.length === 0) errors.push('subagentToolPolicy.ceiling.allow must be a non-empty list')
|
|
62
|
-
const ceilingDeny = ceiling !== null && typeof ceiling === 'object' && !Array.isArray(ceiling)
|
|
63
|
-
? stringList(ceiling.deny, 'ceiling.deny', errors)
|
|
64
|
-
: []
|
|
65
|
-
|
|
66
|
-
const profileIds = new Set()
|
|
67
|
-
const profiles = Array.isArray(raw.profiles) ? raw.profiles : []
|
|
68
|
-
if (profiles.length === 0) errors.push('subagentToolPolicy.profiles must declare at least one profile')
|
|
69
|
-
for (const [index, profile] of profiles.entries()) {
|
|
70
|
-
const path = `profiles[${index}]`
|
|
71
|
-
if (profile === null || typeof profile !== 'object' || Array.isArray(profile)) {
|
|
72
|
-
errors.push(`${path} must be an object`)
|
|
73
|
-
continue
|
|
74
|
-
}
|
|
75
|
-
if (typeof profile.id !== 'string' || profile.id.length === 0) {
|
|
76
|
-
errors.push(`${path}.id is required`)
|
|
77
|
-
} else {
|
|
78
|
-
if (profileIds.has(profile.id)) errors.push(`${path}.id ${JSON.stringify(profile.id)} is duplicated`)
|
|
79
|
-
profileIds.add(profile.id)
|
|
80
|
-
}
|
|
81
|
-
if (typeof profile.name !== 'string' || profile.name.length === 0) {
|
|
82
|
-
errors.push(`${path}.name is required`)
|
|
83
|
-
}
|
|
84
|
-
const allow = stringList(profile.allow, `${path}.allow`, errors)
|
|
85
|
-
const deny = stringList(profile.deny, `${path}.deny`, errors)
|
|
86
|
-
for (const tool of allow) {
|
|
87
|
-
if (!ceilingAllow.includes(tool)) errors.push(`${path}.allow tool ${JSON.stringify(tool)} exceeds ceiling.allow`)
|
|
88
|
-
}
|
|
89
|
-
for (const tool of deny) {
|
|
90
|
-
if (!ceilingAllow.includes(tool)) errors.push(`${path}.deny tool ${JSON.stringify(tool)} is not in ceiling.allow`)
|
|
91
|
-
}
|
|
92
|
-
if (typeof profile.modelSelectable !== 'undefined' && typeof profile.modelSelectable !== 'boolean') {
|
|
93
|
-
errors.push(`${path}.modelSelectable must be a boolean`)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const characterIds = new Set()
|
|
98
|
-
const characterBindings = Array.isArray(raw.characterBindings) ? raw.characterBindings : []
|
|
99
|
-
for (const [index, binding] of characterBindings.entries()) {
|
|
100
|
-
const path = `characterBindings[${index}]`
|
|
101
|
-
if (binding === null || typeof binding !== 'object' || Array.isArray(binding)) {
|
|
102
|
-
errors.push(`${path} must be an object`)
|
|
103
|
-
continue
|
|
104
|
-
}
|
|
105
|
-
if (typeof binding.characterId !== 'string' || binding.characterId.length === 0) {
|
|
106
|
-
errors.push(`${path}.characterId is required`)
|
|
107
|
-
} else {
|
|
108
|
-
if (characterIds.has(binding.characterId)) errors.push(`${path}.characterId ${JSON.stringify(binding.characterId)} is duplicated`)
|
|
109
|
-
characterIds.add(binding.characterId)
|
|
110
|
-
}
|
|
111
|
-
if (typeof binding.profile !== 'string' || !profileIds.has(binding.profile)) {
|
|
112
|
-
errors.push(`${path}.profile ${JSON.stringify(binding.profile)} must reference an existing profile`)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const taskIds = new Set()
|
|
117
|
-
const taskRules = Array.isArray(raw.taskRules) ? raw.taskRules : []
|
|
118
|
-
for (const [index, rule] of taskRules.entries()) {
|
|
119
|
-
const path = `taskRules[${index}]`
|
|
120
|
-
if (rule === null || typeof rule !== 'object' || Array.isArray(rule)) {
|
|
121
|
-
errors.push(`${path} must be an object`)
|
|
122
|
-
continue
|
|
123
|
-
}
|
|
124
|
-
if (typeof rule.id !== 'string' || rule.id.length === 0) {
|
|
125
|
-
errors.push(`${path}.id is required`)
|
|
126
|
-
} else {
|
|
127
|
-
if (taskIds.has(rule.id)) errors.push(`${path}.id ${JSON.stringify(rule.id)} is duplicated`)
|
|
128
|
-
taskIds.add(rule.id)
|
|
129
|
-
}
|
|
130
|
-
if (typeof rule.pattern !== 'string' || rule.pattern.length === 0) {
|
|
131
|
-
errors.push(`${path}.pattern is required`)
|
|
132
|
-
} else {
|
|
133
|
-
try {
|
|
134
|
-
// 不用全局 g 标志;编译期校验非法正则,非法拒绝整次保存。
|
|
135
|
-
new RegExp(rule.pattern)
|
|
136
|
-
} catch (error) {
|
|
137
|
-
errors.push(`${path}.pattern is not a valid regular expression: ${String(error.message ?? error)}`)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (typeof rule.order !== 'number' || !Number.isSafeInteger(rule.order)) {
|
|
141
|
-
errors.push(`${path}.order must be an integer`)
|
|
142
|
-
}
|
|
143
|
-
if (typeof rule.profile !== 'string' || !profileIds.has(rule.profile)) {
|
|
144
|
-
errors.push(`${path}.profile ${JSON.stringify(rule.profile)} must reference an existing profile`)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const expansion = raw.modelExpansion
|
|
149
|
-
if (expansion !== undefined && expansion !== null) {
|
|
150
|
-
const expansionPath = 'modelExpansion'
|
|
151
|
-
if (typeof expansion !== 'object' || Array.isArray(expansion)) {
|
|
152
|
-
errors.push(`${expansionPath} must be an object`)
|
|
153
|
-
} else {
|
|
154
|
-
const expansionAllow = stringList(expansion.allow, `${expansionPath}.allow`, errors)
|
|
155
|
-
for (const tool of expansionAllow) {
|
|
156
|
-
if (!ceilingAllow.includes(tool)) errors.push(`${expansionPath}.allow tool ${JSON.stringify(tool)} exceeds ceiling.allow`)
|
|
157
|
-
}
|
|
158
|
-
if (expansion.maxAdditionalTools !== undefined
|
|
159
|
-
&& (!Number.isSafeInteger(expansion.maxAdditionalTools) || expansion.maxAdditionalTools < 0)) {
|
|
160
|
-
errors.push(`${expansionPath}.maxAdditionalTools must be a non-negative safe integer`)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (defaultProfile !== undefined && typeof defaultProfile === 'string' && defaultProfile.length > 0 && !profileIds.has(defaultProfile)) {
|
|
165
|
-
errors.push(`subagentToolPolicy.defaultProfile ${JSON.stringify(defaultProfile)} must reference an existing profile`)
|
|
166
|
-
}
|
|
167
|
-
return errors
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/** 编译策略:校验通过后归一为运行态结构(Set / 编译正则 / 稳定排序 / 分类器)。 */
|
|
171
|
-
export function compileSubagentToolPolicy(raw) {
|
|
172
|
-
const errors = validateSubagentToolPolicy(raw)
|
|
173
|
-
if (errors.length > 0) {
|
|
174
|
-
throw new Error(`invalid subagentToolPolicy: ${errors.join('; ')}`)
|
|
175
|
-
}
|
|
176
|
-
const ceiling = raw.ceiling
|
|
177
|
-
const ceilingAllow = stringList(ceiling.allow, 'ceiling.allow', [])
|
|
178
|
-
const ceilingDeny = stringList(ceiling.deny, 'ceiling.deny', [])
|
|
179
|
-
const profiles = (Array.isArray(raw.profiles) ? raw.profiles : []).map((profile) => ({
|
|
180
|
-
id: profile.id,
|
|
181
|
-
name: profile.name,
|
|
182
|
-
allow: stringList(profile.allow, 'profile.allow', []),
|
|
183
|
-
deny: stringList(profile.deny, 'profile.deny', []),
|
|
184
|
-
modelSelectable: booleanOption(profile.modelSelectable, false),
|
|
185
|
-
}))
|
|
186
|
-
const profileMap = new Map(profiles.map((profile) => [profile.id, profile]))
|
|
187
|
-
const characterBindings = (Array.isArray(raw.characterBindings) ? raw.characterBindings : []).map((binding) => ({
|
|
188
|
-
characterId: binding.characterId,
|
|
189
|
-
profile: binding.profile,
|
|
190
|
-
modelSelectable: booleanOption(binding.modelSelectable, false),
|
|
191
|
-
}))
|
|
192
|
-
const taskRules = (Array.isArray(raw.taskRules) ? raw.taskRules : [])
|
|
193
|
-
.map((rule) => ({
|
|
194
|
-
id: rule.id,
|
|
195
|
-
name: rule.name ?? rule.id,
|
|
196
|
-
pattern: rule.pattern,
|
|
197
|
-
re: new RegExp(rule.pattern),
|
|
198
|
-
profile: rule.profile,
|
|
199
|
-
order: rule.order,
|
|
200
|
-
modelSelectable: booleanOption(rule.modelSelectable, false),
|
|
201
|
-
}))
|
|
202
|
-
.sort((a, b) => (a.order - b.order) || 0)
|
|
203
|
-
const expansionRaw = raw.modelExpansion
|
|
204
|
-
const expansion = {
|
|
205
|
-
enabled: booleanOption(expansionRaw?.enabled, false),
|
|
206
|
-
allow: stringList(expansionRaw?.allow, 'modelExpansion.allow', []),
|
|
207
|
-
maxAdditionalTools: Number.isSafeInteger(expansionRaw?.maxAdditionalTools) ? expansionRaw.maxAdditionalTools : 0,
|
|
208
|
-
requireApproval: booleanOption(expansionRaw?.requireApproval, true),
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
defaultProfileId: raw.defaultProfile,
|
|
212
|
-
ceiling: { allow: ceilingAllow, deny: ceilingDeny },
|
|
213
|
-
profiles,
|
|
214
|
-
profileMap,
|
|
215
|
-
characterBindings,
|
|
216
|
-
taskRules,
|
|
217
|
-
expansion,
|
|
218
|
-
modelSelectableProfiles: profiles.filter((profile) => profile.modelSelectable).map((profile) => profile.id),
|
|
219
|
-
modelSelectableCharacterIds: characterBindings.filter((binding) => binding.modelSelectable).map((binding) => binding.characterId),
|
|
220
|
-
modelSelectableTaskTypes: taskRules.filter((rule) => rule.modelSelectable).map((rule) => rule.id),
|
|
221
|
-
classify: createOrderedTaskClassifier(taskRules),
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* 解析一次实例的工具集。request 字段:
|
|
227
|
-
* tool_profile / character_id / task_type / additional_tools / restrict_tools /
|
|
228
|
-
* description / prompt(自动分类输入 = description + "\n" + prompt)。
|
|
229
|
-
* availableTools:当前 assembly scope 可解析的继承工具名集合;公开位置参数保持不变。
|
|
230
|
-
*/
|
|
231
|
-
export function resolveSubagentToolPolicy(compiled, request, availableTools) {
|
|
232
|
-
const requestRecord = request ?? {}
|
|
233
|
-
const availableToolNames = new Set(Array.isArray(availableTools) ? availableTools : [])
|
|
234
|
-
const adopted = []
|
|
235
|
-
const ignored = []
|
|
236
|
-
|
|
237
|
-
const explicitProfile = typeof requestRecord.tool_profile === 'string' && requestRecord.tool_profile.length > 0
|
|
238
|
-
? requestRecord.tool_profile
|
|
239
|
-
: undefined
|
|
240
|
-
const explicitCharacter = typeof requestRecord.character_id === 'string' && requestRecord.character_id.length > 0
|
|
241
|
-
? requestRecord.character_id
|
|
242
|
-
: undefined
|
|
243
|
-
const explicitTask = typeof requestRecord.task_type === 'string' && requestRecord.task_type.length > 0
|
|
244
|
-
? requestRecord.task_type
|
|
245
|
-
: undefined
|
|
246
|
-
|
|
247
|
-
// 选择基础 profile:tool_profile > character_id 绑定 > task_type > 自动分类 > default。
|
|
248
|
-
let selectedProfileId = compiled.defaultProfileId
|
|
249
|
-
let selectedCharacterId
|
|
250
|
-
let selectedTaskType
|
|
251
|
-
let selected = false
|
|
252
|
-
if (explicitProfile !== undefined) {
|
|
253
|
-
if (compiled.modelSelectableProfiles.includes(explicitProfile)) {
|
|
254
|
-
selectedProfileId = explicitProfile
|
|
255
|
-
adopted.push('tool_profile')
|
|
256
|
-
selected = true
|
|
257
|
-
} else {
|
|
258
|
-
throw new Error(`tool_profile ${JSON.stringify(explicitProfile)} is not model-selectable`)
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (selected && explicitCharacter !== undefined) ignored.push('character_id')
|
|
262
|
-
if (selected && explicitTask !== undefined) ignored.push('task_type')
|
|
263
|
-
if (!selected && explicitCharacter !== undefined) {
|
|
264
|
-
const binding = compiled.characterBindings.find((item) => item.characterId === explicitCharacter && item.modelSelectable)
|
|
265
|
-
if (binding !== undefined) {
|
|
266
|
-
selectedProfileId = binding.profile
|
|
267
|
-
selectedCharacterId = explicitCharacter
|
|
268
|
-
adopted.push('character_id')
|
|
269
|
-
selected = true
|
|
270
|
-
if (explicitTask !== undefined) ignored.push('task_type')
|
|
271
|
-
} else {
|
|
272
|
-
throw new Error(`character_id ${JSON.stringify(explicitCharacter)} is not model-selectable`)
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (!selected && explicitTask !== undefined) {
|
|
276
|
-
const rule = compiled.taskRules.find((item) => item.id === explicitTask && item.modelSelectable)
|
|
277
|
-
if (rule !== undefined) {
|
|
278
|
-
selectedProfileId = rule.profile
|
|
279
|
-
selectedTaskType = explicitTask
|
|
280
|
-
adopted.push('task_type')
|
|
281
|
-
selected = true
|
|
282
|
-
} else {
|
|
283
|
-
throw new Error(`task_type ${JSON.stringify(explicitTask)} is not model-selectable`)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (!selected) {
|
|
287
|
-
// 未显式选择时按 description + prompt 自动分类。
|
|
288
|
-
const autoText = [requestRecord.description, requestRecord.prompt].filter((item) => typeof item === 'string').join('\n')
|
|
289
|
-
const autoRuleId = compiled.classify(autoText)
|
|
290
|
-
const autoRule = autoRuleId !== undefined ? compiled.taskRules.find((rule) => rule.id === autoRuleId) : undefined
|
|
291
|
-
if (autoRule !== undefined) {
|
|
292
|
-
selectedProfileId = autoRule.profile
|
|
293
|
-
selectedTaskType = autoRuleId
|
|
294
|
-
adopted.push('auto-classify')
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
const profile = compiled.profileMap.get(selectedProfileId) ?? compiled.profileMap.get(compiled.defaultProfileId)
|
|
298
|
-
const baseAllow = profile?.allow ?? []
|
|
299
|
-
|
|
300
|
-
// 扩权校验:requestedAdd ∩ modelExpansion.allow ∩ ceiling.allow ∩ availableToolNames。
|
|
301
|
-
if (requestRecord.additional_tools !== undefined && !Array.isArray(requestRecord.additional_tools)) {
|
|
302
|
-
throw new Error('additional_tools must be an array of tool names')
|
|
303
|
-
}
|
|
304
|
-
const requestedAdd = Array.isArray(requestRecord.additional_tools) ? requestRecord.additional_tools : []
|
|
305
|
-
if (requestedAdd.some((tool) => typeof tool !== 'string' || tool.length === 0)) {
|
|
306
|
-
throw new Error('additional_tools must contain only non-empty tool names')
|
|
307
|
-
}
|
|
308
|
-
const uniqueRequestedAdd = [...new Set(requestedAdd)]
|
|
309
|
-
if (requestedAdd.length !== uniqueRequestedAdd.length) {
|
|
310
|
-
throw new Error('additional_tools must not contain duplicates')
|
|
311
|
-
}
|
|
312
|
-
if (uniqueRequestedAdd.length > 0 && (!compiled.expansion.enabled || compiled.expansion.maxAdditionalTools === 0)) {
|
|
313
|
-
throw new Error('additional_tools is disabled by subagentToolPolicy')
|
|
314
|
-
}
|
|
315
|
-
if (uniqueRequestedAdd.length > compiled.expansion.maxAdditionalTools) {
|
|
316
|
-
throw new Error(`additional_tools exceeds maxAdditionalTools (${compiled.expansion.maxAdditionalTools})`)
|
|
317
|
-
}
|
|
318
|
-
const validatedAdd = []
|
|
319
|
-
if (uniqueRequestedAdd.length > 0) {
|
|
320
|
-
for (const tool of uniqueRequestedAdd) {
|
|
321
|
-
if (!compiled.expansion.allow.includes(tool)) throw new Error(`additional tool ${JSON.stringify(tool)} is not expansion-authorized`)
|
|
322
|
-
if (!compiled.ceiling.allow.includes(tool)) throw new Error(`additional tool ${JSON.stringify(tool)} exceeds ceiling.allow`)
|
|
323
|
-
if (!availableToolNames.has(tool)) throw new Error(`additional tool ${JSON.stringify(tool)} is not available in this assembly`)
|
|
324
|
-
validatedAdd.push(tool)
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
if (requestRecord.restrict_tools !== undefined && !Array.isArray(requestRecord.restrict_tools)) {
|
|
328
|
-
throw new Error('restrict_tools must be an array of tool names')
|
|
329
|
-
}
|
|
330
|
-
const requestedDeny = Array.isArray(requestRecord.restrict_tools) ? requestRecord.restrict_tools : []
|
|
331
|
-
if (requestedDeny.some((tool) => typeof tool !== 'string' || tool.length === 0)) {
|
|
332
|
-
throw new Error('restrict_tools must contain only non-empty tool names')
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// 有效工具集 = (baseAllow ∪ validatedAdd) ∩ ceiling.allow ∩ availableToolNames − deny 三件套。
|
|
336
|
-
const effective = new Set([...baseAllow, ...validatedAdd])
|
|
337
|
-
const ceilingAllowSet = new Set(compiled.ceiling.allow)
|
|
338
|
-
const denySet = new Set([...(profile?.deny ?? []), ...requestedDeny, ...compiled.ceiling.deny])
|
|
339
|
-
for (const tool of [...effective]) {
|
|
340
|
-
if (!ceilingAllowSet.has(tool)) effective.delete(tool)
|
|
341
|
-
if (!availableToolNames.has(tool)) effective.delete(tool)
|
|
342
|
-
if (denySet.has(tool)) effective.delete(tool)
|
|
343
|
-
}
|
|
344
|
-
return {
|
|
345
|
-
adopted,
|
|
346
|
-
ignored,
|
|
347
|
-
profileId: selectedProfileId,
|
|
348
|
-
characterId: selectedCharacterId,
|
|
349
|
-
taskType: selectedTaskType,
|
|
350
|
-
additionalTools: validatedAdd,
|
|
351
|
-
effectiveTools: [...effective].sort(),
|
|
352
|
-
requiresApproval: validatedAdd.length > 0 && compiled.expansion.requireApproval,
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* 模型可见的扩展参数 Schema 片段(JSON Schema property 映射;未开启能力不出现)。
|
|
358
|
-
* 运行时 shadow 工具把它与官方 subagent 参数合并成完整 parameters。
|
|
359
|
-
*/
|
|
360
|
-
export function buildSubagentToolParameters(compiled) {
|
|
361
|
-
const properties = {}
|
|
362
|
-
if (compiled.modelSelectableProfiles.length > 0) {
|
|
363
|
-
properties.tool_profile = {
|
|
364
|
-
type: 'string',
|
|
365
|
-
enum: compiled.modelSelectableProfiles,
|
|
366
|
-
description: '子代理工具档(模型可选择的 profile id)',
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
if (compiled.modelSelectableCharacterIds.length > 0) {
|
|
370
|
-
properties.character_id = {
|
|
371
|
-
type: 'string',
|
|
372
|
-
enum: compiled.modelSelectableCharacterIds,
|
|
373
|
-
description: '角色卡绑定(模型可选择的 character id)',
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
if (compiled.modelSelectableTaskTypes.length > 0) {
|
|
377
|
-
properties.task_type = {
|
|
378
|
-
type: 'string',
|
|
379
|
-
enum: compiled.modelSelectableTaskTypes,
|
|
380
|
-
description: '任务类型(模型可选择的 task rule id)',
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
if (compiled.expansion.enabled && compiled.expansion.maxAdditionalTools > 0) {
|
|
384
|
-
const expansionEnum = compiled.expansion.allow.filter((tool) => compiled.ceiling.allow.includes(tool))
|
|
385
|
-
if (expansionEnum.length > 0) {
|
|
386
|
-
properties.additional_tools = {
|
|
387
|
-
type: 'array',
|
|
388
|
-
items: { type: 'string', enum: expansionEnum },
|
|
389
|
-
maxItems: compiled.expansion.maxAdditionalTools,
|
|
390
|
-
description: '模型请求的附加工具(受用户配置上限约束)',
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
properties.restrict_tools = {
|
|
395
|
-
type: 'array',
|
|
396
|
-
items: { type: 'string' },
|
|
397
|
-
description: '模型可进一步收紧本实例的工具名列表',
|
|
398
|
-
}
|
|
399
|
-
return properties
|
|
400
|
-
}
|