dsh-plugin-prompt-tool 0.7.2 → 0.8.1

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 (169) hide show
  1. package/README.md +185 -46
  2. package/engine/THIRD_PARTY_LICENSES +25 -0
  3. package/engine/actions.mjs +931 -0
  4. package/engine/anchor-match.mjs +6 -3
  5. package/engine/character-tools.mjs +11 -0
  6. package/engine/classify-task.mjs +18 -0
  7. package/engine/compaction-epoch.mjs +41 -20
  8. package/engine/compositions/library/agent-instructions.yml +9 -0
  9. package/engine/compositions/library/command-goal.yml +2 -6
  10. package/engine/compositions/library/compaction.yml +2 -32
  11. package/engine/compositions/library/delegation-ptc.yml +58 -0
  12. package/engine/compositions/library/delegation.yml +5 -38
  13. package/engine/compositions/library/persistent-shell.yml +4 -13
  14. package/engine/compositions/library/planning.yml +8 -23
  15. package/engine/compositions/library/skill-filesystem-cordis.yml +10 -0
  16. package/engine/compositions/library/skill-filesystem.yml +2 -8
  17. package/engine/compositions/library/tool-ask-user.yml +2 -3
  18. package/engine/compositions/library/tool-bash.yml +4 -12
  19. package/engine/compositions/library/tool-cordis.yml +7 -0
  20. package/engine/compositions/library/tool-fs-search.yml +2 -11
  21. package/engine/compositions/library/tool-fs.yml +2 -5
  22. package/engine/compositions/library/tool-goal.yml +2 -6
  23. package/engine/compositions/library/tool-jobs.yml +2 -18
  24. package/engine/compositions/library/tool-plugin-manager-disabled.yml +8 -0
  25. package/engine/compositions/library/tool-plugin-manager.yml +8 -0
  26. package/engine/compositions/library/tool-present.yml +7 -0
  27. package/engine/compositions/library/tool-presentation.yml +9 -0
  28. package/engine/compositions/library/tool-pwsh.yml +2 -6
  29. package/engine/compositions/library/tool-skill.yml +7 -0
  30. package/engine/compositions/library/tool-todo.yml +2 -4
  31. package/engine/compositions/library/tool-web.yml +2 -1
  32. package/engine/compositions/source/local/character-tools.yml +5 -0
  33. package/engine/compositions/source/local/declared-triggers.yml +12 -0
  34. package/engine/compositions/source/local/filesystem-editor.yml +30 -0
  35. package/engine/compositions/source/local/instruction-hint.yml +15 -0
  36. package/engine/compositions/{library/official-persistent-shell.yml → source/local/persistent-shell-posix.yml} +10 -8
  37. package/engine/compositions/source/local/prompt-config-engine.yml +3 -9
  38. package/engine/compositions/source/local/run-code-env.yml +4 -7
  39. package/engine/compositions/source/local/session-var-tools.yml +5 -0
  40. package/engine/compositions/source/local/skill-search.yml +3 -9
  41. package/engine/compositions/source/local/subagent-tool-policy.yml +21 -0
  42. package/engine/compositions/source/local/tool-bash-disabled.yml +9 -0
  43. package/engine/compositions/source/local/tool-config-engine.yml +8 -0
  44. package/engine/compositions/source/local/tool-git-bash.yml +15 -0
  45. package/engine/compositions/source/local/world-book-tools.yml +5 -0
  46. package/engine/condition.mjs +83 -0
  47. package/engine/declared-triggers.mjs +93 -0
  48. package/engine/executor.mjs +364 -185
  49. package/engine/fields.mjs +219 -0
  50. package/engine/fillers.mjs +12 -141
  51. package/engine/host-package.mjs +51 -0
  52. package/engine/instruction-hint.mjs +285 -0
  53. package/engine/interpolate.mjs +157 -29
  54. package/engine/layers.mjs +495 -75
  55. package/engine/predicates.mjs +753 -0
  56. package/engine/prompt-config-engine.mjs +63 -8
  57. package/engine/run-code-env.mjs +48 -31
  58. package/engine/schema.mjs +444 -103
  59. package/engine/sdk-strip.mjs +365 -0
  60. package/engine/session-var-tools.mjs +11 -0
  61. package/engine/session-vars.mjs +8 -6
  62. package/engine/shared.mjs +137 -0
  63. package/engine/skill-search.mjs +34 -21
  64. package/engine/st-macros.d.mts +22 -0
  65. package/engine/st-macros.mjs +208 -0
  66. package/engine/st-render.mjs +105 -0
  67. package/engine/st-world-book.d.mts +10 -0
  68. package/engine/st-world-book.mjs +259 -0
  69. package/engine/strategies.mjs +31 -20
  70. package/engine/subagent-tool-policy-core.mjs +400 -0
  71. package/engine/subagent-tool-policy.mjs +327 -0
  72. package/engine/tool-config-engine.mjs +207 -145
  73. package/engine/tool-definition.mjs +143 -0
  74. package/engine/{custom-bash.mjs → tool-git-bash.mjs} +38 -13
  75. package/engine/trigger-editor-meta.mjs +48 -0
  76. package/engine/trigger-spec.mjs +257 -0
  77. package/engine/trigger.mjs +304 -0
  78. package/engine/world-book-tools.mjs +11 -0
  79. package/lib/client.js +16495 -5456
  80. package/lib/client.js.map +1 -1
  81. package/lib/index.d.mts +628 -382
  82. package/lib/index.mjs +5391 -2942
  83. package/lib/paths-CY5psBqE.d.mts +1233 -0
  84. package/lib/preset-package-BnJqrU4R.mjs +16792 -0
  85. package/lib/preset-transfer.d.mts +2 -0
  86. package/lib/preset-transfer.mjs +2 -0
  87. package/package.json +91 -49
  88. package/preset/pt-cordis/preset.yml +43 -0
  89. package/preset/pt-cordis/skills/cordis-composition-reference/SKILL.md +29 -0
  90. package/preset/pt-cordis/skills/cordis-composition-reference/references/packages.md +501 -0
  91. package/preset/pt-cordis/skills/cordis-plugin-development/SKILL.md +38 -0
  92. package/preset/pt-cordis/skills/cordis-plugin-development/references/host-plugin.md +60 -0
  93. package/preset/pt-cordis/skills/cordis-plugin-development/references/mcp-bundle.md +5 -0
  94. package/preset/pt-cordis/skills/cordis-plugin-development/references/ui-plugin.md +13 -0
  95. package/preset/pt-cordis/skills/cordis-plugin-development/references/verification.md +5 -0
  96. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/client.js +22 -0
  97. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/cordis.patch.yml +3 -0
  98. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/index.js +2 -0
  99. package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/package.json +15 -0
  100. package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/cordis.patch.yml +8 -0
  101. package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/package.json +7 -0
  102. package/preset/pt-cordis/skills/editing-cordis-compositions/SKILL.md +84 -0
  103. package/preset/pt-custom/preset.yml +15 -0
  104. package/preset/pt-minimal/preset.yml +27 -0
  105. package/preset/pt-ptc/preset.yml +45 -0
  106. package/preset/pt-standard/preset.yml +43 -0
  107. package/templates/14-first-turn-anchor.yml +4 -4
  108. package/templates/15-guide-auto.yml +3 -3
  109. package/templates/16-custom-fallback.yml +1 -0
  110. package/templates/18-placeholder-env-facts.yml +3 -3
  111. package/templates/20-system-section.yml +3 -2
  112. package/templates/65-turn-stop.yml +18 -0
  113. package/templates/66-subagent-start.yml +14 -0
  114. package/templates/75-subagent-end.yml +15 -0
  115. package/templates/tools/10-shell-command.yml +1 -1
  116. package/templates/tools/40-delegate-character.yml +1 -1
  117. package/templates/tools/50-delegate-worldbook.yml +1 -1
  118. package/templates/tools/60-delegate-session-var.yml +1 -1
  119. package/templates/tools/70-delegate-worldbook-upsert.yml +1 -1
  120. package/engine/anchor-turn.mjs +0 -68
  121. package/engine/code-presentation.mjs +0 -103
  122. package/engine/compositions/library/anchor-turn.yml +0 -10
  123. package/engine/compositions/library/bootstrap-filesystem.yml +0 -19
  124. package/engine/compositions/library/code-presentation.yml +0 -8
  125. package/engine/compositions/library/context-gate.yml +0 -39
  126. package/engine/compositions/library/cot-drip.yml +0 -11
  127. package/engine/compositions/library/custom-bash.yml +0 -14
  128. package/engine/compositions/library/deliberation-gate.yml +0 -11
  129. package/engine/compositions/library/official-agent-instructions.yml +0 -17
  130. package/engine/compositions/library/official-skill-filesystem-cordis.yml +0 -10
  131. package/engine/compositions/library/official-tool-bash.yml +0 -6
  132. package/engine/compositions/library/official-tool-cordis.yml +0 -13
  133. package/engine/compositions/library/official-tool-presentation.yml +0 -7
  134. package/engine/compositions/library/official-tool-skill.yml +0 -14
  135. package/engine/compositions/library/persona.yml +0 -13
  136. package/engine/compositions/library/prompt-config-engine.yml +0 -16
  137. package/engine/compositions/library/run-code-env.yml +0 -15
  138. package/engine/compositions/library/skill-search.yml +0 -14
  139. package/engine/compositions/library/str-replace-editor.yml +0 -10
  140. package/engine/compositions/library/tool-bootstrap.yml +0 -14
  141. package/engine/compositions/library/tool-config-engine.yml +0 -9
  142. package/engine/compositions/library/tool-filter.yml +0 -8
  143. package/engine/compositions/source/local/code-presentation.yml +0 -8
  144. package/engine/compositions/source/local/context-gate.yml +0 -36
  145. package/engine/compositions/source/local/custom-bash.yml +0 -11
  146. package/engine/compositions/source/local/str-replace-editor.yml +0 -10
  147. package/engine/compositions/source/local/tool-bootstrap.yml +0 -11
  148. package/engine/compositions/source/local/tool-filter.yml +0 -8
  149. package/engine/context-gate.mjs +0 -305
  150. package/engine/cot-drip.mjs +0 -137
  151. package/engine/deliberation-gate.mjs +0 -127
  152. package/engine/tool-bootstrap.mjs +0 -476
  153. package/engine/tool-filter.mjs +0 -80
  154. package/lib/preset-core.d.mts +0 -10
  155. package/lib/preset-core.mjs +0 -77
  156. package/lib/prompt-configs-BnQKRHVb.mjs +0 -884
  157. package/lib/prompt-configs-CN0sJTUL.d.mts +0 -193
  158. package/preset/anchored/preset.yml +0 -296
  159. package/preset/creative/preset.yml +0 -67
  160. package/preset/creative/skills/cordis-plugin-development/SKILL.md +0 -420
  161. package/preset/creative/skills/editing-cordis-compositions/SKILL.md +0 -165
  162. package/preset/custom/preset.yml +0 -12
  163. package/preset/liangshen/preset.yml +0 -75
  164. package/preset/minimal/preset.yml +0 -45
  165. package/preset/ptc/preset.yml +0 -58
  166. package/preset/standard/preset.yml +0 -57
  167. package/skills/manifest.json +0 -7
  168. package/skills/sandboxmod/SKILL.md +0 -49
  169. package/skills/web ui/SKILL.md +0 -42
