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.
Files changed (204) hide show
  1. package/README.md +100 -233
  2. package/engine/actions/append-context.mjs +3 -24
  3. package/engine/actions/assembly.mjs +70 -94
  4. package/engine/actions/catalog.mjs +65 -28
  5. package/engine/actions/content.mjs +8 -152
  6. package/engine/actions/examples.mjs +8 -4
  7. package/engine/actions/inbox-prepend.mjs +4 -5
  8. package/engine/actions/index.mjs +17 -9
  9. package/engine/actions/inject-text.mjs +24 -4
  10. package/engine/actions/pre-step-filter.mjs +3 -4
  11. package/engine/actions/registration.mjs +20 -28
  12. package/engine/actions/request-params.mjs +21 -1
  13. package/engine/actions/shared.mjs +22 -13
  14. package/engine/actions/stream-output.mjs +26 -0
  15. package/engine/actions/tool-gate.mjs +31 -0
  16. package/engine/actions/tool-result.mjs +36 -0
  17. package/engine/actions/tool-visibility.mjs +248 -0
  18. package/engine/agent-capability.mjs +67 -0
  19. package/engine/anchor-match.mjs +14 -21
  20. package/engine/audience.d.mts +2 -0
  21. package/engine/audience.mjs +102 -0
  22. package/engine/branch.d.mts +4 -0
  23. package/engine/branch.mjs +61 -18
  24. package/engine/compaction-epoch.d.mts +25 -0
  25. package/engine/compaction-epoch.mjs +27 -4
  26. package/engine/compositions/source/local/filesystem-editor.yml +3 -3
  27. package/engine/compositions/source/local/instruction-hint.yml +2 -4
  28. package/engine/compositions/source/local/module-file-tools.yml +2 -0
  29. package/engine/compositions/source/local/module-import-tools.yml +4 -0
  30. package/engine/compositions/source/local/module-manifest-tools.yml +2 -0
  31. package/engine/compositions/source/local/module-rule-tools.yml +5 -0
  32. package/engine/compositions/source/local/module-variable-tools.yml +5 -0
  33. package/engine/compositions/source/local/present-as.yml +26 -0
  34. package/engine/compositions/source/local/tool-config-engine.yml +2 -3
  35. package/engine/compositions/source/local/tool-search.yml +11 -0
  36. package/engine/condition-facts.mjs +84 -0
  37. package/engine/conditions/anchor.mjs +63 -23
  38. package/engine/conditions/availability.mjs +1 -1
  39. package/engine/conditions/composite.mjs +3 -3
  40. package/engine/conditions/count.mjs +42 -11
  41. package/engine/conditions/evaluation.mjs +74 -19
  42. package/engine/conditions/examples.mjs +2 -1
  43. package/engine/conditions/index.d.mts +15 -0
  44. package/engine/conditions/index.mjs +23 -9
  45. package/engine/conditions/operators.mjs +4 -0
  46. package/engine/conditions/phase.mjs +9 -3
  47. package/engine/conditions/preset.mjs +1 -1
  48. package/engine/conditions/session.mjs +8 -2
  49. package/engine/conditions/subject.mjs +144 -65
  50. package/engine/conditions/text.mjs +26 -8
  51. package/engine/conditions/variable.mjs +83 -0
  52. package/engine/context-activation.d.mts +11 -0
  53. package/engine/{st-world-book.mjs → context-activation.mjs} +104 -64
  54. package/engine/delivery.d.mts +2 -0
  55. package/engine/delivery.mjs +93 -0
  56. package/engine/discovery.d.mts +38 -0
  57. package/engine/discovery.mjs +146 -0
  58. package/engine/env-safety.mjs +14 -0
  59. package/engine/executor.d.mts +28 -0
  60. package/engine/executor.mjs +290 -205
  61. package/engine/fillers.mjs +39 -8
  62. package/engine/fixed-registration.mjs +12 -0
  63. package/engine/history.mjs +71 -0
  64. package/engine/instruction-blocks.d.mts +13 -0
  65. package/engine/instruction-blocks.mjs +21 -0
  66. package/engine/instruction-files.d.mts +5 -0
  67. package/engine/instruction-files.mjs +97 -0
  68. package/engine/instruction-hint.mjs +69 -103
  69. package/engine/interpolate.d.mts +7 -0
  70. package/engine/interpolate.mjs +44 -13
  71. package/engine/layers.mjs +163 -227
  72. package/engine/machine-paths.mjs +40 -0
  73. package/engine/module-file-tools.mjs +8 -0
  74. package/engine/module-import-tools.mjs +10 -0
  75. package/engine/module-manifest-tools.mjs +8 -0
  76. package/engine/module-rule-tools.mjs +10 -0
  77. package/engine/module-variable-tools.mjs +10 -0
  78. package/engine/order.d.mts +14 -0
  79. package/engine/order.mjs +0 -3
  80. package/engine/path-safety.d.mts +2 -0
  81. package/engine/path-safety.mjs +58 -0
  82. package/engine/pre-step-source.d.mts +69 -0
  83. package/engine/pre-step-source.mjs +103 -0
  84. package/engine/present-as.mjs +77 -0
  85. package/engine/promotion-pool.d.mts +9 -0
  86. package/engine/promotion-pool.mjs +69 -0
  87. package/engine/record.mjs +2 -0
  88. package/engine/rule-engine.mjs +3 -2
  89. package/engine/rule-facts.d.mts +11 -0
  90. package/engine/rule-facts.mjs +60 -0
  91. package/engine/rule-runtime.d.mts +46 -0
  92. package/engine/rule-runtime.mjs +98 -13
  93. package/engine/rule-spec.d.mts +106 -0
  94. package/engine/rule-spec.mjs +301 -27
  95. package/engine/run-code-env.mjs +73 -19
  96. package/engine/schema.mjs +165 -243
  97. package/engine/sdk-strip.mjs +2 -2
  98. package/engine/service-names.d.mts +10 -0
  99. package/engine/service-names.mjs +10 -0
  100. package/engine/shared.d.mts +21 -0
  101. package/engine/shared.mjs +46 -13
  102. package/engine/skill-search.mjs +125 -53
  103. package/engine/source-kind.d.mts +9 -0
  104. package/engine/source-kind.mjs +16 -0
  105. package/engine/strategy-ids.mjs +5 -0
  106. package/engine/template-evaluation.d.mts +16 -0
  107. package/engine/template-evaluation.mjs +178 -0
  108. package/engine/template-frame.d.mts +16 -0
  109. package/engine/template-frame.mjs +69 -0
  110. package/engine/tool-config-engine.mjs +38 -34
  111. package/engine/tool-definition.d.mts +4 -0
  112. package/engine/tool-definition.mjs +5 -1
  113. package/engine/tool-git-bash.mjs +5 -1
  114. package/engine/tool-scope.mjs +22 -0
  115. package/engine/{dev-tool-search.mjs → tool-search.mjs} +63 -65
  116. package/engine/trigger.mjs +15 -270
  117. package/engine/vendor/yaml/dist/compose/resolve-flow-scalar.js +19 -18
  118. package/engine/vendor/yaml/dist/nodes/Alias.js +25 -23
  119. package/engine/vendor/yaml/package.json +1 -1
  120. package/lib/client.js +8312 -6724
  121. package/lib/client.js.map +1 -1
  122. package/lib/index.d.mts +16 -2120
  123. package/lib/index.mjs +26914 -5816
  124. package/modules/ponytail/module.yml +83 -203
  125. package/modules/ptc-mode/module.yml +45 -0
  126. package/modules/soul/assets/core-hard.md +6 -0
  127. package/modules/soul/assets/criteria.md +12 -0
  128. package/modules/soul/assets/echo.md +12 -0
  129. package/modules/soul/assets/layers.md +11 -0
  130. package/modules/soul/assets/ledger.md +9 -0
  131. package/modules/soul/assets/manifest.yml +20 -0
  132. package/modules/soul/assets/reference/00-layers.md +27 -0
  133. package/modules/soul/assets/reference/01-criteria.md +32 -0
  134. package/modules/soul/assets/reference/02-echo.md +158 -0
  135. package/modules/soul/assets/reference/03-vital.md +183 -0
  136. package/modules/soul/assets/reference/04-manifest.json +106 -0
  137. package/modules/soul/assets/reference/05-doctor.md +67 -0
  138. package/modules/soul/assets/reference/06-reference.md +189 -0
  139. package/modules/soul/assets/reference/07-bundle.json +431 -0
  140. package/modules/soul/assets/reference/08-/350/270/251/345/235/221/345/256/236/345/275/225.md +59 -0
  141. package/modules/soul/assets/reference/09-dev-workflow.md +109 -0
  142. package/modules/soul/assets/reference/10-echo-anatomy.md +100 -0
  143. package/modules/soul/assets/reference/11-self.md +80 -0
  144. package/modules/soul/assets/reference/12-ledger.md +159 -0
  145. package/modules/soul/assets/reference/README.md +10 -0
  146. package/modules/soul/assets/templates/agents-template.md +6 -0
  147. package/modules/soul/assets/vital.md +9 -0
  148. package/modules/soul/module.yml +311 -0
  149. package/modules/superpowers/module.yml +122 -0
  150. package/modules/tool-surface/module.yml +51 -47
  151. package/package.json +37 -40
  152. package/skills/dsh-module/SKILL.md +67 -147
  153. package/skills/dsh-module/reference.md +113 -86
  154. package/templates/10-pre-step.yml +2 -3
  155. package/templates/14-first-turn-anchor.yml +10 -14
  156. package/templates/15-guide-auto.yml +32 -28
  157. package/templates/16-custom-fallback.yml +33 -21
  158. package/templates/20-system-section.yml +1 -4
  159. package/templates/50-llm-stream.yml +4 -10
  160. package/templates/60-tool-pipeline.yml +12 -10
  161. package/templates/63-append-context.yml +4 -6
  162. package/templates/65-turn-stop.yml +2 -2
  163. package/templates/67-inbox-prepend.yml +2 -2
  164. package/templates/70-subagent-maintenance.yml +9 -10
  165. package/templates/75-subagent-end.yml +2 -2
  166. package/templates/80-tool-surface.yml +41 -57
  167. package/templates/tools/40-delegate-module-import.yml +22 -0
  168. package/templates/tools/50-delegate-module-rules.yml +14 -0
  169. package/templates/tools/60-delegate-module-variables.yml +17 -0
  170. package/templates/tools/70-delegate-module-rules-edit.yml +31 -0
  171. package/engine/actions/decision.mjs +0 -54
  172. package/engine/actions/guard.mjs +0 -80
  173. package/engine/actions/sdk-strip.mjs +0 -30
  174. package/engine/character-tools.mjs +0 -8
  175. package/engine/classify-task.mjs +0 -53
  176. package/engine/compositions/source/local/character-tools.yml +0 -5
  177. package/engine/compositions/source/local/dev-tool-search.yml +0 -11
  178. package/engine/compositions/source/local/session-var-tools.yml +0 -5
  179. package/engine/compositions/source/local/subagent-tool-policy.yml +0 -21
  180. package/engine/compositions/source/local/world-book-tools.yml +0 -5
  181. package/engine/condition.mjs +0 -96
  182. package/engine/declared-triggers.mjs +0 -93
  183. package/engine/session-var-tools.mjs +0 -8
  184. package/engine/session-vars.mjs +0 -54
  185. package/engine/st-macros.d.mts +0 -22
  186. package/engine/st-macros.mjs +0 -208
  187. package/engine/st-render.mjs +0 -109
  188. package/engine/st-world-book.d.mts +0 -10
  189. package/engine/subagent-tool-policy-core.mjs +0 -400
  190. package/engine/subagent-tool-policy.mjs +0 -352
  191. package/engine/trigger-spec.mjs +0 -173
  192. package/engine/world-book-tools.mjs +0 -8
  193. package/lib/migrate-rules.d.mts +0 -1
  194. package/lib/migrate-rules.mjs +0 -610
  195. package/lib/module-package-DqJZaE71.mjs +0 -2
  196. package/lib/module-package-q2gDRu9H.mjs +0 -2869
  197. package/lib/rule-builder-DMrJten6.mjs +0 -14767
  198. package/templates/61-deny-tools-presentation.yml +0 -27
  199. package/templates/62-guard-deny-tools.yml +0 -20
  200. package/templates/policies/legacy-defaults.yml +0 -34
  201. package/templates/tools/40-delegate-character.yml +0 -15
  202. package/templates/tools/50-delegate-worldbook.yml +0 -15
  203. package/templates/tools/60-delegate-session-var.yml +0 -15
  204. package/templates/tools/70-delegate-worldbook-upsert.yml +0 -36
