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,245 +1,35 @@
1
1
  /**
2
- * trigger — 触发器引擎(B3):把「声明」变成「注册」。
2
+ * trigger — 触发器的两个运行时设施:waterfall 位置取值,与观察者谓词的会话事件接线。
3
3
  *
4
- * 本文件负责调度层,**不含**判断原语与动作实现:
5
- * - `when` 判定由 `engine/conditions/` 提供(九个判断原语 + all/any/not/notAny 组合);
6
- * - `do` 动作由 `engine/actions/` 提供(九类动作);`predicates.mjs` / `actions.mjs` 只留重导出入口。
7
- * - 本文件只做:声明校验(复用 B2 的 fields.mjs)、同通道内的稳定排序、
8
- * 宿主 waterfall 位置映射、注册与 disposer、降级与统一告警、会话态生命周期。
4
+ * 本文件不承担声明校验、排序与注册:那些归 `rule-spec.mjs`(编译)与 `rule-runtime.mjs`
5
+ * (挂载)——规则是唯一的声明载体,`engine/rules` 之外没有第二条触发通道。
9
6
  *
10
- * ## 为什么排序必须由运行时自己做(R13 的权威依据)
7
+ * ## waterfallPosition 为什么与 channelOrder 分开
11
8
  *
12
9
  * 官方 Cordis(`node_modules/@deepseek-ai/cordis/src/events.ts`)**没有数字排序**:
13
- * - `:254-260` `register()` 只做 `options.prepend ? 'unshift' : 'push'`;
14
- * - `:234-243` `waterfall()` 用 `cbs.shift()` 从链首取,所以 unshift 的监听器最先执行
15
- * (最外层),而**多个 prepend 之间后注册的更靠前**;
16
- * - `:228-229` 注释:不调用 `next()` 的监听器会否决其后整条链(含内置行为)。
10
+ * - `register()` 只做 `options.prepend ? 'unshift' : 'push'`;
11
+ * - `waterfall()` 用 `cbs.shift()` 从链首取,所以 unshift 的监听器最先执行(最外层),
12
+ * 而**多个 prepend 之间后注册的更靠前**;不调用 `next()` 的监听器会否决其后整条链。
17
13
  *
18
- * 因此调度必须拆成两个互不混淆的字段,**不得**用一个字段同时承担两件事:
19
- * - `channelOrder: number` —— **同一官方通道内**的声明顺序,本文件做有界稳定排序
20
- * (升序,同值保持声明序),并标明动作在 `next()` 前还是后;
21
- * - `waterfallPosition` —— 适配器在宿主 waterfall 中的位置,只有 `'outermost'`
22
- * 才映射为 `prepend: true`,**不承担**声明间排序。
14
+ * 因此两个字段互不混淆,**不得**用一个字段同时承担两件事:
15
+ * - `channelOrder: number` —— 同一通道内的声明顺序,由 `rule-spec.mjs` 做有界稳定排序;
16
+ * - `waterfallPosition` —— 在宿主 waterfall 中的位置,只有 `'outermost'` 映射为 `prepend: true`。
23
17
  *
24
- * **跨九层的全局调度禁止**:官方九层插入点彼此独立,`prepend` 也不得替代执行层 guard
25
- * 的单调边界(那是 `ctx.tools.guard()` 的职责,见 B3 T2 的动作 (5))。
26
- *
27
- * ## 声明载体约定(与 T4 的能力提供者边界守卫对齐)
28
- *
29
- * 本文件导出的是**引擎本身**(`mountTriggers` / `orderTriggers` / `validateTrigger` /
30
- * `registrationOptions` / `createDecisionLog` 等函数与常量),**不是**某个模块的触发器声明。
31
- * 声明由**消费方模块**导出,约定名为 `engineTriggers`(数组,元素交给 `validateTrigger`);
32
- * 它与能力提供者的 `engineProvider` 登记**互斥**——同一模块不得同时导出两者,
33
- * 该互斥由 `test/engine/provider-boundary.test.mjs` 直接断言。
34
- * 因此「某个模块是触发器还是提供者」看的是它导出了哪一个,而不是它 import 了什么。
35
- *
36
- * ## 与 predicates 的接线要点(T1 交付时确认的事实)
37
- *
38
- * - `when` 直接接 `createXxxPredicate(...)` 的返回值——它们是**函数**,`await when(...)` 原生兼容;
39
- * - **相位 / 计数**两类谓词带 `observe(session, event)` 入口:本文件在同组声明上**共用一条**
40
- * `session/event` 监听喂它们(不按触发器各接一条),且没有这类谓词时**不接**(零开销);
41
- * - **当前预设**类需在**挂载期一次** `await loadStandingMountFor()`,把结果传进
42
- * `createPresetPredicate({ ctx, presetId, standingMountFor })`,这样判定期零 IO;
43
- * - 谓词**运行期不抛**(配置错误在挂载期 fail loud),所以 `when` 的 try/catch 是兜底而非主路径。
44
- *
45
- * ## `state`:会话态声明的最小接口(B4 迁移的契约基准)
46
- *
47
- * `state` 是**声明**(该触发器需要什么会话态),不是运行时对象:运行时的会话态一律经
48
- * `engine/shared.mjs` 的既有读法取得,**不新造接口、不新造存储通道**。B4 迁移既有模块时
49
- * 只允许依赖下面这一组,超出即是新发明的接口,须先回到本文件扩契约:
50
- *
51
- * | 读取 | 既有实现 | 最小形状 |
52
- * |--------------------|-----------------------------------|--------------------------------|
53
- * | 会话标识(记账键) | `session.id` | `string \| undefined` |
54
- * | durable 事件快照 | `shared.sessionEvents(session)` | `{ type, data? }[]`,缺接口=空 |
55
- * | 轮号 | 各模块内联 `event.data.turn` | 有限 `number`,缺失=无轮号 |
56
- * | 子代理判定 | `shared.isDelegated(session)` | `header.delegationDepth > 0` |
57
- * | 会话态 Map 上限 | `shared.MAX_TRACKED_SESSIONS` | 超出整体清空(触发冷扫重建) |
58
- *
59
- * 三条纪律:**(1)** 没有 `session.id` 的会话一律不记账(`predicates.mjs:343` 的既有决定:
60
- * 共用一个 Map 键会让两个会话串味),每次冷扫而非缓存;**(2)** 子代理判定复用
61
- * `isDelegated`,不写字面量比较(原 `progress-reminder` 的 `== 0` 判定与它是同一语义的
62
- * 两种写法,B4 迁移时统一到前者,行为不变);**(3)** `snapshotEvents()` 是正式 API
63
- * (DSH 0.1.2-alpha.4+),缺失时按空日志处理——**不得**回退读旧的 `events` 数组。
64
- *
65
- * **给 B4 的边界(B4 PLAN 的措辞与本文件不一致处,以此为准)**:B4 计划里的
66
- * `session(create, { resetOn })` **不能放进 `shared.mjs`**——`resetOn` 是复位时机,要落地
67
- * 就得在挂载期注册 `session/event` 监听,而 `shared.mjs` 是**没有 `ctx` 的纯函数模块**
68
- * (现有导出全是无状态工具)。B4 的统一访问接口须满足:**(a)** 接收挂载期 `ctx`(或由
69
- * `mountTriggers` 一侧构造),**(b)** 建在本节这组既有读法之上、**不新造事件类型或持久通道**,
70
- * **(c)** 逐模块保留原有键类型与淘汰策略(`Map<session.id>` / `WeakMap<session 对象>` /
71
- * 无上限),**(d)** `resetOn` 按**状态字段**声明,不是按模块(`tool-bootstrap` 的
72
- * `stage` 与 `promotion` 两个字段策略相反,是这条的现成反例)。
73
- */
74
-
75
- import { subjectOf } from './predicates.mjs'
76
-
77
- /**
78
- * 本文件此前**零 import**(`when` / `do` / `warnOnce` 全由调用方注入,会话态读法经 `ctx`
79
- * 取得)。唯一新增的依赖是谓词的**输入契约** `subjectOf`:调度层调用 `when` 时必须先把
80
- * 事件参数表归一为**单个载荷对象**(契约见 `predicates.mjs` 头部),否则除 `names` 外,
81
- * 各原语在真实通道上都取不到它们要的域——`phase` 曾因此恒为「已晋升」、`count` 恒为 0。
82
- * 声明路径同源:`actions.mjs` 的 `withWhen` 用同一函数归一。
18
+ * 跨九层的全局调度禁止:官方九层插入点彼此独立,`prepend` 也不得替代 `ctx.tools.guard()`
19
+ * 的单调边界。
83
20
  */