@@ -1,476 +0,0 @@
1
- /**
2
- * Anchored tool bootstrap — keep the FIRST model request on the Minimal
3
- * preset's REAL tool schema (persistent `bash` + `str_replace_editor`), then
4
- * keep the assembled catalog once the session has produced its first durable
5
- * promotion signal. Code Mode (PTC) wire presentation lives in the companion
6
- * `code-presentation` plugin (mounted as a separate row), not here.
7
- * Injected-context control lives in the companion `context-gate` plugin.
8
- *
9
- * GATE MODE (liangshen stabilize 扩展, source: xiaobright/dsh-anchored-standard
10
- * MIT + phase-1 quarantine): `promoteGate: true` gates the promotion on the
11
- * first reasoning block classifying minimal-like (`we` present, no `let me`),
12
- * `maxPromoteSteps` (default 4) is the gate fallback, and
13
- * `promoteAfterFirstResponse: true` promotes a tool-less first response and
14
- * releases an anchor-gated session at its first `turn/end`.
15
- * `personaSectionsOnly: true` narrows phase-1 prompt sections to the persona
16
- * (plan-mode policy and other sections return after promotion);
17
- * `workspaceLine: true` appends the session's working directory to the
18
- * promoted persona; `phase1FirstCallInstruction` is an opt-in extra line
19
- * appended to the phase-1 persona (test builds, issue #274) — unset keeps
20
- * the exact one-line Minimal anchor.
21
- *
22
- * STAGES MODE (渐进披露, 参考 dsh-router-standard progressive disclosure 自写,
23
- * MIT): `stages: [{ name, tools }]` 声明时激活多级阶段窄化——目录 = 当前阶段
24
- * 工具 + 预放(stagePreUnlock 档,默认 1);`stageAdvanceTool`(默认
25
- * phase_advance)推进阶段;调用更高阶段工具 = 直达(自动跳到其档);阶段
26
- * 状态由 durable tool/call 事件推导(resume/reload 自动恢复,无文件),
27
- * compaction 不重置(阶段是会话级进度)。阶段文案经 `stageSectionTemplate`
28
- * 参数化(引擎只提供动态状态,不写死引导文本)。stages 与 promoteOn 门控
29
- * 互不影响:阶段窄化从首轮开始,晋升/compaction 仍由 context-gate 管注入。
30
- *
31
- * The phase is derived from durable session events, so resume and reload
32
- * preserve it. By default (`promoteOn: 'either'`) a session promotes after the
33
- * first `tool/call` OR the first `assistant/message`, whichever comes first:
34
- * request #1 always sees the bootstrap catalog and later requests keep the
35
- * assembled catalog. The original `'tool-call'` mode is kept for compatibility,
36
- * but it can trap a session in bootstrap forever when the first model reply
37
- * makes no tool call — the `'either'` default removes that trap while keeping
38
- * the first-request anchor intact.
39
- *
40
- * First-request conditions established by the reproduction work (issues #6
41
- * and #11, 2026-08-15):
42
- *
43
- * 1. Tool schema. The API-visible first-request catalog decides whether the
44
- * session anchors on the Minimal trajectory. At the adapter-default
45
- * maxTokens (256000 on the official endpoint) the Minimal tool pair —
46
- * persistent `bash` + `str_replace_editor` — anchored 5/5 runs with zero
47
- * `let me` first-lines, while every standard-family schema (pwsh/read,
48
- * pwsh only, sandboxed bash/read) fell into standard-like behavior
49
- * (11/11). Bootstrap therefore exposes exactly the Minimal pair, not
50
- * Standard's `pwsh`/`read`.
51
- *
52
- * 2. Output budget. On the official endpoint the first request's `max_tokens`
53
- * also dominated the trajectory anchor at 1024 (`We need` style in 26/32
54
- * runs against 0/5 at 256000, independent of tool descriptions). The
55
- * Minimal tool schema, however, anchors at 256000 WITHOUT any cap, and the
56
- * cap's delivery depends on the profile package's `prepareCall` behavior
57
- * (it reaches the request on the 0.1.0-rc.5 source checkout; a prebuilt
58
- * rc.6-reporting profile package observed in issue #11 overwrote it with
59
- * `adapterDefaults.maxTokens`). `bootstrapMaxTokens` is therefore OPT-IN:
60
- * leave it unset to run the Minimal schema at the adapter default, or set
61
- * it to cap the first request. When set, the cap is stripped after
62
- * promotion — the next request's seed proposal carries the previous
63
- * header's maxTokens forward, so the release must be explicit.
64
- *
65
- * 3. Injected context is NOT this plugin's concern: the companion
66
- * `context-gate` plugin (shared/context-gate.mjs, mounted as the FIRST
67
- * row) owns the unified injection control — runtime-context suppression
68
- * on the assembly path and a claimed-baseline deny on the pre-step
69
- * waterfall, both keyed to the same epoch-aware promotion phase. Mount it
70
- * separately for context control alone; this file narrows only the tool
71
- * catalog (plus the optional output cap below).
72
- *
73
- * SUBAGENTS: `includeSubagents` (default false) controls whether subagents
74
- * (delegationDepth > 0) follow the same bootstrap phase as the main session —
75
- * false (default) keeps the assembled catalog from their very first request
76
- * (the historical prompt-tool default: delegated agents inherit the full
77
- * tool surface); true makes their first request see the bootstrap pair and
78
- * their own first reply or tool call promotes them. Keep in sync with the
79
- * context-gate row's flag when both rows are present.
80
- *
81
- * POST-PROMOTION CATALOG (prompt-tool patch): after promotion both modes
82
- * keep the assembled catalog (no narrowing). The controlled phase below
83
- * still narrows the catalog before promotion and after compaction.
84
- * COMPACTION (local addition): a compaction rewrites the whole surface, so the
85
- * first post-compaction request is a "second first request". Promotion is
86
- * epoch-aware (see compaction-epoch.mjs): after `compaction/end` the session
87
- * falls back to the controlled phase — the bootstrap pair plus
88
- * `compactionTools` (a core work set, default none) — until a NEW durable
89
- * promotion signal exists past that boundary. The model is mid-task and needs
90
- * to keep working, but still faces a small catalog instead of the full
91
- * Standard set.
92
- *
93
- * Robustness:
94
- * - Promotion decisions are memoized per session id for this process; the
95
- * durable event scan runs once per session per process, then O(1).
96
- * - Subagents keep the assembled catalog unless `includeSubagents: true`.
97
- * - A missing bootstrap tool degrades to the full catalog with a one-time
98
- * warning instead of throwing, so a composition drift can never brick
99
- * every request of a session.
100
- * - Invalid config (bad tool lists, unknown `promoteOn`, malformed flags,
101
- * non-positive `bootstrapMaxTokens`) fails at apply time, i.e. at preset
102
- * mount, where it is visible and fixable.
103
- */
104
-
105
- import { createEpochPromotion } from './compaction-epoch.mjs'
106
- import { booleanOption, createWarnOnce, parsePromoteOn, validateConfig } from './shared.mjs'
107
-
108
- /** Cordis plugin name used by loader diagnostics. */
109
- export const name = 'anchored-tool-bootstrap'
110
-
111
- /**
112
- * Deliberately NO inject list: the listeners only touch services at event
113
- * time. Keep this row right AFTER the context-gate row in agent.cordis.yml:
114
- * waterfall after-next transforms apply in reverse registration order, so the
115
- * tool filter here must register before any plugin that touches the same
116
- * assembly. The optional budget listener registers with `prepend: true` so a
117
- * later listener can never override the first-round cap after we set it.
118
- */
119
- export const inject = []
120
-
121
- /** Every config key this plugin accepts — anything else is a typo. */
122
- const ALLOWED_KEYS = new Set([
123
- 'bootstrapTools', 'promoteOn', 'bootstrapMaxTokens', 'compactionTools',
124
- 'includeSubagents',
125
- 'promoteGate', 'maxPromoteSteps', 'promoteAfterFirstResponse',
126
- 'personaSectionsOnly', 'workspaceLine', 'phase1FirstCallInstruction',
127
- 'stages', 'stagePreUnlock', 'stageAdvanceTool', 'stageAdvanceDescription', 'stageSectionTemplate',
128
- ])
129
-
130
- /** 预设 persona section 名(官方注册名 + 旧名)。 */
131
- const PERSONA_SECTION_NAMES = new Set(['deployment:persona', 'persona'])
132
-
133
- /** 晋升后 persona 附加的工作目录行前缀。 */
134
- const WORKSPACE_LINE_PREFIX = '\n\nYour working directory is '
135
-
136
-
137
- /** Non-empty string list config validator. */
138
- function stringList(value, field, allowEmpty = false) {
139
- if (!Array.isArray(value) || (value.length === 0 && !allowEmpty) || value.some((item) => typeof item !== 'string' || item.length === 0)) {
140
- throw new TypeError(`${name}: ${field} must be a non-empty array of non-empty strings`)
141
- }
142
- return [...new Set(value)]
143
- }
144
-
145
- function stringListOrEmpty(value, field) {
146
- if (value === undefined) return []
147
- return stringList(value, field)
148
- }
149
-
150
- /** 阶段定义校验:非空数组,每项 { name: string, tools: 非空字符串数组 }。 */
151
- function stageDefs(value, field) {
152
- if (value === undefined) return undefined
153
- if (!Array.isArray(value) || value.length === 0) {
154
- throw new TypeError(`${name}: ${field} must be a non-empty array of { name, tools }`)
155
- }
156
- return value.map((stage, index) => {
157
- const label = `${field}[${index}]`
158
- if (stage === null || typeof stage !== 'object' || Array.isArray(stage)) {
159
- throw new TypeError(`${name}: ${label} must be an object`)
160
- }
161
- if (typeof stage.name !== 'string' || stage.name.length === 0) {
162
- throw new TypeError(`${name}: ${label}.name must be a non-empty string`)
163
- }
164
- const tools = stringList(stage.tools, `${label}.tools`)
165
- return { name: stage.name, tools }
166
- })
167
- }
168
-
169
-
170
- /**
171
- * Validate the optional first-request output cap. `undefined` means NO cap:
172
- * the Minimal tool schema anchors at the adapter-default maxTokens, and the
173
- * cap's delivery is profile-package dependent (see the header note), so it is
174
- * opt-in rather than the default.
175
- */
176
- function optionalPositiveInt(value, field) {
177
- if (value === undefined) return undefined
178
- if (!Number.isSafeInteger(value) || value <= 0) {
179
- throw new TypeError(`${name}: ${field} must be a positive safe integer`)
180
- }
181
- return value
182
- }
183
-
184
- /** Register the per-session bootstrap filters. */
185
- export function apply(ctx, config) {
186
- const source = validateConfig(name, config, ALLOWED_KEYS)
187
- // 门控模式(promoteGate / promoteAfterFirstResponse)固定 either 晋升语义,
188
- // promoteOn 显式非 either 时与门控互斥,fail loud 而非静默忽略。
189
- if ((source.promoteGate === true || source.promoteAfterFirstResponse === true)
190
- && source.promoteOn !== undefined && source.promoteOn !== 'either') {
191
- throw new TypeError(`${name}: promoteGate/promoteAfterFirstResponse 门控模式固定 either 晋升语义,promoteOn 必须省略或为 "either"`)
192
- }
193
- // bootstrapTools 允许空数组 = 零工具模式(上游 zero-tool-bootstrap 等价:
194
- // 首请求 tools: [] 产生最深 "we" 轨迹,assistant/message 后晋升)。
195
- const bootstrapTools = stringList(source.bootstrapTools, 'bootstrapTools', true)
196
- const promoteEvents = parsePromoteOn(name, source.promoteOn)
197
- const bootstrapMaxTokens = optionalPositiveInt(source.bootstrapMaxTokens, 'bootstrapMaxTokens')
198
- const includeSubagents = booleanOption(name, source.includeSubagents, 'includeSubagents', false)
199
- const personaSectionsOnly = booleanOption(name, source.personaSectionsOnly, 'personaSectionsOnly', false)
200
- const workspaceLine = booleanOption(name, source.workspaceLine, 'workspaceLine', false)
201
- const phase1FirstCallInstruction = typeof source.phase1FirstCallInstruction === 'string'
202
- ? source.phase1FirstCallInstruction
203
- : ''
204
- // 渐进披露(stages 模式):声明时激活多级阶段窄化,替代"两相"窄化语义。
205
- const stages = stageDefs(source.stages, 'stages')
206
- const stagePreUnlock = source.stagePreUnlock === undefined
207
- ? 1
208
- : Number.isSafeInteger(source.stagePreUnlock) && source.stagePreUnlock >= 0
209
- ? source.stagePreUnlock
210
- : (() => { throw new TypeError(`${name}: stagePreUnlock must be an integer >= 0`) })()
211
- const stageAdvanceTool = typeof source.stageAdvanceTool === 'string' && source.stageAdvanceTool.length > 0
212
- ? source.stageAdvanceTool
213
- : 'phase_advance'
214
- const stageAdvanceDescription = typeof source.stageAdvanceDescription === 'string' && source.stageAdvanceDescription.length > 0
215
- ? source.stageAdvanceDescription
216
- : 'Declare the current stage complete and advance to the next stage (unlocks more tools). Call only when the current stage is genuinely done. Pre-unlocked tools are directly callable — using one jumps straight to its stage.'
217
- const stageSectionTemplate = source.stageSectionTemplate === undefined
218
- ? 'Stage {{stageName}} ({{stage}}/{{total}}). Unlocked tools: {{unlocked}}. Advance via {{advanceTool}} when the current stage is done.'
219
- : (typeof source.stageSectionTemplate === 'string' ? source.stageSectionTemplate : '')
220
- // 工具名 → 阶段索引(直达语义查询表)。
221
- const toolStage = new Map()
222
- if (stages !== undefined) {
223
- stages.forEach((stage, index) => {
224
- for (const tool of stage.tools) toolStage.set(tool, index)
225
- })
226
- }
227
- /** sessionId -> 当前阶段(durable 推导;compaction 不重置)。 */
228
- const stageBySession = new Map()
229
- /** 从 tool/call 事件提取工具名(兼容 source.tool / toolName / name 形状)。 */
230
- const toolNameOf = (event) => {
231
- const data = event?.data
232
- if (data === null || typeof data !== 'object') return ''
233
- const message = data.message !== null && typeof data.message === 'object' ? data.message : data
234
- if (message === null || typeof message !== 'object') return ''
235
- if (typeof message?.source?.tool === 'string') return message.source.tool
236
- if (typeof message?.toolName === 'string') return message.toolName
237
- if (typeof message?.name === 'string') return message.name
238
- return ''
239
- }
240
- const advanceStage = (sessionId, toolName) => {
241
- if (stages === undefined) return
242
- const current = stageBySession.get(sessionId) ?? 0
243
- if (toolName === stageAdvanceTool) {
244
- stageBySession.set(sessionId, Math.min(current + 1, stages.length - 1))
245
- return
246
- }
247
- const owned = toolStage.get(toolName)
248
- if (owned !== undefined && owned > current) stageBySession.set(sessionId, owned)
249
- }
250
- /** 冷启动:从 durable log 重建阶段(resume/reload 同相位)。 */
251
- const scanStage = (session) => {
252
- let stage = 0
253
- for (const event of session.events) {
254
- if (event?.type !== 'tool/call') continue
255
- const toolName = toolNameOf(event)
256
- if (toolName === stageAdvanceTool) stage = Math.min(stage + 1, stages.length - 1)
257
- else {
258
- const owned = toolStage.get(toolName)
259
- if (owned !== undefined && owned > stage) stage = owned
260
- }
261
- }
262
- stageBySession.set(session.id, stage)
263
- return stage
264
- }
265
- const currentStage = (session) => {
266
- if (stages === undefined) return 0
267
- if (session === undefined) return 0
268
- const entry = stageBySession.get(session.id)
269
- return entry === undefined ? scanStage(session) : entry
270
- }
271
- // Core work set exposed after a compaction, before re-promotion. Empty
272
- // means "no compaction recovery catalog": the session stays on the
273
- // bootstrap pair until a new promotion signal.
274
- const compactionTools = stringListOrEmpty(source.compactionTools, 'compactionTools')
275
-
276
- const promotion = createEpochPromotion(promoteEvents, {
277
- includeSubagents,
278
- promoteGate: source.promoteGate === true,
279
- promoteAfterFirstResponse: source.promoteAfterFirstResponse === true,
280
- maxPromoteSteps: source.maxPromoteSteps,
281
- })
282
-
283
- ctx.on('session/event', (session, event) => promotion.observe(session, event))
284
- // 渐进披露:阶段推进/直达(stages 模式;durable 推导,compaction 不重置)。
285
- if (stages !== undefined) {
286
- ctx.on('session/event', (session, event) => {
287
- if (event?.type !== 'tool/call') return
288
- advanceStage(session.id, toolNameOf(event))
289
- })
290
- }
291
-
292
- const warnOnce = createWarnOnce(ctx, name)
293
-
294
- /** 晋升后给 persona 追加工作目录行(无 persona/无 cwd/已含则原样返回)。 */
295
- const withWorkspaceLine = (assembly, agent) => {
296
- const cwd = agent?.session?.header?.cwd
297
- if (typeof cwd !== 'string' || cwd.length === 0) return assembly
298
- if (!Array.isArray(assembly.sections)) return assembly
299
- const line = `${WORKSPACE_LINE_PREFIX}${cwd}.`
300
- const persona = assembly.sections.find((section) =>
301
- PERSONA_SECTION_NAMES.has(section?.name)
302
- && typeof section?.text === 'string'
303
- && !section.text.includes(line))
304
- if (persona === undefined) return assembly
305
- return {
306
- ...assembly,
307
- sections: assembly.sections.map((section) => section === persona
308
- ? { ...section, text: `${persona.text}${line}` }
309
- : section),
310
- }
311
- }
312
-
313
- /** Narrow the assembled catalog to a keep-set; validate required names. */
314
- const keepTools = (assembled, keep, missingAllowsFullCatalog) => {
315
- const available = new Set(assembled.tools.map((tool) => tool.name))
316
- const missing = [...keep].filter((toolName) => !available.has(toolName))
317
- if (missing.length > 0) {
318
- warnOnce(
319
- `${name}: expected every phase tool; missing=${JSON.stringify(missing)} — `
320
- + (missingAllowsFullCatalog ? 'bootstrap disabled, full catalog exposed' : 'continuing with what is available'),
321
- )
322
- if (missingAllowsFullCatalog) return assembled
323
- }
324
- return {
325
- ...assembled,
326
- tools: assembled.tools.filter((tool) => keep.has(tool.name)),
327
- }
328
- }
329
-
330
- ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
331
- // Downstream errors propagate untouched; only this filter's own logic is guarded.
332
- const assembled = await next()
333
- try {
334
- const agent = context.agent
335
- if (agent === undefined) return assembled
336
- if (agent.session === undefined) return assembled
337
- if ((agent.session?.header?.delegationDepth ?? 0) > 0 && !includeSubagents) {
338
- // 默认:子代理继承完整目录(历史 prompt-tool 默认);
339
- // includeSubagents=true 时落到下方正常相位逻辑(首轮裁剪 + 晋升)。
340
- return assembled
341
- }
342
- if (stages !== undefined) {
343
- // 渐进披露:目录 = 当前阶段 + 预放档工具;阶段 section 按模板注入(文案参数化)。
344
- const stage = currentStage(agent.session)
345
- const keep = new Set()
346
- const upper = Math.min(stage + stagePreUnlock, stages.length - 1)
347
- for (let i = 0; i <= upper; i += 1) {
348
- for (const toolName of stages[i].tools) keep.add(toolName)
349
- }
350
- let next = keepTools(assembled, keep, true)
351
- if (stageSectionTemplate.length > 0) {
352
- const unlocked = stages
353
- .slice(0, upper + 1)
354
- .flatMap((s) => s.tools)
355
- .join(', ')
356
- const text = stageSectionTemplate
357
- .replaceAll('{{stage}}', String(stage + 1))
358
- .replaceAll('{{stageName}}', stages[stage].name)
359
- .replaceAll('{{unlocked}}', unlocked)
360
- .replaceAll('{{total}}', String(stages.length))
361
- .replaceAll('{{advanceTool}}', stageAdvanceTool)
362
- next = {
363
- ...next,
364
- sections: Array.isArray(next.sections)
365
- ? [...next.sections, { name: 'stage-status', text }]
366
- : [{ name: 'stage-status', text }],
367
- }
368
- }
369
- return next
370
- }
371
- const status = promotion.status(agent)
372
- if (status.promoted) {
373
- // prompt-tool patch: keep the assembled catalog after promotion
374
- // (Code Mode presentation is the companion code-presentation row's job).
375
- return workspaceLine ? withWorkspaceLine(assembled, agent) : assembled
376
- }
377
- // Controlled phase: the bootstrap pair; after a compaction, plus the
378
- // compaction work set so mid-task work can continue. Context control is
379
- // NOT here: the companion `context-gate` plugin owns it (see the header
380
- // note), so this filter touches only the tool catalog.
381
- const { boundary } = status
382
- const keep = new Set(bootstrapTools)
383
- if (boundary >= 0) {
384
- for (const toolName of compactionTools) keep.add(toolName)
385
- // 零工具模式:compaction 回退补 shell(对齐上游 zero-tool-bootstrap),
386
- // 否则 mid-task 模型无 shell 可用。
387
- if (bootstrapTools.length === 0) {
388
- const available = new Set(assembled.tools.map((tool) => tool.name))
389
- for (const toolName of ['bash', 'pwsh']) if (available.has(toolName)) keep.add(toolName)
390
- }
391
- }
392
- let next = keepTools(assembled, keep, true)
393
- if (personaSectionsOnly) {
394
- // Phase-1 提示词段只留 persona(plan-mode 策略等晋升后才恢复)。
395
- const sections = Array.isArray(next.sections)
396
- ? next.sections.filter((section) => PERSONA_SECTION_NAMES.has(section?.name))
397
- : undefined
398
- if (sections !== undefined) {
399
- const phase1Sections = phase1FirstCallInstruction === ''
400
- ? sections
401
- : sections.map((section) => {
402
- if (typeof section?.text !== 'string' || section.text.includes(phase1FirstCallInstruction)) return section
403
- return { ...section, text: `${section.text}${phase1FirstCallInstruction}` }
404
- })
405
- next = { ...next, sections: phase1Sections }
406
- }
407
- }
408
- return next
409
- } catch (error) {
410
- // A filter bug must never brick a session: degrade to the full catalog.
411
- warnOnce(`${name}: bootstrap filter failed, exposing the full catalog: ${String((error && error.message) || error)}`)
412
- return assembled
413
- }
414
- })
415
-
416
- // Optionally cap the first model request's output budget while bootstrapping.
417
- // Unset (`bootstrapMaxTokens` omitted) means the adapter default flows — the
418
- // Minimal tool schema anchors at 256000 without a cap (issue #11).
419
- if (bootstrapMaxTokens !== undefined) {
420
- // Same registration discipline as the pre-step strip below: `prepend`
421
- // keeps this listener the OUTERMOST transform of the agent/request
422
- // waterfall for the same registration-order reasons (loader row
423
- // application is concurrent; row order alone does not decide listener
424
- // order — see issue #6 and upstream PR #13), so a later listener can
425
- // never override the first-round budget after we set it.
426
- ctx.on('agent/request', async (payload, next) => {
427
- const resolved = await next()
428
- const agent = payload.agent
429
- if (promotion.status(agent).promoted) {
430
- // The next request's seed proposal carries the previous header's
431
- // maxTokens forward, so the injected cap must be stripped explicitly —
432
- // otherwise it would persist for the whole session.
433
- if (resolved.maxTokens === bootstrapMaxTokens) {
434
- const { maxTokens: _bootstrap, ...rest } = resolved
435
- return rest
436
- }
437
- return resolved
438
- }
439
- return {
440
- ...resolved,
441
- maxTokens: bootstrapMaxTokens,
442
- }
443
- }, { prepend: true })
444
- }
445
-
446
- // 渐进披露:阶段推进工具(stages 模式)。推进状态由事件流推导(observe),
447
- // 本工具只返回推进结果文案——绝不手改 state(durable 单一真相)。
448
- if (stages !== undefined) {
449
- ctx.tools.register({
450
- name: stageAdvanceTool,
451
- description: stageAdvanceDescription,
452
- parameters: { type: 'object', properties: {}, additionalProperties: false },
453
- output: {
454
- schema: {
455
- type: 'object',
456
- additionalProperties: false,
457
- properties: {
458
- text: { type: 'string' },
459
- },
460
- required: ['text'],
461
- },
462
- render: (_args, value) => [{ type: 'text', text: value.text }],
463
- },
464
- async execute(_args, exec) {
465
- const session = exec?.agent?.session
466
- if (session === undefined) return { text: 'phase_advance: no session context' }
467
- const stage = currentStage(session)
468
- const next = Math.min(stage + 1, stages.length - 1)
469
- const text = stage >= stages.length - 1
470
- ? `Already at the final stage (${stages[stage].name}) — all tools unlocked.`
471
- : `Advanced to stage ${next + 1}/${stages.length} (${stages[next].name}). Unlocked: ${stages[next].tools.join(', ')}.`
472
- return { text }
473
- },
474
- })
475
- }
476
- }
@@ -1,80 +0,0 @@
1
- /**
2
- * Main-session tool filter — apply a persistent allow/deny mask to the
3
- * assembled tool catalog, covering EVERY registered tool (including tools
4
- * contributed by arbitrary third-party plugins).
5
- *
6
- * Unlike `tool-bootstrap` (phase-1 bootstrap narrowing that restores the full
7
- * catalog after promotion), this filter is CONSTANT: it runs on every
8
- * `system-prompt/assemble` and keeps the mask applied before and after
9
- * promotion, so main-session tool restrictions never disappear.
10
- *
11
- * Config:
12
- * - `allow`: non-empty keep-set. When present, ONLY tools whose name is in
13
- * this list survive (white-list); `deny` is then subtracted from it.
14
- * - `deny`: non-empty remove-set. Tools whose name is in this list are
15
- * dropped regardless of `allow`.
16
- * - `includeSubagents`: true = the same mask applies to delegated
17
- * sub-agents; false (default) = main session only, sub-agents keep the
18
- * assembled catalog (they already have their own toolFilter{allow,deny}
19
- * in the tool-subagent row).
20
- *
21
- * Both lists empty = no filtering (official default), zero overhead.
22
- * Failure degrades to the full catalog — a filter bug must never brick a
23
- * session (same discipline as tool-bootstrap).
24
- */
25
-
26
- import { booleanOption, createWarnOnce, validateConfig } from './shared.mjs'
27
-
28
- /** Cordis plugin name used by loader diagnostics. */
29
- export const name = 'anchored-tool-filter'
30
-
31
- export const inject = []
32
-
33
- /** Every config key this plugin accepts — anything else is a typo. */
34
- const ALLOWED_KEYS = new Set(['allow', 'deny', 'includeSubagents', 'enabled'])
35
-
36
- /** Non-empty string list validator (returns a Set). */
37
- function nameSet(value, field) {
38
- if (value === undefined) return undefined
39
- if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || item.length === 0)) {
40
- throw new TypeError(`${name}: ${field} must be an array of non-empty strings`)
41
- }
42
- return new Set(value)
43
- }
44
-
45
- /** Apply the mask to one assembled catalog. */
46
- function applyMask(assembled, allow, deny) {
47
- if (allow === undefined && deny === undefined) return assembled
48
- const tools = (Array.isArray(assembled.tools) ? assembled.tools : [])
49
- .filter((tool) => {
50
- const toolName = typeof tool?.name === 'string' ? tool.name : ''
51
- if (toolName.length === 0) return true
52
- if (deny !== undefined && deny.has(toolName)) return false
53
- if (allow !== undefined && !allow.has(toolName)) return false
54
- return true
55
- })
56
- return { ...assembled, tools }
57
- }
58
-
59
- /** Register the per-assembly main-session tool mask. */
60
- export function apply(ctx, config) {
61
- const source = validateConfig(name, config, ALLOWED_KEYS)
62
- if (source.enabled === false) return
63
- const allow = nameSet(source.allow, 'allow')
64
- const deny = nameSet(source.deny, 'deny')
65
- const includeSubagents = booleanOption(name, source.includeSubagents, 'includeSubagents', false)
66
- const warnOnce = createWarnOnce(ctx, name)
67
-
68
- ctx.on('system-prompt/assemble', async (assembly, context, next) => {
69
- const assembled = await next()
70
- try {
71
- const agent = context?.agent
72
- const depth = agent?.session?.header?.delegationDepth ?? 0
73
- if (depth > 0 && !includeSubagents) return assembled
74
- return applyMask(assembled, allow, deny)
75
- } catch (error) {
76
- warnOnce(`${name}: filter failed, exposing the full catalog: ${String((error && error.message) || error)}`)
77
- return assembled
78
- }
79
- })
80
- }
@@ -1,10 +0,0 @@
1
- import { a as mergePromptConfigs, i as loadPromptConfigFiles, n as PromptConfigFile, o as renderPromptConfigYaml, r as PromptConfigSpec, t as BuildCordisOptions } from "./prompt-configs-CN0sJTUL.mjs";
2
- //#region src/preset-core.d.ts
3
- /**
4
- * 渲染 anchored 组合文件:
5
- * agent.cordis.yml 是模板自带完整组合(含 prompt-config-engine 行),
6
- * 动态值全部由 preset.yml 的 variables 声明为 __TOKEN__;本函数只做变量替换 + YAML 校验。
7
- */
8
- declare function buildCordis(prompt: string, options?: BuildCordisOptions): string;
9
- //#endregion
10
- export { type BuildCordisOptions, type PromptConfigFile, type PromptConfigSpec, buildCordis, loadPromptConfigFiles, mergePromptConfigs, renderPromptConfigYaml };
@@ -1,77 +0,0 @@
1
- import { h as loadPresetSpec, n as mergePromptConfigs, r as renderPromptConfigYaml, s as assertCompositionArray, t as loadPromptConfigFiles, x as renderComposition } from "./prompt-configs-BnQKRHVb.mjs";
2
- import { fileURLToPath } from "node:url";
3
- //#region src/preset-core.ts
4
- /**
5
- * prompt-tool preset 核心(兼容层):
6
- * - buildCordis:按单一参数 preset.yml 渲染 anchored 组合文件(不再做字符串补丁)。
7
- * 新代码请使用 src/host/ 下的 manifest / write-preset / prompt-configs / templates。
8
- */
9
- const ANCHORED_TEMPLATE_DIR = fileURLToPath(new URL("../preset/anchored/", import.meta.url));
10
- /**
11
- * 渲染 anchored 组合文件:
12
- * agent.cordis.yml 是模板自带完整组合(含 prompt-config-engine 行),
13
- * 动态值全部由 preset.yml 的 variables 声明为 __TOKEN__;本函数只做变量替换 + YAML 校验。
14
- */
15
- function buildCordis(prompt, options = {}) {
16
- const spec = loadPresetSpec(ANCHORED_TEMPLATE_DIR);
17
- const runtime = {
18
- promptText: prompt,
19
- firstTurnAnchor: options.firstTurnAnchor === true,
20
- firstTurnCustom: options.firstTurnCustom === true,
21
- firstTurnText: typeof options.firstTurnText === "string" ? options.firstTurnText : "",
22
- guideCustom: options.guideCustom === true,
23
- guideText: typeof options.guideText === "string" ? options.guideText : "",
24
- guideEnabled: typeof options.guideEnabled === "boolean" ? options.guideEnabled : void 0,
25
- injectPrompt: options.injectPrompt !== false,
26
- usePtcMode: typeof options.usePtcMode === "boolean" ? options.usePtcMode : void 0,
27
- bootstrapMaxTokens: Number.isSafeInteger(options.bootstrapMaxTokens) && (options.bootstrapMaxTokens ?? 0) > 0 ? options.bootstrapMaxTokens : 0,
28
- modelProvider: typeof options.modelProvider === "string" && options.modelProvider.length > 0 ? options.modelProvider : "",
29
- modelName: typeof options.modelName === "string" && options.modelName.length > 0 ? options.modelName : "",
30
- subagentModelProvider: typeof options.subagentModelProvider === "string" && options.subagentModelProvider.length > 0 ? options.subagentModelProvider : "",
31
- subagentModelName: typeof options.subagentModelName === "string" && options.subagentModelName.length > 0 ? options.subagentModelName : "",
32
- modelReasoningEffort: typeof options.modelReasoningEffort === "string" ? options.modelReasoningEffort : "",
33
- modelTemperature: typeof options.modelTemperature === "string" ? options.modelTemperature : "",
34
- modelMaxTokens: typeof options.modelMaxTokens === "string" ? options.modelMaxTokens : "",
35
- subagentReasoningEffort: typeof options.subagentReasoningEffort === "string" ? options.subagentReasoningEffort : "",
36
- subagentTemperature: typeof options.subagentTemperature === "string" ? options.subagentTemperature : "",
37
- subagentMaxTokens: typeof options.subagentMaxTokens === "string" ? options.subagentMaxTokens : "",
38
- toolFilterAllow: options.toolFilterAllow,
39
- toolFilterDeny: options.toolFilterDeny,
40
- maxDepth: options.maxDepth,
41
- allowKinds: options.allowKinds,
42
- promoteGate: options.promoteGate,
43
- promoteAfterFirstResponse: options.promoteAfterFirstResponse,
44
- maxPromoteSteps: options.maxPromoteSteps,
45
- bootstrapTools: options.bootstrapTools,
46
- compactionTools: options.compactionTools,
47
- personaSectionsOnly: options.personaSectionsOnly,
48
- workspaceLine: options.workspaceLine,
49
- messageSources: options.messageSources,
50
- deferredSources: options.deferredSources,
51
- deferredGraceSteps: options.deferredGraceSteps,
52
- instructionHint: options.instructionHint,
53
- phase1FirstCallInstruction: options.phase1FirstCallInstruction,
54
- stages: options.stages,
55
- stagePreUnlock: options.stagePreUnlock,
56
- stageAdvanceTool: options.stageAdvanceTool,
57
- stageAdvanceDescription: options.stageAdvanceDescription,
58
- stageSectionTemplate: options.stageSectionTemplate,
59
- toolFilterSubagents: options.toolFilterSubagents,
60
- anchorTurn: options.anchorTurn,
61
- anchorTurnText: options.anchorTurnText,
62
- deliberationGate: options.deliberationGate,
63
- deliberationMinChars: options.deliberationMinChars,
64
- deliberationMaxGatesPerTurn: options.deliberationMaxGatesPerTurn,
65
- cotDrip: options.cotDrip,
66
- cotDripEvery: options.cotDripEvery,
67
- cotDripMaxPerTurn: options.cotDripMaxPerTurn,
68
- firstTurnWord: typeof options.firstTurnWord === "string" && options.firstTurnWord.length > 0 ? options.firstTurnWord : void 0
69
- };
70
- const out = renderComposition(spec, runtime, ANCHORED_TEMPLATE_DIR);
71
- const parsed = assertCompositionArray(out, spec);
72
- if (!new Set(parsed.map((row) => row?.id)).has("prompt-config-engine")) throw new Error("generated agent.cordis.yml is missing the prompt-config-engine row");
73
- if (parsed.find((row) => row?.id === "prompt-config-engine")?.config?.configsDir !== "../prompt-configs") throw new Error("generated agent.cordis.yml prompt-config-engine row must point configsDir at ../prompt-configs");
74
- return out;
75
- }
76
- //#endregion
77
- export { buildCordis, loadPromptConfigFiles, mergePromptConfigs, renderPromptConfigYaml };