@@ -1,352 +0,0 @@
1
- /**
2
- * subagent-tool-policy — agent-scoped subagent/subagent_fork shadow 工具。
3
- *
4
- * 本模块通过运行中 DSH 入口解析同一份 dsh-tools / dsh-scope,避免生成目录
5
- * 加载第二份 registry 类型。每个 shadow 只安装到当前 assembly generation 的
6
- * descendant Agent;实例权限在 SubagentStartRequest 创建窗口冻结。
7
- *
8
- * ── 能力提供者边界(T4)────────────────────────────────────────────────────
9
- * 本模块是**能力提供者**,不是触发器。分类判据(与组合源 subagent-tool-policy.yml、
10
- * tool-config-engine.yml 及 test/engine/provider-boundary.test.mjs 同一份措辞):
11
- * - 会给模型提供可调用能力(注册工具 / 域 / 服务)的 → 能力提供者;
12
- * - 干预流程(改提示词、改装配、裁决、追加)的 → 声明式触发器。
13
- * 因此本模块**不接入** engine/trigger.mjs:它不订阅装配 waterfall,也没有声明式
14
- * 触发器(`if`/`then` 声明),只在 agent 创建时向该 agent 的 scope 注册两个可调用工具。影子工具解决的是
15
- * 「子代理能用什么工具」,属于能力面,不是流程干预面。
16
- *
17
- * 样板收敛:
18
- * - 配置声明走 fields.mjs 的 `defineConfig`(未知键在挂载期 fail loud);
19
- * - 注册走 disposer 契约,但这里保留 `agent.ctx.effect(...)` 而不是 shared.keepDisposer,
20
- * 依据见 install() 内的注释(整组原子回滚 + 按 agent 定向撤销句柄);
21
- * - 降级告警统一 `${name}: <what>; <fallback>` 一个前缀格式。
22
- *
23
- * 安全边界(T4 明令不得触碰,改动本模块时同样不得放宽):
24
- * - 扩权审批门 expansionApproval:requested additional_tools 必须先拿到
25
- * `allowed-once`,无 approval 通道时 fail loud(不静默放行);
26
- * - fail loud 语义:显式内联或策略文件内容非法一律抛错;只有路径入口的 ENOENT
27
- * 才降级为官方委派行为。
28
- *
29
- * 登记入口:`engineProvider`(数据导出),供边界守卫消费。提供者登记与声明式触发器
30
- * 声明(约定为 `engineTriggers`)**互斥**,同一模块不得同时导出两者。
31
- */
32
- import { readFileSync } from 'node:fs'
33
- import { fileURLToPath } from 'node:url'
34
- import { parse as parseYaml } from './vendor/yaml/index.js'
35
- import { importHostPackage } from './host-package.mjs'
36
- import {
37
- buildSubagentToolParameters,
38
- compileSubagentToolPolicy,
39
- resolveSubagentToolPolicy,
40
- } from './subagent-tool-policy-core.mjs'
41
- import { defineConfig, passthrough } from './fields.mjs'
42
-
43
- export const name = 'subagent-tool-policy'
44
- export const inject = ['agents', 'subagents', 'tools']
45
-
46
- const [{ ToolArgsError }, { scopeChainOf, scopeOf }] = await Promise.all([
47
- importHostPackage('@deepseek-ai/dsh-tools'),
48
- importHostPackage('@deepseek-ai/dsh-scope'),
49
- ])
50
-
51
- const BASE_PARAMETERS = {
52
- description: { type: 'string', description: '被委派任务的简短说明(展示用)' },
53
- prompt: { type: 'string', description: '子代理用户消息内容' },
54
- run_in_background: { type: 'boolean', description: '后台可续跑,立即返回 durable 子代理 id' },
55
- }
56
- const SPAWN_PARAMETERS = {
57
- provider: { type: 'string', description: '子代理 LLM provider;与 model 成对提供' },
58
- model: { type: 'string', description: '子代理模型 id;与 provider 成对提供' },
59
- reasoning_effort: { type: 'string', description: '子代理推理强度' },
60
- }
61
-
62
- /** 两个 shadow 的模型可见工具名:注册点与 engineProvider 登记共用这一份,防清单漂移。 */
63
- const SHADOW_TOOL_NAMES = { spawn: 'subagent', fork: 'subagent_fork' }
64
-
65
- /**
66
- * 配置契约:白名单由字段声明派生(此前没有白名单,未知键被静默忽略)。
67
- * 五个旧键保留 passthrough:它们对非字符串/非对象值都是静默取默认或忽略
68
- *(policyFile / spawnProvider / forkProvider 取默认名,agentOptions 非对象则 undefined,
69
- * maxDepth 原样透传、由下游按 `'provider-managed'` 或数字自行判定),
70
- * 内联 policy 单独完整校验;缺字段才启用路径模式,显式非法值不得降级。
71
- */
72
- const configFields = defineConfig({
73
- policyFile: passthrough((value) => (typeof value === 'string' && value.length > 0 ? value : '../subagent-tools/policy.yml')),
74
- policy: passthrough(value => value),
75
- spawnProvider: passthrough((value) => (typeof value === 'string' && value.length > 0 ? value : 'spawn')),
76
- forkProvider: passthrough((value) => (typeof value === 'string' && value.length > 0 ? value : 'fork')),
77
- maxDepth: passthrough((value) => value),
78
- agentOptions: passthrough((value) => value),
79
- })
80
- export const configContract = {
81
- ...configFields,
82
- parse(config, plugin) {
83
- const source = configFields.parse(config, plugin)
84
- if (Object.hasOwn(config ?? {}, 'policy')) compileSubagentToolPolicy(source.policy)
85
- else delete source.policy
86
- return source
87
- },
88
- }
89
-
90
- /**
91
- * 能力提供者登记(T4 边界守卫的数据源)。
92
- * `provides.kind === 'fixed'`:工具名在装配期就是确定的(两种委派入口各一个 shadow),
93
- * 守卫据此断言「提供者登记的工具名 = 实际注册的工具名」,并断言它们不出现在触发器声明里。
94
- */
95
- export const engineProvider = {
96
- kind: 'provider',
97
- moduleId: name,
98
- registers: 'tools',
99
- provides: { kind: 'fixed', tools: Object.values(SHADOW_TOOL_NAMES) },
100
- }
101
-
102
- function resolvePolicyFile(config) {
103
- const raw = typeof config?.policyFile === 'string' && config.policyFile.length > 0
104
- ? config.policyFile
105
- : '../subagent-tools/policy.yml'
106
- return fileURLToPath(new URL(raw, import.meta.url))
107
- }
108
-
109
- function loadCompiledPolicy(config) {
110
- if (Object.hasOwn(config, 'policy')) return compileSubagentToolPolicy(config.policy)
111
- const raw = readFileSync(resolvePolicyFile(config), 'utf8')
112
- const parsed = parseYaml(raw, { logLevel: 'silent' })
113
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
114
- throw new TypeError('policy.yml must be a YAML map')
115
- }
116
- return compileSubagentToolPolicy(parsed)
117
- }
118
-
119
- function providerFor(kind, config) {
120
- if (kind === 'fork') return typeof config?.forkProvider === 'string' && config.forkProvider.length > 0 ? config.forkProvider : 'fork'
121
- return typeof config?.spawnProvider === 'string' && config.spawnProvider.length > 0 ? config.spawnProvider : 'spawn'
122
- }
123
-
124
- function assertString(value, field, required = false) {
125
- if (value === undefined && !required) return
126
- if (typeof value !== 'string' || value.length === 0) throw new ToolArgsError([`${field}: expected non-empty string`])
127
- }
128
-
129
- function validateShadowArgs(args, compiled, kind) {
130
- if (args === null || typeof args !== 'object' || Array.isArray(args)) throw new ToolArgsError(['arguments: expected object'])
131
- assertString(args.description, 'description', true)
132
- assertString(args.prompt, 'prompt', true)
133
- if (args.run_in_background !== undefined && typeof args.run_in_background !== 'boolean') {
134
- throw new ToolArgsError(['run_in_background: expected boolean'])
135
- }
136
- for (const field of ['tool_profile', 'character_id', 'task_type']) assertString(args[field], field)
137
- if (kind === 'spawn') {
138
- for (const field of ['provider', 'model', 'reasoning_effort']) assertString(args[field], field)
139
- if ((args.provider === undefined) !== (args.model === undefined)) {
140
- throw new ToolArgsError(['provider and model must be supplied together'])
141
- }
142
- }
143
- const properties = buildSubagentToolParameters(compiled)
144
- for (const field of ['tool_profile', 'character_id', 'task_type']) {
145
- if (args[field] !== undefined && !properties[field]?.enum?.includes(args[field])) {
146
- throw new ToolArgsError([`${field}: value is not model-selectable`])
147
- }
148
- }
149
- for (const field of ['additional_tools', 'restrict_tools']) {
150
- if (args[field] !== undefined && (!Array.isArray(args[field]) || args[field].some((item) => typeof item !== 'string' || item.length === 0))) {
151
- throw new ToolArgsError([`${field}: expected an array of non-empty strings`])
152
- }
153
- }
154
- if (args.additional_tools !== undefined) {
155
- if (properties.additional_tools === undefined) throw new ToolArgsError(['additional_tools: expansion is disabled'])
156
- if (args.additional_tools.length > compiled.expansion.maxAdditionalTools) {
157
- throw new ToolArgsError([`additional_tools: exceeds maxAdditionalTools (${compiled.expansion.maxAdditionalTools})`])
158
- }
159
- const invalid = args.additional_tools.filter((tool) => !properties.additional_tools.items.enum.includes(tool))
160
- if (invalid.length > 0) throw new ToolArgsError([`additional_tools: unauthorized tools ${invalid.join(', ')}`])
161
- }
162
- }
163
-
164
- async function expansionApproval(ctx, run, toolName, resolved) {
165
- const approval = ctx.get('approval')
166
- if (approval === undefined || typeof approval.request !== 'function') {
167
- throw new Error(`tool ${toolName} requests additional_tools, but no approval channel is available`)
168
- }
169
- const outcome = await approval.request({
170
- agent: run.agent,
171
- toolName,
172
- callId: run.callId,
173
- reason: `parent=${run.agent.id}; profile=${resolved.profileId}; character=${resolved.characterId ?? '-'}; task=${resolved.taskType ?? '-'}; additional=[${resolved.additionalTools.join(', ')}]; effective=[${resolved.effectiveTools.join(', ')}]`,
174
- signal: run.signal,
175
- })
176
- if (outcome !== 'allowed-once') throw new Error(`approval for ${toolName} additional_tools was ${outcome}`)
177
- }
178
-
179
- function configuredAgentOptions(config) {
180
- const raw = config?.agentOptions
181
- return raw !== null && typeof raw === 'object' && !Array.isArray(raw) ? { ...raw } : undefined
182
- }
183
-
184
- function requestedAgentOptions(config, request) {
185
- const baseline = configuredAgentOptions(config)
186
- if (request.provider === undefined && request.model === undefined && request.reasoning_effort === undefined) return baseline
187
- return {
188
- ...(baseline ?? {}),
189
- ...(request.provider !== undefined ? { provider: request.provider, model: request.model } : {}),
190
- ...(request.reasoning_effort !== undefined ? { reasoningEffort: request.reasoning_effort } : {}),
191
- }
192
- }
193
-
194
- async function preflightRoute(ctx, parent, options, signal) {
195
- if (options === undefined) return
196
- const provider = options.provider ?? parent.options?.provider
197
- const model = options.model ?? parent.options?.model
198
- if (provider === undefined || model === undefined) throw new Error('cannot select child LLM values without an effective provider and model')
199
- const llm = ctx.get('llm')
200
- if (llm === undefined || typeof llm.resolveCallConfig !== 'function') {
201
- throw new Error('cannot resolve the selected child LLM route because the llm service is unavailable')
202
- }
203
- await llm.resolveCallConfig({
204
- provider,
205
- model,
206
- ...(options.reasoningEffort === undefined ? {} : { reasoningEffort: options.reasoningEffort }),
207
- ...(options.maxTokens === undefined ? {} : { maxTokens: options.maxTokens }),
208
- }, signal)
209
- }
210
-
211
- async function settleForeground(run) {
212
- const [execution] = await Promise.allSettled([run.result])
213
- const [disposal] = await Promise.allSettled([Promise.resolve().then(() => run.dispose())])
214
- if (execution.status === 'rejected') throw execution.reason
215
- if (disposal.status === 'rejected') throw disposal.reason
216
- const result = execution.value
217
- if (result.stopReason !== 'completed') {
218
- throw new Error(`subagent stopped with ${result.stopReason}${result.diagnostic === undefined ? '' : `: ${result.diagnostic}`}`)
219
- }
220
- return { kind: 'foreground', runId: run.id, output: result.output }
221
- }
222
-
223
- function availableAssemblyTools(tools, compositionScope) {
224
- return tools.schemas(compositionScope).map((schema) => schema.name)
225
- }
226
-
227
- function createShadowTool(ctx, tools, compositionScope, compiled, kind, config) {
228
- const toolName = SHADOW_TOOL_NAMES[kind]
229
- const provider = providerFor(kind, config)
230
- const properties = { ...BASE_PARAMETERS, ...(kind === 'spawn' ? SPAWN_PARAMETERS : {}), ...buildSubagentToolParameters(compiled) }
231
- return {
232
- name: toolName,
233
- description: `创建子代理执行委派任务;实例工具权限由 subagentToolPolicy 解析并在创建时冻结。`,
234
- parameters: { type: 'object', properties, required: ['description', 'prompt'] },
235
- output: { schema: { type: 'object', additionalProperties: true }, render: (_args, value) => [{ type: 'text', text: JSON.stringify(value, null, 2) }] },
236
- execute: async (args, run) => {
237
- validateShadowArgs(args, compiled, kind)
238
- if (run.agent === undefined) throw new Error('no active agent for subagent start')
239
- const resolved = resolveSubagentToolPolicy(compiled, args, availableAssemblyTools(tools, compositionScope))
240
- if (resolved.requiresApproval) await expansionApproval(ctx, run, toolName, resolved)
241
- const childProvider = ctx.subagents.getProvider(provider)
242
- if (childProvider === undefined) throw new Error(`subagent provider ${provider} is not registered`)
243
- if (childProvider.capabilities?.toolFilter !== true) {
244
- throw new Error(`provider ${provider} does not support per-child toolFilter`)
245
- }
246
- const maxDepth = config?.maxDepth === 'provider-managed' ? undefined : config?.maxDepth
247
- if (typeof maxDepth === 'number' && childProvider.capabilities?.depthLimit !== true) {
248
- throw new Error(`provider ${provider} cannot enforce maxDepth`)
249
- }
250
- const agentOptions = kind === 'spawn' ? requestedAgentOptions(config, args) : undefined
251
- if (agentOptions !== undefined && childProvider.capabilities?.agentOptions !== true) {
252
- throw new Error(`provider ${provider} does not support child agentOptions`)
253
- }
254
- if (kind === 'spawn' && agentOptions !== undefined) {
255
- await preflightRoute(ctx, run.agent, agentOptions, run.signal)
256
- if (ctx.subagents.getProvider(provider) !== childProvider) throw new Error(`subagent provider ${provider} changed during route preflight`)
257
- }
258
- run.signal.throwIfAborted()
259
- const request = {
260
- prompt: [{ type: 'text', text: args.prompt }],
261
- parent: run.agent,
262
- ...(agentOptions === undefined ? {} : { agentOptions }),
263
- ...(maxDepth === undefined ? {} : { maxDepth }),
264
- toolFilter: { allow: resolved.effectiveTools },
265
- }
266
- const policy = {
267
- profile: resolved.profileId,
268
- characterId: resolved.characterId ?? null,
269
- taskType: resolved.taskType ?? null,
270
- adoptedSelectors: resolved.adopted,
271
- ignoredSelectors: resolved.ignored,
272
- additionalTools: resolved.additionalTools,
273
- effectiveTools: resolved.effectiveTools,
274
- }
275
- if (args.run_in_background !== false) {
276
- if (childProvider.prepareContinuable === undefined) throw new Error(`provider ${provider} does not support continuable children`)
277
- const child = await ctx.subagents.startContinuable({ provider, label: args.description, request, signal: run.signal })
278
- return { kind: 'continuable', subagentId: child.childId, policy }
279
- }
280
- const childRun = await ctx.subagents.start(provider, { ...request, label: args.description, signal: run.signal })
281
- return { ...(await settleForeground(childRun)), policy }
282
- },
283
- }
284
- }
285
-
286
- export function apply(ctx, config) {
287
- // 内联 policy 在准备期即可校验;旧配置字段仍保留历史归一化结果。
288
- const source = configContract.parse(config, name)
289
- let compiled
290
- try {
291
- compiled = loadCompiledPolicy(source)
292
- } catch (error) {
293
- const message = error instanceof Error ? error.message : String(error)
294
- // 策略文件缺失 = 用户在能力卡里把开关关掉了(模块声明保留):降级为官方委派行为,
295
- // 不注册 shadow 工具,也不让整个预设启动失败。文件在但内容损坏才是真错误——fail loud。
296
- if (error?.code === 'ENOENT') {
297
- ctx.logger?.warn(`${name}: policy file missing, subagent tool policy disabled; official delegation behavior kept`)
298
- return
299
- }
300
- throw new Error(`${name}: cannot load policy: ${message}`)
301
- }
302
- const compositionScope = scopeOf(ctx)
303
- if (compositionScope === undefined) throw new Error(`${name}: requires an assembly scope`)
304
- const installs = new Map()
305
- const installing = new WeakSet()
306
- let active = true
307
- const belongsToComposition = (agent) => scopeChainOf(scopeOf(agent.ctx)).includes(compositionScope)
308
- const install = (agent) => {
309
- if (!active || installing.has(agent) || installs.has(agent) || !belongsToComposition(agent)) return
310
- // 注册走 disposer 契约。这里保留 ctx.effect 而不是 shared.keepDisposer,两条依据:
311
- // (1) 两个 shadow 必须整组注册:任一个失败时由 Cordis 回滚已注册的那个
312
- // (keepDisposer 无回滚语义,第二个失败会留下第一个的残留注册);
313
- // (2) remove(agent) 需要按 agent 定向撤销,而 keepDisposer 返回 void,拿不到句柄。
314
- // register/unregister 同步发 tools/change;登记完成前的重入不能再次注册同名工具。
315
- installing.add(agent)
316
- try {
317
- const dispose = agent.ctx.effect(() => {
318
- const disposers = [
319
- agent.ctx.tools.register(createShadowTool(ctx, agent.ctx.tools, compositionScope, compiled, 'spawn', source)),
320
- agent.ctx.tools.register(createShadowTool(ctx, agent.ctx.tools, compositionScope, compiled, 'fork', source)),
321
- ]
322
- return () => { for (const item of disposers) item() }
323
- }, `${name}: ${agent.id} shadow`)
324
- installs.set(agent, dispose)
325
- } finally {
326
- installing.delete(agent)
327
- }
328
- }
329
- const remove = (agent) => {
330
- const dispose = installs.get(agent)
331
- if (dispose === undefined) return
332
- installs.delete(agent)
333
- void Promise.resolve(dispose()).catch((error) => ctx.logger?.warn(`${name}: shadow cleanup failed: ${String(error)}`))
334
- }
335
- ctx.on('agent/created', ({ agent }) => install(agent))
336
- ctx.on('agent/disposed', ({ agent }) => remove(agent))
337
- const reconcile = () => {
338
- for (const agent of ctx.agents.list()) {
339
- if (belongsToComposition(agent)) install(agent)
340
- else remove(agent)
341
- }
342
- }
343
- ctx.on('tools/change', reconcile)
344
- ctx.effect(() => () => {
345
- // Agent 可以继续存活:模块先停接线,再撤回其持有的 Agent scope 贡献。
346
- active = false
347
- const disposers = [...installs.values()]
348
- installs.clear()
349
- return Promise.all(disposers.map(dispose => dispose())).then(() => undefined)
350
- }, `${name}: shadows`)
351
- reconcile()
352
- }
@@ -1,173 +0,0 @@
1
- /** 旧 triggers 声明供离线迁移与历史契约验证;新运行时只编译 rules。 */
2
- import { ACTION_KINDS, actionExecutionPoint, prepareAction, registerAction } from './actions.mjs'
3
- import { WATERFALL_POSITIONS, orderTriggers, registrationOptions, wireTriggerObservers } from './trigger.mjs'
4
- import { compileWhen } from './conditions/index.mjs'
5
- import { conjunction, expandActions } from './branch.mjs'
6
- export { compileWhen, COMPOSITE_OPERATORS, PREDICATE_FACTORIES } from './conditions/index.mjs'
7
- const DECLARATION_FIELDS = Object.freeze(['id', 'channel', 'channelOrder', 'waterfallPosition', 'phase', 'if', 'then', 'else', 'when', 'do'])
8
- const ACTION_PHASES = Object.freeze(['before-next', 'after-next'])
9
-
10
- /** `then`(单个动作或数组)归一化为动作声明数组;分支节点留到 `expandActions` 展开。 */
11
- function actionList(value, label) {
12
- const list = Array.isArray(value) ? value : [value]
13
- if (list.length === 0) throw new TypeError(`${label} must be an action declaration or a non-empty array of them`)
14
- return list
15
- }
16
-
17
- /** 展开后的叶子必须是已知动作。 */
18
- function assertActionKind(action, at) {
19
- if (action === null || typeof action !== 'object' || Array.isArray(action)) {
20
- throw new TypeError(`trigger-spec: ${at} must be an action declaration object`)
21
- }
22
- if (typeof action.kind !== 'string' || !Object.hasOwn(ACTION_KINDS, action.kind)) {
23
- throw new TypeError(
24
- `trigger-spec: ${at}.kind must be one of ${Object.keys(ACTION_KINDS).join(', ')} — got ${JSON.stringify(action.kind)}`,
25
- )
26
- }
27
- return action
28
- }
29
-
30
- /**
31
- * 声明级 `if` 与动作自身分支条件的合取;来自**声明级** `else` 的动作跳过声明级判定
32
- * (它的条件里已含 `not(if)`)。两个条件缺省都表示无条件。
33
- */
34
- function actionGate(declarationWhen, action) {
35
- const own = action.actionWhen
36
- const base = action.bypassRuleWhen === true ? undefined : declarationWhen
37
- if (base === undefined) return own
38
- if (own === undefined) return base
39
- return subject => base(subject) === true && own(subject) === true
40
- }
41
-
42
- /**
43
- * 编译一条声明:校验字段 + 编译 `if` 判定 + 归一化 `then` 动作。
44
- *
45
- * @param {object} spec 声明(`{ id, channel, if?, then, else?, channelOrder?, waterfallPosition?, phase? }`)
46
- * @param {object} [context] 同 {@link compileWhen},另可提供 promptConfigOptions(createPromptConfigs 的模板/策略编译选项)。
47
- * @returns {{id: string, channel: string, channelOrder: number, waterfallPosition: string, phase: string, when: Function|undefined, actions: object[], promptConfigOptions?: object}}
48
- */
49
- export function compileDeclaration(spec, context = {}) {
50
- if (spec === null || typeof spec !== 'object' || Array.isArray(spec)) {
51
- throw new TypeError('trigger-spec: trigger declaration must be an object')
52
- }
53
- const unknown = Object.keys(spec).filter((key) => !DECLARATION_FIELDS.includes(key))
54
- if (unknown.length > 0) {
55
- throw new TypeError(
56
- `trigger-spec: unknown trigger field(s) ${unknown.sort().join(', ')} — allowed: ${[...DECLARATION_FIELDS].sort().join(', ')}`,
57
- )
58
- }
59
- if (typeof spec.id !== 'string' || spec.id.length === 0) {
60
- throw new TypeError('trigger-spec: trigger id must be a non-empty string')
61
- }
62
- if (typeof spec.channel !== 'string' || spec.channel.length === 0) {
63
- throw new TypeError(`trigger-spec: trigger ${spec.id}: channel must be a non-empty event name`)
64
- }
65
- const label = `trigger-spec: trigger ${spec.id}`
66
- // 旧名已按引擎重构退役:显式拒绝并给出新名。
67
- if (spec.when !== undefined) throw new TypeError(`${label}: "when" 已退役,改用 "if"`)
68
- if (spec.do !== undefined) throw new TypeError(`${label}: "do" 已退役,改用 "then"`)
69
- const triggerIf = spec.if
70
- const triggerThen = spec.then
71
- if (triggerThen === undefined) throw new TypeError(`${label}: then is required`)
72
- const channelOrder = spec.channelOrder ?? 0
73
- if (!Number.isSafeInteger(channelOrder) || channelOrder < 0) {
74
- throw new TypeError(`trigger-spec: trigger ${spec.id}: channelOrder must be a non-negative safe integer`)
75
- }
76
- const waterfallPosition = spec.waterfallPosition ?? 'default'
77
- if (!WATERFALL_POSITIONS.has(waterfallPosition)) {
78
- throw new TypeError(`trigger-spec: trigger ${spec.id}: waterfallPosition must be one of ${[...WATERFALL_POSITIONS].join(', ')}`)
79
- }
80
- // 声明级 `if` 由注册侧判定;声明级 `else` 的动作自带 `not(if)` 并跳过它,否则自相矛盾。
81
- const expanded = [
82
- ...expandActions(actionList(triggerThen, `${label}.then`), {}, `${label}.then`),
83
- ...(spec.else === undefined ? [] : expandActions(actionList(spec.else, `${label}.else`), {
84
- outer: triggerIf === undefined ? [] : [{ not: triggerIf }],
85
- bypass: true,
86
- }, `${label}.else`)),
87
- ]
88
- const when = compileWhen(triggerIf, context)
89
- const actions = expanded.map(({ node, conditions, bypass }, index) => Object.assign(
90
- { ...assertActionKind(node, `action[${index}]`) },
91
- { actionWhen: compileWhen(conjunction(conditions), context), bypassRuleWhen: bypass },
92
- ))
93
- // 保存期复用注册入口的纯准备阶段;不绑定 ctx,不创建预算或监听器。
94
- for (const action of actions) prepareAction(action, {
95
- when: action.actionWhen ?? when, promptConfigOptions: context.promptConfigOptions, ...registrationOptions({ waterfallPosition }),
96
- })
97
- const phase = spec.phase ?? actionExecutionPoint(actions[0]).phase
98
- if (!ACTION_PHASES.includes(phase)) {
99
- throw new TypeError(`trigger-spec: trigger ${spec.id}: phase must be one of ${ACTION_PHASES.join(', ')}`)
100
- }
101
- for (const action of actions) {
102
- const point = actionExecutionPoint(action)
103
- if (spec.channel !== point.channel) {
104
- throw new TypeError(`trigger-spec: trigger ${spec.id}: action ${action.kind} channel must be ${point.channel}`)
105
- }
106
- if (phase !== point.phase) {
107
- throw new TypeError(`trigger-spec: trigger ${spec.id}: action ${action.kind} phase must be ${point.phase}`)
108
- }
109
- }
110
- return {
111
- id: spec.id,
112
- channel: spec.channel,
113
- channelOrder,
114
- waterfallPosition,
115
- phase,
116
- when,
117
- actions,
118
- ...(context.promptConfigOptions === undefined ? {} : { promptConfigOptions: context.promptConfigOptions }),
119
- }
120
- }
121
-
122
- /**
123
- * 编译一组声明:先归一化,再按 `channelOrder` 做稳定排序(同值保持声明序)。
124
- * 排序复用 `trigger.mjs` 的 `orderTriggers`——声明路径与内联函数路径共用同一套调度语义。
125
- */
126
- export function compileDeclarations(specs, context = {}) {
127
- if (!Array.isArray(specs)) throw new TypeError('trigger-spec: triggers must be an array')
128
- return orderTriggers(specs.map((spec) => compileDeclaration(spec, context)))
129
- }
130
-
131
- /** 该声明在其通道上的注册选项(只有 `outermost` 才 prepend),转发 `trigger.mjs` 的实现。 */
132
- export function declarationRegistrationOptions(compiled) {
133
- return registrationOptions(compiled)
134
- }
135
-
136
- /**
137
- * 把**编译好的**声明逐条注册——声明路径的挂载入口。
138
- *
139
- * 一条声明的每个动作各注册一次,带上:
140
- * - `when`:该声明的判定(`undefined` = 无条件,注册侧据此跳过判定);
141
- * - `prepend`:`waterfallPosition === 'outermost'` 时落在外层。
142
- * 会话态谓词(相位 / 计数)的 `observe` 喂入走 `wireTriggerObservers`——与
143
- * `mountTriggers` 的内联路径**共用同一实现**,不各写一条 `session/event` 监听。
144
- *
145
- * @param ctx 挂载作用域
146
- * @param compiled {@link compileDeclarations} 的产物
147
- * @returns disposer:撤销全部注册(含 observe 接线与每个动作自己的释放)
148
- */
149
- export function mountDeclarations(ctx, compiled, { plugin, warnOnce } = {}) {
150
- const disposers = []
151
- const observer = wireTriggerObservers(ctx, compiled, { plugin, warnOnce })
152
- if (observer !== undefined) disposers.push(observer)
153
- for (const trigger of compiled) {
154
- for (const action of trigger.actions) {
155
- disposers.push(registerAction(ctx, action, {
156
- plugin,
157
- warnOnce,
158
- promptConfigOptions: trigger.promptConfigOptions,
159
- when: actionGate(trigger.when, action),
160
- prepend: trigger.waterfallPosition === 'outermost',
161
- }))
162
- }
163
- }
164
- return () => {
165
- for (const dispose of disposers.splice(0)) {
166
- try {
167
- if (typeof dispose === 'function') dispose()
168
- } catch {
169
- // 释放失败不得反过来打断卸载流程(与 actions.mjs 同一纪律)。
170
- }
171
- }
172
- }
173
- }
@@ -1,8 +0,0 @@
1
- /** 世界书管理工具预设模块。 */
2
- export const name = "world-book-tools"
3
-
4
- export function apply(ctx) {
5
- ctx.inject(["pt-world-book-tools"], (scopeCtx) => {
6
- scopeCtx.effect(() => scopeCtx.get("pt-world-book-tools").mount(scopeCtx), name + ": mount")
7
- })
8
- }
@@ -1 +0,0 @@
1
- export {}