84
21
 
85
22
  /** 声明里的合法位置取值(与 events.ts 的布尔注册策略一一对应)。 */
86
23
  export const WATERFALL_POSITIONS = new Set(['default', 'outermost'])
87
24
 
88
- /** 动作在 `next()` 之前还是之后执行 —— 影响同通道内的可观测顺序。 */
89
- export const ACTION_PHASES = new Set(['before-next', 'after-next'])
90
-
91
- /**
92
- * 同通道内的稳定排序:`channelOrder` 升序,同值保持**声明序**。
93
- * 稳定是硬要求:同值声明的相对顺序若随排序实现变化,声明就不可预测。
94
- */
95
- export function orderTriggers(declarations) {
96
- const list = Array.isArray(declarations) ? declarations : []
97
- return list
98
- .map((declaration, index) => ({ declaration, index }))
99
- .sort((left, right) => {
100
- const delta = (left.declaration.channelOrder ?? 0) - (right.declaration.channelOrder ?? 0)
101
- return delta !== 0 ? delta : left.index - right.index
102
- })
103
- .map((entry) => entry.declaration)
104
- }
105
-
106
- /**
107
- * 把声明的位置字段映射为 Cordis 的注册选项。
108
- * 只有 `'outermost'` 才 prepend —— 这是**位置**,不是排序;同通道顺序归 `orderTriggers`。
109
- */
110
- export function registrationOptions(declaration) {
111
- return declaration.waterfallPosition === 'outermost' ? { prepend: true } : {}
112
- }
113
-
114
- /**
115
- * 校验一条触发器声明:
116
- * - 必填:`id`(非空串)、`channel`(官方事件名)、`when`(判定函数)、`do`(动作函数);
117
- * - `channelOrder` 缺省 0(非负安全整数)、`waterfallPosition` 缺省 `'default'`、
118
- * `phase` 缺省 `'after-next'`、`degrade` 缺省告警并跳过本触发器。
119
- * 未知字段一律 fail loud:声明写错键名是最容易被静默忽略的一类错。
120
- */
121
- export function validateTrigger(declaration, plugin) {
122
- if (declaration === null || typeof declaration !== 'object' || Array.isArray(declaration)) {
123
- throw new TypeError(`${plugin}: trigger declaration must be an object`)
124
- }
125
- const known = new Set(['id', 'channel', 'channelOrder', 'waterfallPosition', 'phase', 'when', 'do', 'degrade', 'state'])
126
- const unknown = Object.keys(declaration).filter((key) => !known.has(key))
127
- if (unknown.length > 0) {
128
- throw new TypeError(`${plugin}: unknown trigger field(s) ${unknown.join(', ')} — allowed: ${[...known].sort().join(', ')}`)
129
- }
130
- if (typeof declaration.id !== 'string' || declaration.id.length === 0) {
131
- throw new TypeError(`${plugin}: trigger.id must be a non-empty string`)
132
- }
133
- if (typeof declaration.channel !== 'string' || declaration.channel.length === 0) {
134
- throw new TypeError(`${plugin}: trigger ${declaration.id}: channel must be a non-empty event name`)
135
- }
136
- if (typeof declaration.when !== 'function') {
137
- throw new TypeError(`${plugin}: trigger ${declaration.id}: when must be a predicate function`)
138
- }
139
- if (typeof declaration.do !== 'function') {
140
- throw new TypeError(`${plugin}: trigger ${declaration.id}: do must be an action function`)
141
- }
142
- const channelOrder = declaration.channelOrder ?? 0
143
- if (!Number.isSafeInteger(channelOrder) || channelOrder < 0) {
144
- throw new TypeError(`${plugin}: trigger ${declaration.id}: channelOrder must be a non-negative safe integer`)
145
- }
146
- const waterfallPosition = declaration.waterfallPosition ?? 'default'
147
- if (!WATERFALL_POSITIONS.has(waterfallPosition)) {
148
- throw new TypeError(`${plugin}: trigger ${declaration.id}: waterfallPosition must be one of ${[...WATERFALL_POSITIONS].join(', ')}`)
149
- }
150
- const phase = declaration.phase ?? 'after-next'
151
- if (!ACTION_PHASES.has(phase)) {
152
- throw new TypeError(`${plugin}: trigger ${declaration.id}: phase must be one of ${[...ACTION_PHASES].join(', ')}`)
153
- }
154
- return {
155
- id: declaration.id,
156
- channel: declaration.channel,
157
- channelOrder,
158
- waterfallPosition,
159
- phase,
160
- when: declaration.when,
161
- do: declaration.do,
162
- degrade: declaration.degrade ?? 'skip',
163
- state: declaration.state,
164
- }
165
- }
166
-
167
- /**
168
- * 决策链诊断(T5):把「这次为什么没生效」变成一次可读的输出。
169
- *
170
- * 借鉴 dsh-agent-studio 的 `reportFirstDecision`(`apply.ts:238-250`):一次装配看起来
171
- * 没生效时,原因可能有多种而**结果长得一样** —— 取不到 agent/session、触发器未启用、
172
- * 谓词未命中、动作被外层门控剥离、判定走了降级。逐项试太慢,所以按顺序记一条链。
173
- *
174
- * 约束(PLAN T5 的 security):
175
- * - **每会话只输出一次**(按 sessionId 去重);
176
- * - 只记录触发器 id、通道、判定结果、动作是否执行、是否走降级 ——
177
- * **绝不打印消息正文、变量值或用户内容**;
178
- * - 默认关闭时零额外日志;日志自身抛错不得影响装配。
179
- *
180
- * **flush 由调用方驱动**:引擎无法知道「一轮装配」何时结束(同一通道可能有多个触发器、
181
- * 跨通道还会继续判定),所以 `mountTriggers` 只 `step`(累积到内存),
182
- * 由最外层监听器在整轮处理结束时调一次 `flush`。在步与步之间提前 flush 会输出**不完整的链**
183
- * —— 那正是本文件最初实现踩过的坑(被 T5 自己的断言抓出来)。
184
- */
185
- export function createDecisionLog({ enabled = false, logger, sessionOf } = {}) {
186
- const reported = new Set()
187
- const pending = new Map()
188
- // args 有两种形状:step 收到的是 handler 的完整参数数组,调用方 flush 时可能只给 payload。
189
- // 两种都要归一到同一个 key —— 否则 step 存 's-1'、flush 查 '<no-session>',链永远输出不出来
190
- //(这正是 T5 断言抓到的现象:logged.length 恒为 0)。
191
- const keyOf = (args) => {
192
- try {
193
- const values = Array.isArray(args) ? args : [args]
194
- return String(sessionOf?.(...values) ?? '<no-session>')
195
- } catch {
196
- return '<no-session>'
197
- }
198
- }
199
- return {
200
- /** 记一步(只入内存,不输出)。 */
201
- step(args, line) {
202
- if (!enabled) return
203
- const key = keyOf(args)
204
- const lines = pending.get(key) ?? []
205
- lines.push(line)
206
- pending.set(key, lines)
207
- },
208
- /** 该会话的链只输出一次;失败静默 —— 日志不得影响装配。 */
209
- flush(args) {
210
- if (!enabled) return
211
- const key = keyOf(args)
212
- if (reported.has(key)) return
213
- const lines = pending.get(key)
214
- if (lines === undefined || lines.length === 0) return
215
- reported.add(key)
216
- try {
217
- logger?.info?.(`[trigger-decision] session=${key} :: ${lines.join(' | ')}`)
218
- } catch {
219
- // 日志失败不得影响装配。
220
- }
221
- },
222
- }
223
- }
224
-
225
- /**
226
- * 注册一组触发器。
227
- *
228
- * 每个触发器在**自己的合法通道**上注册一次;`when` 抛错按 `degrade` 处理
229
- * (默认 `'skip'`:告警一次并跳过本次,**不吞掉下游异常** —— 只有本触发器自身的
230
- * 判定失败才降级,`next()` 之后的错误照常向上抛)。
231
- *
232
- * @returns disposer:注销本组全部监听器。
233
- */
234
25
  /**
235
26
  * 把 `session/event` 喂给带 `observe(session, event)` 入口的谓词(相位 / 计数两类)。
236
27
  *
237
- * 同一组声明**共用一条**监听(不按触发器各接一条,避免 N 倍监听);没有这类谓词时
238
- * **不接**(返回 `undefined`,零开销)。观察者自身抛错只告警、不影响其它触发器
28
+ * 同一组观察者**共用一条**监听(不按规则各接一条,避免 N 倍监听);没有这类谓词时
29
+ * **不接**(返回 `undefined`,零开销)。观察者自身抛错只告警、不影响其它观察者
239
30
  * ——与判定失败的降级纪律一致。
240
31
  *
241
- * 两条调用路径共用它:`mountTriggers`(`do` 是函数的内联路径)与
242
- * `trigger-spec.mjs` 的 `mountDeclarations`(`do` 是动作声明的路径)。
32
+ * 由 `rule-runtime.mjs` 的 `mountRuleSources` 接线:规则条件里的观察者谓词共用这一条监听。
243
33
  *
244
34
  * @returns disposer,或 `undefined`(本组无观察者)
245
35
  */
