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
package/engine/actions/index.mjs
CHANGED
|
@@ -4,13 +4,14 @@ import { ACTION_KINDS } from './catalog.mjs'
|
|
|
4
4
|
import { actionSupportsWhen, actionExecutionPoint, createTurnBudget, withWhen, channelBinder } from './registration.mjs'
|
|
5
5
|
import { prepareInjectText } from './inject-text.mjs'
|
|
6
6
|
import { prepareAssembly } from './assembly.mjs'
|
|
7
|
-
import {
|
|
7
|
+
import { prepareToolGate } from './tool-gate.mjs'
|
|
8
|
+
import { prepareToolResult } from './tool-result.mjs'
|
|
8
9
|
import { prepareAppendContext } from './append-context.mjs'
|
|
9
|
-
import { prepareGuard } from './guard.mjs'
|
|
10
|
-
import { prepareSdkStrip } from './sdk-strip.mjs'
|
|
11
10
|
import { prepareRequestParams } from './request-params.mjs'
|
|
11
|
+
import { prepareStreamOutput } from './stream-output.mjs'
|
|
12
12
|
import { prepareInboxPrepend } from './inbox-prepend.mjs'
|
|
13
13
|
import { preparePreStepFilter } from './pre-step-filter.mjs'
|
|
14
|
+
import { prepareToolVisibility } from './tool-visibility.mjs'
|
|
14
15
|
export { ACTION_KINDS, ACTION_DEGRADE } from './catalog.mjs'
|
|
15
16
|
export { actionSupportsWhen, actionExecutionPoint } from './registration.mjs'
|
|
16
17
|
const DEFAULT_PLUGIN = 'prompt-actions'
|
|
@@ -18,13 +19,14 @@ const DEFAULT_PLUGIN = 'prompt-actions'
|
|
|
18
19
|
const PREPARERS = {
|
|
19
20
|
'inject-text': prepareInjectText,
|
|
20
21
|
assembly: prepareAssembly,
|
|
21
|
-
|
|
22
|
+
'tool-gate': prepareToolGate,
|
|
23
|
+
'tool-result': prepareToolResult,
|
|
22
24
|
'append-context': prepareAppendContext,
|
|
23
|
-
guard: prepareGuard,
|
|
24
|
-
'sdk-strip': prepareSdkStrip,
|
|
25
25
|
'request-params': prepareRequestParams,
|
|
26
|
+
'stream-output': prepareStreamOutput,
|
|
26
27
|
'inbox-prepend': prepareInboxPrepend,
|
|
27
28
|
'pre-step-filter': preparePreStepFilter,
|
|
29
|
+
'tool-visibility': prepareToolVisibility,
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
|
@@ -32,7 +34,7 @@ const PREPARERS = {
|
|
|
32
34
|
*
|
|
33
35
|
* @param action `{ kind, id?, ... }`,形状见 {@link ACTION_KINDS}。
|
|
34
36
|
* @param options.plugin 调用方插件名(错误消息 / 告警 / disposer 标签),与
|
|
35
|
-
*
|
|
37
|
+
* 与规则运行时 `mountRuleSources` 同一约定;缺省 `prompt-actions`。
|
|
36
38
|
* @param options.warnOnce 复用宿主模块的告警器;缺省为本文件自己的 warnOnce。
|
|
37
39
|
* @param options.when 可选判定:不命中(非 `true`)即放行下游、不执行本动作。仅对
|
|
38
40
|
* {@link ON_REGISTERED_KINDS} 里的动作有效;对 `inject-text` / `guard` 传它会挂载期报错
|
|
@@ -40,6 +42,7 @@ const PREPARERS = {
|
|
|
40
42
|
* @param options.prepend 让本动作落在宿主 waterfall 的**最外层**(否决型动作需要)。同样只对
|
|
41
43
|
* `ON_REGISTERED_KINDS` 有效;它表达的是**位置**,不承担声明之间的排序。
|
|
42
44
|
* @param options.promptConfigOptions 宿主提供的 createPromptConfigs 选项(模板基准和策略目录),不从动作数据读取。
|
|
45
|
+
* @param options.agent 当前挂载 scope 的真实 Agent;受众限定的固定系统段必须显式提供。
|
|
43
46
|
* @returns 已校验的动作类别与调用方插件名。
|
|
44
47
|
*/
|
|
45
48
|
export function validateActionOptions(action, options = {}) {
|
|
@@ -72,14 +75,17 @@ export function prepareAction(action, options = {}) {
|
|
|
72
75
|
return (ctx) => {
|
|
73
76
|
const warnOnce = options.warnOnce ?? createWarnOnce(ctx, plugin)
|
|
74
77
|
const budget = createTurnBudget(max)
|
|
75
|
-
const on = withWhen(options.on ?? channelBinder(ctx, kind, plugin, options.prepend === true), options.when, warnOnce, phase)
|
|
76
78
|
const disposers = []
|
|
77
79
|
const collect = (disposer) => { if (typeof disposer === 'function') disposers.push(disposer) }
|
|
80
|
+
const missHandlers = []
|
|
81
|
+
const onMiss = (callback) => { if (typeof callback === 'function') missHandlers.push(callback) }
|
|
82
|
+
const missAll = (...args) => { for (const callback of missHandlers) { try { callback(...args) } catch { /* 撤销失败不得反过来打断装配 */ } } }
|
|
83
|
+
const on = withWhen(options.on ?? channelBinder(ctx, kind, plugin, options.prepend === true), options.when, warnOnce, phase, missAll)
|
|
78
84
|
// 每轮预算的轮边界由 durable 事件驱动(与谓词的 observe 同一事件源,但**独立记账**:
|
|
79
85
|
// 预算是「本动作生效次数」,不是 durable 事件计数)。
|
|
80
86
|
if (budget !== undefined) collect(ctx.on('session/event', (session, event) => budget.observe(session, event)))
|
|
81
87
|
const take = (subject) => budget?.take(subject) ?? true
|
|
82
|
-
bind?.(ctx, { warnOnce, on, collect, take })
|
|
88
|
+
bind?.(ctx, { warnOnce, on, collect, take, onMiss, agent: options.agent })
|
|
83
89
|
const dispose = () => {
|
|
84
90
|
for (const release of disposers.splice(0)) {
|
|
85
91
|
try {
|
|
@@ -90,6 +96,8 @@ export function prepareAction(action, options = {}) {
|
|
|
90
96
|
}
|
|
91
97
|
}
|
|
92
98
|
keepDisposer(ctx, dispose, `${plugin}: action ${labelOf(action)}`)
|
|
99
|
+
// 调用方(rule-runtime)把 miss 挂到该动作的条目上;没有回调时保持 undefined。
|
|
100
|
+
dispose.miss = missHandlers.length === 0 ? undefined : missAll
|
|
93
101
|
return dispose
|
|
94
102
|
}
|
|
95
103
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyPromptConfigSources } from '../executor.mjs'
|
|
2
|
+
import { createPreStepSource, presetSourceId } from '../pre-step-source.mjs'
|
|
2
3
|
import { wireLayers } from '../layers.mjs'
|
|
3
4
|
import { createPromptConfigs } from '../schema.mjs'
|
|
5
|
+
import { actionExecutionPoint } from './registration.mjs'
|
|
4
6
|
|
|
5
7
|
export function prepareInjectText(action, plugin, promptConfigOptions) {
|
|
6
8
|
const source = action.config
|
|
@@ -10,13 +12,31 @@ export function prepareInjectText(action, plugin, promptConfigOptions) {
|
|
|
10
12
|
if (typeof source.layer !== 'string' || source.layer.length === 0) {
|
|
11
13
|
throw new TypeError(`${plugin}: inject-text config.layer is required (九层之一)`)
|
|
12
14
|
}
|
|
15
|
+
// 层→注册点的判据只有 `actionExecutionPoint` 一处:没有通道的层必须在这里**显式拒绝**,
|
|
16
|
+
// 否则公开 API 的 inject-text 会静默变成「配了没效果」(原先它落到已删的 tool-pipeline 分派)。
|
|
17
|
+
actionExecutionPoint(action)
|
|
13
18
|
// 纯声明复用提示词配置编译;函数仅来自已有编程接口,不写回可序列化声明。
|
|
14
19
|
const config = typeof source.resolve === 'function' ? source : createPromptConfigs([source], promptConfigOptions)[0]
|
|
15
|
-
return (ctx, { warnOnce, collect }) => {
|
|
20
|
+
return (ctx, { warnOnce, collect, agent }) => {
|
|
16
21
|
if (config.layer === 'pre-step') {
|
|
17
|
-
|
|
22
|
+
const options = action.options ?? {}
|
|
23
|
+
const sourceId = typeof options.sourceId === 'string' && options.sourceId.length > 0
|
|
24
|
+
? options.sourceId
|
|
25
|
+
: presetSourceId(config.id)
|
|
26
|
+
const source = createPreStepSource({
|
|
27
|
+
sourceId,
|
|
28
|
+
ctx,
|
|
29
|
+
configs: [config],
|
|
30
|
+
onOutcome: options.onOutcome,
|
|
31
|
+
promotion: options.promotion,
|
|
32
|
+
})
|
|
33
|
+
collect(applyPromptConfigSources([source], {
|
|
34
|
+
agent,
|
|
35
|
+
...(options.prepend === undefined ? {} : { prepend: options.prepend }),
|
|
36
|
+
...(options.layers === undefined ? {} : { layers: options.layers }),
|
|
37
|
+
}))
|
|
18
38
|
return
|
|
19
39
|
}
|
|
20
|
-
collect(wireLayers(ctx, [config], warnOnce))
|
|
40
|
+
collect(wireLayers(ctx, [config], warnOnce, { agent }))
|
|
21
41
|
}
|
|
22
42
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NAME_LIST, labelOf } from './shared.mjs'
|
|
2
|
+
import { isPluginKind } from '../source-kind.mjs'
|
|
2
3
|
|
|
3
4
|
export function preparePreStepFilter(action, plugin) {
|
|
4
5
|
const label = labelOf(action)
|
|
@@ -36,10 +37,8 @@ export function preparePreStepFilter(action, plugin) {
|
|
|
36
37
|
if (blockPlugins !== undefined) {
|
|
37
38
|
messages = messages.filter((message) => {
|
|
38
39
|
const source = message?.source
|
|
39
|
-
// v4
|
|
40
|
-
|
|
41
|
-
const isPlugin = source?.kind === 'plugin'
|
|
42
|
-
|| (typeof source?.kind === 'string' && source.kind.startsWith('plugin:'))
|
|
40
|
+
// 插件来源(v4 或旧日志)的判定收在 source-kind;漏掉会让 blockPlugins 逃生阀静默失效。
|
|
41
|
+
const isPlugin = isPluginKind(source?.kind)
|
|
43
42
|
if (!isPlugin) return true
|
|
44
43
|
const name = source?.plugin
|
|
45
44
|
// 只认插件自报的字符串身份;非字符串一律不拦(宁可少拦,不可误删上下文)。
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MAX_TRACKED_SESSIONS } from '../shared.mjs'
|
|
2
2
|
import { subjectOf } from '../conditions/subject.mjs'
|
|
3
|
+
import { INJECTION_CHANNELS } from '../schema.mjs'
|
|
3
4
|
import { ACTION_KINDS } from './catalog.mjs'
|
|
4
5
|
|
|
5
6
|
export function channelBinder(ctx, kind, plugin, prepend) {
|
|
@@ -14,7 +15,7 @@ export function channelBinder(ctx, kind, plugin, prepend) {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const ON_REGISTERED_KINDS = new Set(['assembly', '
|
|
18
|
+
const ON_REGISTERED_KINDS = new Set(['assembly', 'tool-gate', 'tool-result', 'append-context', 'request-params', 'stream-output', 'inbox-prepend', 'pre-step-filter', 'tool-visibility'])
|
|
18
19
|
|
|
19
20
|
/** 编辑目录与注册校验共用支持面;不另给客户端维护一份名单。 */
|
|
20
21
|
export const actionSupportsWhen = (kind) => ON_REGISTERED_KINDS.has(kind)
|
|
@@ -25,10 +26,7 @@ export const actionSupportsWhen = (kind) => ON_REGISTERED_KINDS.has(kind)
|
|
|
25
26
|
*
|
|
26
27
|
* 依据(`@deepseek-ai/dsh-agent/lib/types/runtime-types.d.ts` 的逐事件 `@mode` 注释):
|
|
27
28
|
* **只有 `waterfall` 才有 `next`**;`agent/pre-step`/`agent/request` 是 waterfall,
|
|
28
|
-
* 而 `agent/inbox/inserted
|
|
29
|
-
* `agent/turn-stopping`(既有 append-context「续跑」的通道)是 **serial**——
|
|
30
|
-
* 两者都没有 `next`。第三项此前是个隐患:`append-context(continue)` 一旦带 `when`,
|
|
31
|
-
* 旧实现会把 `turn` 当成 `next` 调掉。
|
|
29
|
+
* 而 `agent/inbox/inserted` 是 **emit**,`agent/turn-stopping` 是 **serial**,两者没有 `next`。
|
|
32
30
|
*/
|
|
33
31
|
export const NEXT_FREE_CHANNELS = new Set([
|
|
34
32
|
'session/event',
|
|
@@ -38,30 +36,18 @@ export const NEXT_FREE_CHANNELS = new Set([
|
|
|
38
36
|
|
|
39
37
|
/** 动作的固定执行点;声明编译与 when 接线共用,不能用声明字段改变注册器。 */
|
|
40
38
|
export function actionExecutionPoint(action) {
|
|
41
|
-
if (action.kind === '
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
if (action.kind === '
|
|
45
|
-
return action.mode === 'continue'
|
|
46
|
-
? { channel: 'agent/turn-stopping', phase: 'before-next' }
|
|
47
|
-
: { channel: 'tools/post-execute', phase: 'after-next' }
|
|
48
|
-
}
|
|
39
|
+
if (action.kind === 'tool-gate') return { channel: 'tools/pre-execute', phase: 'before-next' }
|
|
40
|
+
if (action.kind === 'tool-result') return { channel: 'tools/post-execute', phase: 'before-next' }
|
|
41
|
+
if (action.kind === 'append-context') return { channel: 'tools/post-execute', phase: 'after-next' }
|
|
42
|
+
if (action.kind === 'stream-output') return { channel: 'llm/stream', phase: 'before-next' }
|
|
49
43
|
if (action.kind === 'inject-text') {
|
|
44
|
+
// 层→注册点由 schema 的 LAYER_DEFINITIONS 派生(`INJECTION_CHANNELS`),不在此另写层表。
|
|
50
45
|
const layer = action.config?.layer
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'runtime-context': 'system-prompt/assemble',
|
|
55
|
-
'agent-request': 'agent/request',
|
|
56
|
-
'llm-stream': 'llm/stream',
|
|
57
|
-
'turn-stop': 'agent/turn-stopping',
|
|
58
|
-
'subagent-start': 'subagent/start',
|
|
59
|
-
'subagent-end': 'subagent/end',
|
|
60
|
-
}[layer]
|
|
61
|
-
if (channel === undefined) {
|
|
62
|
-
throw new TypeError(`inject-text layer ${JSON.stringify(layer)} has no single trigger channel; tool-pipeline uses separate decision actions`)
|
|
46
|
+
const point = INJECTION_CHANNELS[layer]
|
|
47
|
+
if (point === undefined) {
|
|
48
|
+
throw new TypeError(`inject-text layer ${JSON.stringify(layer)} has no single trigger channel — 该层在工具链上不可注入,请改用 tool-gate / append-context 动作`)
|
|
63
49
|
}
|
|
64
|
-
return
|
|
50
|
+
return point
|
|
65
51
|
}
|
|
66
52
|
const channel = ACTION_KINDS[action.kind].events[0]
|
|
67
53
|
return { channel, phase: NEXT_FREE_CHANNELS.has(channel) ? 'before-next' : 'after-next' }
|
|
@@ -131,8 +117,9 @@ export function createTurnBudget(max) {
|
|
|
131
117
|
* 并行调用不越过预算(见 `deliberation-gate.mjs` 的注释),无条件 `await` 会破坏它。
|
|
132
118
|
* 返回 Promise 的谓词退回异步路径——声明编译出的谓词恒同步,这条兜底是给手写调用方的。
|
|
133
119
|
*/
|
|
134
|
-
export function withWhen(on, when, warnOnce, phase) {
|
|
120
|
+
export function withWhen(on, when, warnOnce, phase, onMiss) {
|
|
135
121
|
if (typeof when !== 'function') return on
|
|
122
|
+
const miss = () => { if (typeof onMiss === 'function') onMiss() }
|
|
136
123
|
// 注意参数个数:`on` 的签名是 `(event, handler, options)`——包装后的处理器必须占据
|
|
137
124
|
// **第二个**位置,多传一个参数会把 options 挤到错位(首版就踩了这个,被测试抓出)。
|
|
138
125
|
return (event, handler, options) => on(event, (...args) => {
|
|
@@ -143,7 +130,12 @@ export function withWhen(on, when, warnOnce, phase) {
|
|
|
143
130
|
// 事件参数表:按通道归一后再交给谓词——否则除 `names` 外,各原语在真实载荷上都
|
|
144
131
|
// 取不到它们要的域(`phase` 曾因此恒为「已晋升」、`count` 恒 0)。
|
|
145
132
|
const evaluate = (invoke, fallback) => {
|
|
146
|
-
const decide = (decided) =>
|
|
133
|
+
const decide = (decided) => {
|
|
134
|
+
if (decided === true) return invoke()
|
|
135
|
+
// 动作级 when 为假也是「本轮没命中」:撤销型动作要在这里收手,不能留下上一轮的闸。
|
|
136
|
+
miss()
|
|
137
|
+
return fallback()
|
|
138
|
+
}
|
|
147
139
|
const failed = (error) => {
|
|
148
140
|
warnOnce(`when predicate failed: ${String(error?.message ?? error)}`)
|
|
149
141
|
return fallback()
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { labelOf } from './shared.mjs'
|
|
2
|
-
import {
|
|
2
|
+
import { matchesAgentScope } from '../layers.mjs'
|
|
3
|
+
import { assertReplacePatch } from '../schema.mjs'
|
|
4
|
+
|
|
5
|
+
/** 浅合并或整体替换;unset 只删除值仍等于声明期望的键,保留其他来源的覆盖。 */
|
|
6
|
+
function applyAgentRequestParams(params, base) {
|
|
7
|
+
const patch = params.patch ?? {}
|
|
8
|
+
if (params.replace === true) return { ...patch }
|
|
9
|
+
const merged = { ...base, ...patch }
|
|
10
|
+
let result
|
|
11
|
+
for (const [key, expected] of Object.entries(params.unset ?? {})) {
|
|
12
|
+
if (!Object.is(merged[key], expected)) continue
|
|
13
|
+
result ??= { ...merged }
|
|
14
|
+
delete result[key]
|
|
15
|
+
}
|
|
16
|
+
return result ?? merged
|
|
17
|
+
}
|
|
3
18
|
|
|
4
19
|
export function prepareRequestParams(action, plugin) {
|
|
5
20
|
const label = labelOf(action)
|
|
@@ -11,9 +26,14 @@ export function prepareRequestParams(action, plugin) {
|
|
|
11
26
|
if (unset !== undefined && (unset === null || typeof unset !== 'object' || Array.isArray(unset))) {
|
|
12
27
|
throw new TypeError(`${plugin}: ${label}.unset must be an object of { key: expectedValue }`)
|
|
13
28
|
}
|
|
29
|
+
// 只接受布尔值,避免错误配置静默退化为浅合并。
|
|
30
|
+
if (action.replace !== undefined && typeof action.replace !== 'boolean') {
|
|
31
|
+
throw new TypeError(`${plugin}: ${label}.replace must be a boolean (省略 = false) — got ${JSON.stringify(action.replace)}`)
|
|
32
|
+
}
|
|
14
33
|
if (action.replace === true && unset !== undefined) {
|
|
15
34
|
throw new TypeError(`${plugin}: ${label} cannot combine replace with unset — 整体替换没有可比较的下游值`)
|
|
16
35
|
}
|
|
36
|
+
assertReplacePatch(patch, action.replace, `${plugin}: ${label}.patch`)
|
|
17
37
|
const params = { ...(patch !== undefined ? { patch } : {}), ...(unset !== undefined ? { unset } : {}), ...(action.replace === true ? { replace: true } : {}) }
|
|
18
38
|
return (_ctx, { warnOnce, on, collect, take }) => collect(on('agent/request', async (payload, next) => {
|
|
19
39
|
const base = await next()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { stringList } from '../fields.mjs'
|
|
2
|
-
import { parseToolNames } from '../shared.mjs'
|
|
3
2
|
export const RUN_CODE = 'run_code'
|
|
4
3
|
|
|
5
4
|
export const NAME_LIST = stringList({ as: 'set', dedupe: true, allowEmpty: true })
|
|
@@ -20,16 +19,14 @@ export const labelOf = (action) => (typeof action?.id === 'string' && action.id.
|
|
|
20
19
|
export function createMask(source, label, plugin, allowExtra) {
|
|
21
20
|
const allow = NAME_LIST.parse(source?.allow, plugin, `${label}.allow`)
|
|
22
21
|
const deny = NAME_LIST.parse(source?.deny, plugin, `${label}.deny`)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
22
|
+
// allow 与 deny 可以同时给:官方 ToolRestriction 就是 and/or,判据是「先按 allow 保留,
|
|
23
|
+
// 再按 deny 剔除」(deepseek-harness packages/core/tools/src/index.ts:701-706、759-765)。
|
|
26
24
|
if (allow === undefined && deny === undefined && allowExtra === undefined) {
|
|
27
25
|
throw new TypeError(`${plugin}: ${label} needs allow and/or deny — 空名单无法表达「剔哪些工具」`)
|
|
28
26
|
}
|
|
29
27
|
// `run_code` 是 PTC 呈现**唯一**的可调用入口(见本文件顶部)。把它写进 **deny** 名单会让
|
|
30
|
-
// 整个 PTC
|
|
31
|
-
//
|
|
32
|
-
// 2026-09-22 用户拍板:allow 模式允许点名它(旧模块本就能把它写进白名单)。
|
|
28
|
+
// 整个 PTC 面失效,一律挂载期拒绝。allow 名单不必点名它:它是保留传输,不属于可裁剪的
|
|
29
|
+
// 工具面;旧模块、tool-surface 这类 native 收窄声明因此可以直接与 PTC 共存。
|
|
33
30
|
if (deny?.has(RUN_CODE)) {
|
|
34
31
|
throw new TypeError(`${plugin}: ${label}.deny must not name the reserved ${RUN_CODE} transport — it is the only callable entry of the PTC presentation`)
|
|
35
32
|
}
|
|
@@ -50,6 +47,8 @@ export function createMask(source, label, plugin, allowExtra) {
|
|
|
50
47
|
/** 与 tool-filter 的名单判据 / Layer.admits 同一判据(tool-filter 模块已删除,判据保留)。 */
|
|
51
48
|
blocks: (toolName) => {
|
|
52
49
|
if (typeof toolName !== 'string' || toolName.length === 0) return false
|
|
50
|
+
// 保留传输不参与任何名单:allow 没点名也必须留下,deny 已在挂载期拒绝。
|
|
51
|
+
if (toolName === RUN_CODE) return false
|
|
53
52
|
if (deny !== undefined && deny.has(toolName)) return true
|
|
54
53
|
if (allowExtra?.has(toolName) === true) return false
|
|
55
54
|
// 声明了 allow、或声明了 allowFrom(= 白名单模式,静态名单视为空集),未点名即裁掉。
|
|
@@ -58,13 +57,23 @@ export function createMask(source, label, plugin, allowExtra) {
|
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
|
|
60
|
+
/**
|
|
61
|
+
* 工具可见面的单一判据:装配目录、`tools:sdk` 正文与执行闸共用同一份 fail-open 决定。
|
|
62
|
+
*
|
|
63
|
+
* `available` = 目录名 ∪ SDK 正文名;`required` = 静态 allow ∪ 动态解锁项,再剔掉
|
|
64
|
+
* `run_code`(保留传输)与 `mask.blocks()` 为真的名字(deny 覆盖者不该被要求存在)。
|
|
65
|
+
* 缺项时三面必须一起放宽——只放宽一面会让模型看到的与能调的不一致。
|
|
66
|
+
*/
|
|
67
|
+
export function toolSurfacePlan(mask, available) {
|
|
68
|
+
// 动作层的挂载期校验(prepareToolVisibility)已拒绝 requireMatch + 无 allow。
|
|
69
|
+
const required = mask.requireMatch
|
|
70
|
+
? [...new Set([...(mask.allow ?? []), ...(mask.allowExtra ?? [])])].filter((name) => name !== RUN_CODE && !mask.blocks(name))
|
|
71
|
+
: []
|
|
72
|
+
const missing = required.filter((name) => !available.has(name))
|
|
73
|
+
return { failOpen: missing.length > 0, missing }
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
export function requireString(value, label, plugin) {
|
|
62
77
|
if (typeof value !== 'string') throw new TypeError(`${plugin}: ${label} must be a string`)
|
|
63
78
|
return value
|
|
64
79
|
}
|
|
65
|
-
|
|
66
|
-
export function toolNameSet(value, plugin, field) {
|
|
67
|
-
if (value === undefined) return undefined
|
|
68
|
-
const names = NAME_LIST.parse(typeof value === 'string' ? parseToolNames(value) : value, plugin, field)
|
|
69
|
-
return names !== undefined && names.size > 0 ? names : undefined
|
|
70
|
-
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { labelOf } from './shared.mjs'
|
|
2
|
+
import { subjectOf } from '../conditions/subject.mjs'
|
|
3
|
+
|
|
4
|
+
async function* replacedStream(text) {
|
|
5
|
+
yield { type: 'block-start', index: 0, blockType: 'text' }
|
|
6
|
+
yield { type: 'text-delta', index: 0, text }
|
|
7
|
+
yield { type: 'block-end', index: 0, block: { type: 'text', text } }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function prepareStreamOutput(action, plugin) {
|
|
11
|
+
const label = labelOf(action)
|
|
12
|
+
const mode = action.mode ?? 'pass'
|
|
13
|
+
if (!['pass', 'replace'].includes(mode)) throw new TypeError(`${plugin}: ${label}.mode must be pass or replace`)
|
|
14
|
+
if (action.text !== undefined && typeof action.text !== 'string') throw new TypeError(`${plugin}: ${label}.text must be a string`)
|
|
15
|
+
const text = action.text ?? ''
|
|
16
|
+
return (ctx, { on, collect, take, warnOnce }) => collect(on('llm/stream', (options, next) => {
|
|
17
|
+
if (mode !== 'replace' || text.length === 0) return next()
|
|
18
|
+
try {
|
|
19
|
+
const agent = subjectOf('llm/stream', [options], warnOnce, ctx).agent
|
|
20
|
+
if (take({ agent })) return replacedStream(text)
|
|
21
|
+
} catch (error) {
|
|
22
|
+
warnOnce(`${plugin}: stream-output action ${label} failed: ${String(error?.message ?? error)}`)
|
|
23
|
+
}
|
|
24
|
+
return next()
|
|
25
|
+
}))
|
|
26
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { labelOf } from './shared.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* tool-gate —— 工具执行前的裁决(pre 相)。
|
|
5
|
+
*
|
|
6
|
+
* 从原 `decision` 拆出:字段只保留这一相真正读的键,写错相位就是未知字段而不是静默失效。
|
|
7
|
+
* 异常与条件未命中一律走 `next()`(放行)——裁决 bug 不该卡死调用。
|
|
8
|
+
*/
|
|
9
|
+
export function prepareToolGate(action, plugin) {
|
|
10
|
+
const label = labelOf(action)
|
|
11
|
+
const decision = action.decision ?? 'allow'
|
|
12
|
+
if (!['allow', 'deny', 'ask'].includes(decision)) {
|
|
13
|
+
throw new TypeError(`${plugin}: ${label}.decision must be one of allow, deny, ask`)
|
|
14
|
+
}
|
|
15
|
+
if (action.reason !== undefined && typeof action.reason !== 'string') {
|
|
16
|
+
throw new TypeError(`${plugin}: ${label}.reason must be a string`)
|
|
17
|
+
}
|
|
18
|
+
return (_ctx, { warnOnce, on, collect, take }) => collect(on('tools/pre-execute', async (exec, next) => {
|
|
19
|
+
try {
|
|
20
|
+
if (decision === 'allow') return next()
|
|
21
|
+
// `||` 而非 `??`:空串是「取引擎缺省」的中性值,不是「无解释的拒绝」。
|
|
22
|
+
const outcome = decision === 'deny'
|
|
23
|
+
? { kind: 'deny', reason: String(action.reason || `${label}: denied by action`) }
|
|
24
|
+
: { kind: 'ask' }
|
|
25
|
+
return take(exec) ? outcome : next()
|
|
26
|
+
} catch (error) {
|
|
27
|
+
warnOnce(`${plugin}: tool-gate action ${label} failed: ${String(error?.message ?? error)}`)
|
|
28
|
+
return next()
|
|
29
|
+
}
|
|
30
|
+
}))
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { labelOf } from './shared.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* tool-result —— 工具结果结算后的处理(post 相)。
|
|
5
|
+
*
|
|
6
|
+
* 从原 `decision` 拆出:只读这一相的字段。`text` 仅 replace / block 使用;
|
|
7
|
+
* 异常一律 `next()`(接受),绝不让结果处理把工具调用变成失败。
|
|
8
|
+
*/
|
|
9
|
+
export function prepareToolResult(action, plugin) {
|
|
10
|
+
const label = labelOf(action)
|
|
11
|
+
const mode = action.action ?? 'accept'
|
|
12
|
+
if (!['accept', 'replace', 'block'].includes(mode)) {
|
|
13
|
+
throw new TypeError(`${plugin}: ${label}.action must be one of accept, replace, block`)
|
|
14
|
+
}
|
|
15
|
+
if (action.text !== undefined && typeof action.text !== 'string') {
|
|
16
|
+
throw new TypeError(`${plugin}: ${label}.text must be a string`)
|
|
17
|
+
}
|
|
18
|
+
const text = typeof action.text === 'string' ? action.text : ''
|
|
19
|
+
return (_ctx, { warnOnce, on, collect, take }) => collect(on('tools/post-execute', async (exec, result, next) => {
|
|
20
|
+
try {
|
|
21
|
+
if (mode === 'accept') return next()
|
|
22
|
+
if (mode === 'replace' && text.length > 0) {
|
|
23
|
+
if (!take(exec)) return next()
|
|
24
|
+
return { kind: 'accept', content: [{ type: 'text', text }] }
|
|
25
|
+
}
|
|
26
|
+
if (mode === 'block') {
|
|
27
|
+
if (!take(exec)) return next()
|
|
28
|
+
return { kind: 'block', feedback: [{ type: 'text', text: text.length > 0 ? text : `${label}: blocked by action` }] }
|
|
29
|
+
}
|
|
30
|
+
return next()
|
|
31
|
+
} catch (error) {
|
|
32
|
+
warnOnce(`${plugin}: tool-result action ${label} failed: ${String(error?.message ?? error)}`)
|
|
33
|
+
return next()
|
|
34
|
+
}
|
|
35
|
+
}))
|
|
36
|
+
}
|