@@ -257,48 +47,3 @@ export function wireTriggerObservers(ctx, triggers, { plugin, warnOnce } = {}) {
257
47
  }
258
48
  })
259
49
  }
260
-
261
- export function mountTriggers(ctx, declarations, { plugin, warnOnce, diagnose } = {}) {
262
- const warn = typeof warnOnce === 'function' ? warnOnce : () => {}
263
- const disposers = []
264
- const ordered = orderTriggers((Array.isArray(declarations) ? declarations : []).map((raw) => validateTrigger(raw, plugin)))
265
-
266
- // 会话态生命周期:共用 `wireTriggerObservers`(与声明路径同一实现)。
267
- const observer = wireTriggerObservers(ctx, ordered, { plugin, warnOnce: warn })
268
- if (observer !== undefined) disposers.push(observer)
269
-
270
- for (const trigger of ordered) {
271
- const handler = async (...args) => {
272
- const next = args[args.length - 1]
273
- const payload = args.slice(0, -1)
274
- const result = trigger.phase === 'after-next' ? await next() : undefined
275
- const pass = () => trigger.phase === 'after-next' ? result : next()
276
- let decided
277
- try {
278
- decided = await trigger.when(subjectOf(trigger.channel, payload, warn))
279
- } catch (error) {
280
- // 判定失败只影响本触发器:告警一次并放行下游(expose-all 语义)。
281
- warn(`${plugin}: trigger ${trigger.id} predicate failed: ${String(error?.message ?? error)}`)
282
- diagnose?.step(args, `${trigger.id}@${trigger.channel} predicate=error action=skipped`)
283
- return pass()
284
- }
285
- if (decided !== true) {
286
- diagnose?.step(args, `${trigger.id}@${trigger.channel} predicate=miss action=skipped`)
287
- return pass()
288
- }
289
- diagnose?.step(args, `${trigger.id}@${trigger.channel} predicate=hit phase=${trigger.phase} action=ran`)
290
- if (trigger.phase === 'before-next') {
291
- // 返回值参与瀑布:裁决类动作在 `next()` **之前**给出终局(deny/ask),
292
- // 不返回值即「无话可说」,交给下游(与「只 guard 自身逻辑」一致)。
293
- const early = await trigger.do(...payload)
294
- return early === undefined ? next() : early
295
- }
296
- // 返回值同样参与瀑布:这正是裁决类动作(deny / ask / replace / block)的出口
297
- // ——丢弃它会让「命中却拦不住」,是本文件被 trigger-rebuild 对拍抓到的缺陷。
298
- const overridden = await trigger.do(...payload, result)
299
- return overridden === undefined ? result : overridden
300
- }
301
- disposers.push(ctx.on(trigger.channel, handler, registrationOptions(trigger)))
302
- }
303
- return () => { for (const dispose of disposers) dispose() }
304
- }
@@ -64,46 +64,47 @@ function plainValue(source, onError) {
64
64
  }
65
65
  if (badChar)
66
66
  onError(0, 'BAD_SCALAR_START', `Plain value cannot start with ${badChar}`);
67
- return foldLines(source);
67
+ return unfoldLines(source);
68
68
  }
69
69
  function singleQuotedValue(source, onError) {
70
70
  if (source[source.length - 1] !== "'" || source.length === 1)
71
71
  onError(source.length, 'MISSING_CHAR', "Missing closing 'quote");
72
- return foldLines(source.slice(1, -1)).replace(/''/g, "'");
72
+ return unfoldLines(source.slice(1, -1)).replace(/''/g, "'");
73
73
  }
74
- function foldLines(source) {
74
+ function unfoldLines(source) {
75
+ const line = /(.*?)\r?\n/sy;
76
+ let match = line.exec(source);
77
+ if (!match)
78
+ return source;
75
79
  /**
76
- * The negative lookbehind here and in the `re` RegExp is to
80
+ * The negative lookbehinds in these RegExps are to
77
81
  * prevent causing a polynomial search time in certain cases.
78
82
  *
79
- * The try-catch is for Safari, which doesn't support this yet:
83
+ * The try-catch is for Safari < 16.4 and other old browsers:
80
84
  * https://caniuse.com/js-regexp-lookbehind
81
85
  */
82
- let first, line;
86
+ let trimEnd, trimBoth;
83
87
  try {
84
- first = new RegExp('(.*?)(?<![ \t])[ \t]*\r?\n', 'sy');
85
- line = new RegExp('[ \t]*(.*?)(?:(?<![ \t])[ \t]*)?\r?\n', 'sy');
88
+ trimEnd = new RegExp('(?<![ \t])[ \t]+$');
89
+ trimBoth = new RegExp('^[ \t]+|(?<![ \t])[ \t]+$', 'g');
86
90
  }
87
91
  catch {
88
- first = /(.*?)[ \t]*\r?\n/sy;
89
- line = /[ \t]*(.*?)[ \t]*\r?\n/sy;
92
+ trimEnd = /[ \t]+$/;
93
+ trimBoth = /^[ \t]+|[ \t]+$/g;
90
94
  }
91
- let match = first.exec(source);
92
- if (!match)
93
- return source;
94
- let res = match[1];
95
+ let res = match[1].replace(trimEnd, '');
95
96
  let sep = ' ';
96
- let pos = first.lastIndex;
97
- line.lastIndex = pos;
97
+ let pos = line.lastIndex;
98
98
  while ((match = line.exec(source))) {
99
- if (match[1] === '') {
99
+ const lm = match[1].replace(trimBoth, '');
100
+ if (lm === '') {
100
101
  if (sep === '\n')
101
102
  res += sep;
102
103
  else
103
104
  sep = '\n';
104
105
  }
105
106
  else {
106
- res += sep + match[1];
107
+ res += sep + lm;
107
108
  sep = ' ';
108
109
  }
109
110
  pos = line.lastIndex;
@@ -43,38 +43,40 @@ class Alias extends NodeBase {
43
43
  if (node.anchor === this.source)
44
44
  found = node;
45
45
  }
46
+ if (found && ctx) {
47
+ const { anchors, doc, maxAliasCount } = ctx;
48
+ let data = anchors.get(found);
49
+ if (!data) {
50
+ // Resolve anchors for Node.prototype.toJS()
51
+ toJS(found, null, ctx);
52
+ data = anchors.get(found);
53
+ }
54
+ /* istanbul ignore if */
55
+ if (data?.res === undefined) {
56
+ const msg = 'This should not happen: Alias anchor was not resolved?';
57
+ throw new ReferenceError(msg);
58
+ }
59
+ if (maxAliasCount >= 0) {
60
+ data.count += 1;
61
+ if (data.aliasCount === 0)
62
+ data.aliasCount = getAliasCount(doc, found, anchors);
63
+ if (data.count * data.aliasCount > maxAliasCount) {
64
+ const msg = 'Excessive alias count indicates a resource exhaustion attack';
65
+ throw new ReferenceError(msg);
66
+ }
67
+ }
68
+ }
46
69
  return found;
47
70
  }
48
71
  toJSON(_arg, ctx) {
49
72
  if (!ctx)
50
73
  return { source: this.source };
51
- const { anchors, doc, maxAliasCount } = ctx;
52
- const source = this.resolve(doc, ctx);
74
+ const source = this.resolve(ctx.doc, ctx);
53
75
  if (!source) {
54
76
  const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
55
77
  throw new ReferenceError(msg);
56
78
  }
57
- let data = anchors.get(source);
58
- if (!data) {
59
- // Resolve anchors for Node.prototype.toJS()
60
- toJS(source, null, ctx);
61
- data = anchors.get(source);
62
- }
63
- /* istanbul ignore if */
64
- if (data?.res === undefined) {
65
- const msg = 'This should not happen: Alias anchor was not resolved?';
66
- throw new ReferenceError(msg);
67
- }
68
- if (maxAliasCount >= 0) {
69
- data.count += 1;
70
- if (data.aliasCount === 0)
71
- data.aliasCount = getAliasCount(doc, source, anchors);
72
- if (data.count * data.aliasCount > maxAliasCount) {
73
- const msg = 'Excessive alias count indicates a resource exhaustion attack';
74
- throw new ReferenceError(msg);
75
- }
76
- }
77
- return data.res;
79
+ return ctx.anchors.get(source).res;
78
80
  }
79
81
  toString(ctx, _onComment, _onChompKeep) {
80
82
  const src = `*${this.source}`;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml-vendor",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Vendored ESM build of npm `yaml` for the generated agent preset runtime (zero external dependency).",