dsh-plugin-prompt-tool 0.4.2 → 0.5.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 +125 -222
- package/engine/compaction-epoch.mjs +139 -0
- package/engine/compositions/library/bootstrap-filesystem.yml +19 -0
- package/engine/compositions/library/compaction.yml +52 -0
- package/engine/compositions/library/context-gate.yml +40 -0
- package/engine/compositions/library/custom-bash.yml +14 -0
- package/engine/compositions/library/delegation.yml +85 -0
- package/engine/compositions/library/official-agent-instructions.yml +17 -0
- package/engine/compositions/library/official-persistent-shell.yml +56 -0
- package/engine/compositions/library/official-skill-filesystem-cordis.yml +10 -0
- package/engine/compositions/library/official-tool-bash.yml +6 -0
- package/engine/compositions/library/official-tool-cordis.yml +13 -0
- package/engine/compositions/library/official-tool-presentation.yml +7 -0
- package/engine/compositions/library/official-tool-skill.yml +14 -0
- package/engine/compositions/library/persistent-shell.yml +36 -0
- package/engine/compositions/library/persona.yml +9 -0
- package/engine/compositions/library/planning.yml +41 -0
- package/engine/compositions/library/prompt-config-engine.yml +16 -0
- package/engine/compositions/library/router-first-turn.yml +9 -0
- package/engine/compositions/library/run-code-env.yml +15 -0
- package/engine/compositions/library/skill-filesystem.yml +13 -0
- package/engine/compositions/library/skill-search.yml +14 -0
- package/engine/compositions/library/str-replace-editor.yml +10 -0
- package/engine/compositions/library/tool-ask-user.yml +8 -0
- package/engine/compositions/library/tool-bash.yml +16 -0
- package/engine/compositions/library/tool-bootstrap.yml +16 -0
- package/engine/compositions/library/tool-filter.yml +12 -0
- package/engine/compositions/library/tool-fs-search.yml +18 -0
- package/engine/compositions/library/tool-fs.yml +10 -0
- package/engine/compositions/library/tool-goal.yml +19 -0
- package/engine/compositions/library/tool-jobs.yml +23 -0
- package/engine/compositions/library/tool-pwsh.yml +12 -0
- package/engine/compositions/library/tool-todo.yml +11 -0
- package/engine/compositions/library/tool-web.yml +9 -0
- package/engine/compositions/source/local/context-gate.yml +37 -0
- package/engine/compositions/source/local/custom-bash.yml +11 -0
- package/engine/compositions/source/local/prompt-config-engine.yml +13 -0
- package/engine/compositions/source/local/router-first-turn.yml +5 -0
- package/engine/compositions/source/local/run-code-env.yml +12 -0
- package/engine/compositions/source/local/skill-search.yml +11 -0
- package/engine/compositions/source/local/tool-bootstrap.yml +13 -0
- package/engine/context-gate.mjs +305 -0
- package/{preset → engine}/custom-bash.mjs +243 -243
- package/engine/executor.mjs +271 -0
- package/engine/fillers.mjs +273 -0
- package/engine/layers.mjs +220 -0
- package/engine/prompt-config-engine.mjs +49 -0
- package/{preset → engine}/router-first-turn.mjs +85 -73
- package/engine/run-code-env.mjs +208 -0
- package/engine/schema.mjs +294 -0
- package/{preset → engine}/shared.mjs +54 -0
- package/engine/strategies.mjs +223 -0
- package/{preset → engine}/tool-bootstrap.mjs +350 -282
- package/engine/tool-filter.mjs +80 -0
- package/engine/vendor/yaml/LICENSE +13 -0
- package/engine/vendor/yaml/dist/compose/compose-collection.js +88 -0
- package/engine/vendor/yaml/dist/compose/compose-doc.js +43 -0
- package/engine/vendor/yaml/dist/compose/compose-node.js +109 -0
- package/engine/vendor/yaml/dist/compose/compose-scalar.js +86 -0
- package/engine/vendor/yaml/dist/compose/composer.js +219 -0
- package/engine/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
- package/engine/vendor/yaml/dist/compose/resolve-block-scalar.js +198 -0
- package/engine/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
- package/engine/vendor/yaml/dist/compose/resolve-end.js +37 -0
- package/engine/vendor/yaml/dist/compose/resolve-flow-collection.js +207 -0
- package/engine/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
- package/engine/vendor/yaml/dist/compose/resolve-props.js +146 -0
- package/engine/vendor/yaml/dist/compose/util-contains-newline.js +34 -0
- package/engine/vendor/yaml/dist/compose/util-empty-scalar-position.js +26 -0
- package/engine/vendor/yaml/dist/compose/util-flow-indent-check.js +15 -0
- package/engine/vendor/yaml/dist/compose/util-map-includes.js +13 -0
- package/engine/vendor/yaml/dist/doc/Document.js +335 -0
- package/engine/vendor/yaml/dist/doc/anchors.js +71 -0
- package/engine/vendor/yaml/dist/doc/applyReviver.js +55 -0
- package/engine/vendor/yaml/dist/doc/createNode.js +88 -0
- package/engine/vendor/yaml/dist/doc/directives.js +176 -0
- package/engine/vendor/yaml/dist/errors.js +57 -0
- package/engine/vendor/yaml/dist/index.js +17 -0
- package/engine/vendor/yaml/dist/log.js +11 -0
- package/engine/vendor/yaml/dist/nodes/Alias.js +116 -0
- package/engine/vendor/yaml/dist/nodes/Collection.js +147 -0
- package/engine/vendor/yaml/dist/nodes/Node.js +38 -0
- package/engine/vendor/yaml/dist/nodes/Pair.js +36 -0
- package/engine/vendor/yaml/dist/nodes/Scalar.js +24 -0
- package/engine/vendor/yaml/dist/nodes/YAMLMap.js +144 -0
- package/engine/vendor/yaml/dist/nodes/YAMLSeq.js +113 -0
- package/engine/vendor/yaml/dist/nodes/addPairToJSMap.js +63 -0
- package/engine/vendor/yaml/dist/nodes/identity.js +36 -0
- package/engine/vendor/yaml/dist/nodes/toJS.js +37 -0
- package/engine/vendor/yaml/dist/parse/cst-scalar.js +214 -0
- package/engine/vendor/yaml/dist/parse/cst-stringify.js +61 -0
- package/engine/vendor/yaml/dist/parse/cst-visit.js +97 -0
- package/engine/vendor/yaml/dist/parse/cst.js +98 -0
- package/engine/vendor/yaml/dist/parse/lexer.js +721 -0
- package/engine/vendor/yaml/dist/parse/line-counter.js +39 -0
- package/engine/vendor/yaml/dist/parse/parser.js +975 -0
- package/engine/vendor/yaml/dist/public-api.js +102 -0
- package/engine/vendor/yaml/dist/schema/Schema.js +37 -0
- package/engine/vendor/yaml/dist/schema/common/map.js +17 -0
- package/engine/vendor/yaml/dist/schema/common/null.js +15 -0
- package/engine/vendor/yaml/dist/schema/common/seq.js +17 -0
- package/engine/vendor/yaml/dist/schema/common/string.js +14 -0
- package/engine/vendor/yaml/dist/schema/core/bool.js +19 -0
- package/engine/vendor/yaml/dist/schema/core/float.js +43 -0
- package/engine/vendor/yaml/dist/schema/core/int.js +38 -0
- package/engine/vendor/yaml/dist/schema/core/schema.js +23 -0
- package/engine/vendor/yaml/dist/schema/json/schema.js +62 -0
- package/engine/vendor/yaml/dist/schema/tags.js +96 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/binary.js +58 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/bool.js +26 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/float.js +46 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/int.js +71 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/merge.js +67 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/omap.js +74 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/pairs.js +78 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/set.js +93 -0
- package/engine/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/engine/vendor/yaml/dist/stringify/foldFlowLines.js +146 -0
- package/engine/vendor/yaml/dist/stringify/stringify.js +129 -0
- package/engine/vendor/yaml/dist/stringify/stringifyCollection.js +153 -0
- package/engine/vendor/yaml/dist/stringify/stringifyComment.js +20 -0
- package/engine/vendor/yaml/dist/stringify/stringifyDocument.js +85 -0
- package/engine/vendor/yaml/dist/stringify/stringifyNumber.js +25 -0
- package/engine/vendor/yaml/dist/stringify/stringifyPair.js +150 -0
- package/engine/vendor/yaml/dist/stringify/stringifyString.js +336 -0
- package/engine/vendor/yaml/dist/util.js +11 -0
- package/engine/vendor/yaml/dist/visit.js +233 -0
- package/engine/vendor/yaml/index.js +5 -0
- package/engine/vendor/yaml/package.json +11 -0
- package/lib/client.js +4421 -781
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +504 -51
- package/lib/index.mjs +3871 -731
- package/lib/preset-core.d.mts +7 -37
- package/lib/preset-core.mjs +44 -283
- package/lib/prompt-configs-BcP6miu9.d.mts +102 -0
- package/lib/prompt-configs-k7wuZs0C.mjs +719 -0
- package/package.json +104 -98
- package/preset/anchored/preset.yml +344 -0
- package/preset/creative/preset.yml +62 -0
- package/preset/custom/preset.yml +13 -0
- package/preset/liangshen/preset.yml +66 -0
- package/preset/minimal/preset.yml +41 -0
- package/preset/ptc/preset.yml +50 -0
- package/preset/standard/preset.yml +49 -0
- package/skills/manifest.json +7 -0
- package/skills/sandboxmod/SKILL.md +49 -49
- package/skills/web ui/SKILL.md +42 -0
- package/templates/10-pre-step.yml +28 -0
- package/templates/11-merged-a.yml +11 -0
- package/templates/13-anchor.yml +19 -0
- package/templates/14-first-turn-anchor.yml +27 -0
- package/templates/15-guide-auto.yml +25 -0
- package/templates/16-custom-fallback.yml +21 -0
- package/templates/17-instruction-hint.yml +23 -0
- package/templates/18-placeholder-env-facts.yml +11 -0
- package/templates/19-placeholder-skill-catalog.yml +19 -0
- package/templates/20-system-section.yml +14 -0
- package/templates/30-runtime-context.yml +10 -0
- package/templates/31-runtime-context-placeholder.yml +18 -0
- package/templates/40-agent-request.yml +11 -0
- package/templates/50-llm-stream.yml +9 -0
- package/templates/60-tool-pipeline.yml +12 -0
- package/AGENTS.md +0 -4
- package/plan.md +0 -312
- package/preset/agent.cordis.yml +0 -443
- package/preset/compaction-epoch.mjs +0 -81
- package/preset/context-gate.mjs +0 -165
- package/preset/instruction-hint.mjs +0 -217
- package/preset/near-anchor.mjs +0 -101
- package/preset/preset.yml +0 -3
- package/preset/prompt-injector.mjs +0 -112
- package/preset/router-guide.mjs +0 -79
- package/preset.md +0 -115
- package/upstream/dsh-anchored-standard/LICENSE +0 -22
- package/upstream/dsh-anchored-standard/NOTICE +0 -19
- package/upstream/dsh-anchored-standard/REVISION +0 -1
- package/upstream/dsh-anchored-standard/preset/agent.cordis.yml +0 -440
- package/upstream/dsh-anchored-standard/preset/compaction-epoch.mjs +0 -81
- package/upstream/dsh-anchored-standard/preset/context-gate.mjs +0 -202
- package/upstream/dsh-anchored-standard/preset/custom-bash.mjs +0 -219
- package/upstream/dsh-anchored-standard/preset/dev-tool-search.mjs +0 -131
- package/upstream/dsh-anchored-standard/preset/instruction-hint.mjs +0 -231
- package/upstream/dsh-anchored-standard/preset/preset.yml +0 -3
- package/upstream/dsh-anchored-standard/preset/skill-search.mjs +0 -142
- package/upstream/dsh-anchored-standard/preset/tool-bootstrap.mjs +0 -301
- /package/{preset → engine}/skill-search.mjs +0 -0
|
@@ -1,282 +1,350 @@
|
|
|
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; `usePtcMode` optionally switches the wire presentation to
|
|
6
|
-
* Code Mode (PTC). Injected-context control lives in the companion
|
|
7
|
-
* `context-gate` plugin, not here.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* first
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* `
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
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; `usePtcMode` optionally switches the wire presentation to
|
|
6
|
+
* Code Mode (PTC). Injected-context control lives in the companion
|
|
7
|
+
* `context-gate` plugin, not here.
|
|
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
|
+
* The phase is derived from durable session events, so resume and reload
|
|
23
|
+
* preserve it. By default (`promoteOn: 'either'`) a session promotes after the
|
|
24
|
+
* first `tool/call` OR the first `assistant/message`, whichever comes first:
|
|
25
|
+
* request #1 always sees the bootstrap catalog and later requests keep the
|
|
26
|
+
* assembled catalog. The original `'tool-call'` mode is kept for compatibility,
|
|
27
|
+
* but it can trap a session in bootstrap forever when the first model reply
|
|
28
|
+
* makes no tool call — the `'either'` default removes that trap while keeping
|
|
29
|
+
* the first-request anchor intact.
|
|
30
|
+
*
|
|
31
|
+
* First-request conditions established by the reproduction work (issues #6
|
|
32
|
+
* and #11, 2026-08-15):
|
|
33
|
+
*
|
|
34
|
+
* 1. Tool schema. The API-visible first-request catalog decides whether the
|
|
35
|
+
* session anchors on the Minimal trajectory. At the adapter-default
|
|
36
|
+
* maxTokens (256000 on the official endpoint) the Minimal tool pair —
|
|
37
|
+
* persistent `bash` + `str_replace_editor` — anchored 5/5 runs with zero
|
|
38
|
+
* `let me` first-lines, while every standard-family schema (pwsh/read,
|
|
39
|
+
* pwsh only, sandboxed bash/read) fell into standard-like behavior
|
|
40
|
+
* (11/11). Bootstrap therefore exposes exactly the Minimal pair, not
|
|
41
|
+
* Standard's `pwsh`/`read`.
|
|
42
|
+
*
|
|
43
|
+
* 2. Output budget. On the official endpoint the first request's `max_tokens`
|
|
44
|
+
* also dominated the trajectory anchor at 1024 (`We need` style in 26/32
|
|
45
|
+
* runs against 0/5 at 256000, independent of tool descriptions). The
|
|
46
|
+
* Minimal tool schema, however, anchors at 256000 WITHOUT any cap, and the
|
|
47
|
+
* cap's delivery depends on the profile package's `prepareCall` behavior
|
|
48
|
+
* (it reaches the request on the 0.1.0-rc.5 source checkout; a prebuilt
|
|
49
|
+
* rc.6-reporting profile package observed in issue #11 overwrote it with
|
|
50
|
+
* `adapterDefaults.maxTokens`). `bootstrapMaxTokens` is therefore OPT-IN:
|
|
51
|
+
* leave it unset to run the Minimal schema at the adapter default, or set
|
|
52
|
+
* it to cap the first request. When set, the cap is stripped after
|
|
53
|
+
* promotion — the next request's seed proposal carries the previous
|
|
54
|
+
* header's maxTokens forward, so the release must be explicit.
|
|
55
|
+
*
|
|
56
|
+
* 3. Injected context is NOT this plugin's concern: the companion
|
|
57
|
+
* `context-gate` plugin (shared/context-gate.mjs, mounted as the FIRST
|
|
58
|
+
* row) owns the unified injection control — runtime-context suppression
|
|
59
|
+
* on the assembly path and a claimed-baseline deny on the pre-step
|
|
60
|
+
* waterfall, both keyed to the same epoch-aware promotion phase. Mount it
|
|
61
|
+
* separately for context control alone; this file narrows only the tool
|
|
62
|
+
* catalog (plus the optional output cap below).
|
|
63
|
+
*
|
|
64
|
+
* SUBAGENTS: `includeSubagents` (default false) controls whether subagents
|
|
65
|
+
* (delegationDepth > 0) follow the same bootstrap phase as the main session —
|
|
66
|
+
* false (default) keeps the assembled catalog from their very first request
|
|
67
|
+
* (the historical prompt-tool default: delegated agents inherit the full
|
|
68
|
+
* tool surface); true makes their first request see the bootstrap pair and
|
|
69
|
+
* their own first reply or tool call promotes them. Keep in sync with the
|
|
70
|
+
* context-gate row's flag when both rows are present.
|
|
71
|
+
*
|
|
72
|
+
* POST-PROMOTION CATALOG (prompt-tool patch): after promotion both modes
|
|
73
|
+
* keep the assembled catalog. `usePtcMode` switches the wire presentation
|
|
74
|
+
* to Code Mode (PTC, single run_code) instead of narrowing the resident set.
|
|
75
|
+
* The controlled phase below still narrows the catalog before promotion and
|
|
76
|
+
* after compaction.
|
|
77
|
+
* COMPACTION (local addition): a compaction rewrites the whole surface, so the
|
|
78
|
+
* first post-compaction request is a "second first request". Promotion is
|
|
79
|
+
* epoch-aware (see compaction-epoch.mjs): after `compaction/end` the session
|
|
80
|
+
* falls back to the controlled phase — the bootstrap pair plus
|
|
81
|
+
* `compactionTools` (a core work set, default none) — until a NEW durable
|
|
82
|
+
* promotion signal exists past that boundary. The model is mid-task and needs
|
|
83
|
+
* to keep working, but still faces a small catalog instead of the full
|
|
84
|
+
* Standard set.
|
|
85
|
+
*
|
|
86
|
+
* Robustness:
|
|
87
|
+
* - Promotion decisions are memoized per session id for this process; the
|
|
88
|
+
* durable event scan runs once per session per process, then O(1).
|
|
89
|
+
* - Subagents keep the assembled catalog unless `includeSubagents: true`.
|
|
90
|
+
* - A missing bootstrap tool degrades to the full catalog with a one-time
|
|
91
|
+
* warning instead of throwing, so a composition drift can never brick
|
|
92
|
+
* every request of a session.
|
|
93
|
+
* - Invalid config (bad tool lists, unknown `promoteOn`, malformed flags,
|
|
94
|
+
* non-positive `bootstrapMaxTokens`) fails at apply time, i.e. at preset
|
|
95
|
+
* mount, where it is visible and fixable.
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
import { createEpochPromotion } from './compaction-epoch.mjs'
|
|
99
|
+
import { booleanOption, createWarnOnce, parsePromoteOn, validateConfig } from './shared.mjs'
|
|
100
|
+
|
|
101
|
+
/** Cordis plugin name used by loader diagnostics. */
|
|
102
|
+
export const name = 'anchored-tool-bootstrap'
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Deliberately NO inject list: the listeners only touch services at event
|
|
106
|
+
* time. Keep this row right AFTER the context-gate row in agent.cordis.yml:
|
|
107
|
+
* waterfall after-next transforms apply in reverse registration order, so the
|
|
108
|
+
* tool filter here must register before any plugin that touches the same
|
|
109
|
+
* assembly. The optional budget listener registers with `prepend: true` so a
|
|
110
|
+
* later listener can never override the first-round cap after we set it.
|
|
111
|
+
*/
|
|
112
|
+
export const inject = []
|
|
113
|
+
|
|
114
|
+
/** Every config key this plugin accepts — anything else is a typo. */
|
|
115
|
+
const ALLOWED_KEYS = new Set([
|
|
116
|
+
'bootstrapTools', 'promoteOn', 'bootstrapMaxTokens', 'compactionTools',
|
|
117
|
+
'includeSubagents', 'usePtcMode',
|
|
118
|
+
'promoteGate', 'maxPromoteSteps', 'promoteAfterFirstResponse',
|
|
119
|
+
'personaSectionsOnly', 'workspaceLine', 'phase1FirstCallInstruction',
|
|
120
|
+
])
|
|
121
|
+
|
|
122
|
+
/** 预设 persona section 名(官方注册名 + 旧名)。 */
|
|
123
|
+
const PERSONA_SECTION_NAMES = new Set(['deployment:persona', 'persona'])
|
|
124
|
+
|
|
125
|
+
/** 晋升后 persona 附加的工作目录行前缀。 */
|
|
126
|
+
const WORKSPACE_LINE_PREFIX = '\n\nYour working directory is '
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/** Non-empty string list config validator. */
|
|
130
|
+
function stringList(value, field) {
|
|
131
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== 'string' || item.length === 0)) {
|
|
132
|
+
throw new TypeError(`${name}: ${field} must be a non-empty array of non-empty strings`)
|
|
133
|
+
}
|
|
134
|
+
return [...new Set(value)]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function stringListOrEmpty(value, field) {
|
|
138
|
+
if (value === undefined) return []
|
|
139
|
+
return stringList(value, field)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Validate the optional first-request output cap. `undefined` means NO cap:
|
|
145
|
+
* the Minimal tool schema anchors at the adapter-default maxTokens, and the
|
|
146
|
+
* cap's delivery is profile-package dependent (see the header note), so it is
|
|
147
|
+
* opt-in rather than the default.
|
|
148
|
+
*/
|
|
149
|
+
function optionalPositiveInt(value, field) {
|
|
150
|
+
if (value === undefined) return undefined
|
|
151
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
152
|
+
throw new TypeError(`${name}: ${field} must be a positive safe integer`)
|
|
153
|
+
}
|
|
154
|
+
return value
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Register the per-session bootstrap filters. */
|
|
158
|
+
export function apply(ctx, config) {
|
|
159
|
+
const source = validateConfig(name, config, ALLOWED_KEYS)
|
|
160
|
+
// 门控模式(promoteGate / promoteAfterFirstResponse)固定 either 晋升语义,
|
|
161
|
+
// promoteOn 显式非 either 时与门控互斥,fail loud 而非静默忽略。
|
|
162
|
+
if ((source.promoteGate === true || source.promoteAfterFirstResponse === true)
|
|
163
|
+
&& source.promoteOn !== undefined && source.promoteOn !== 'either') {
|
|
164
|
+
throw new TypeError(`${name}: promoteGate/promoteAfterFirstResponse 门控模式固定 either 晋升语义,promoteOn 必须省略或为 "either"`)
|
|
165
|
+
}
|
|
166
|
+
const bootstrapTools = stringList(source.bootstrapTools, 'bootstrapTools')
|
|
167
|
+
const promoteEvents = parsePromoteOn(name, source.promoteOn)
|
|
168
|
+
const bootstrapMaxTokens = optionalPositiveInt(source.bootstrapMaxTokens, 'bootstrapMaxTokens')
|
|
169
|
+
const includeSubagents = booleanOption(name, source.includeSubagents, 'includeSubagents', false)
|
|
170
|
+
const usePtcMode = booleanOption(name, source.usePtcMode, 'usePtcMode', true)
|
|
171
|
+
const personaSectionsOnly = booleanOption(name, source.personaSectionsOnly, 'personaSectionsOnly', false)
|
|
172
|
+
const workspaceLine = booleanOption(name, source.workspaceLine, 'workspaceLine', false)
|
|
173
|
+
const phase1FirstCallInstruction = typeof source.phase1FirstCallInstruction === 'string'
|
|
174
|
+
? source.phase1FirstCallInstruction
|
|
175
|
+
: ''
|
|
176
|
+
// Core work set exposed after a compaction, before re-promotion. Empty
|
|
177
|
+
// means "no compaction recovery catalog": the session stays on the
|
|
178
|
+
// bootstrap pair until a new promotion signal.
|
|
179
|
+
const compactionTools = stringListOrEmpty(source.compactionTools, 'compactionTools')
|
|
180
|
+
|
|
181
|
+
const promotion = createEpochPromotion(promoteEvents, {
|
|
182
|
+
includeSubagents,
|
|
183
|
+
promoteGate: source.promoteGate === true,
|
|
184
|
+
promoteAfterFirstResponse: source.promoteAfterFirstResponse === true,
|
|
185
|
+
maxPromoteSteps: source.maxPromoteSteps,
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// prompt-tool patch: optional Code Mode (PTC) wire presentation after promotion.
|
|
189
|
+
const presentationBySession = new WeakMap()
|
|
190
|
+
const agentBySession = new WeakMap()
|
|
191
|
+
const presentationState = (session) => {
|
|
192
|
+
let state = presentationBySession.get(session)
|
|
193
|
+
if (state === undefined) {
|
|
194
|
+
state = { applied: false, disposer: undefined }
|
|
195
|
+
presentationBySession.set(session, state)
|
|
196
|
+
}
|
|
197
|
+
return state
|
|
198
|
+
}
|
|
199
|
+
const applyCodePresentation = (agent) => {
|
|
200
|
+
const session = agent?.session
|
|
201
|
+
if (session === undefined) return
|
|
202
|
+
const state = presentationState(session)
|
|
203
|
+
if (state.applied) return
|
|
204
|
+
const tools = agent.ctx?.tools
|
|
205
|
+
if (tools === undefined || typeof tools.presentAs !== 'function') return
|
|
206
|
+
state.disposer = tools.presentAs('code')
|
|
207
|
+
state.applied = true
|
|
208
|
+
}
|
|
209
|
+
const releaseCodePresentation = (session) => {
|
|
210
|
+
const state = presentationBySession.get(session)
|
|
211
|
+
if (state === undefined) return
|
|
212
|
+
if (typeof state.disposer === 'function') {
|
|
213
|
+
try { state.disposer() } catch { /* never brick the session */ }
|
|
214
|
+
}
|
|
215
|
+
state.disposer = undefined
|
|
216
|
+
state.applied = false
|
|
217
|
+
}
|
|
218
|
+
ctx.on('session/event', (session, event) => promotion.observe(session, event))
|
|
219
|
+
|
|
220
|
+
ctx.on('session/event', (session, event) => {
|
|
221
|
+
if (!usePtcMode) return
|
|
222
|
+
if (event.type === 'compaction/end') {
|
|
223
|
+
releaseCodePresentation(session)
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
if (event.type !== 'step/end' && event.type !== 'turn/end') return
|
|
227
|
+
const agent = agentBySession.get(session)
|
|
228
|
+
if (agent !== undefined && promotion.status(agent).promoted) applyCodePresentation(agent)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
const warnOnce = createWarnOnce(ctx, name)
|
|
232
|
+
|
|
233
|
+
/** 晋升后给 persona 追加工作目录行(无 persona/无 cwd/已含则原样返回)。 */
|
|
234
|
+
const withWorkspaceLine = (assembly, agent) => {
|
|
235
|
+
const cwd = agent?.session?.header?.cwd
|
|
236
|
+
if (typeof cwd !== 'string' || cwd.length === 0) return assembly
|
|
237
|
+
if (!Array.isArray(assembly.sections)) return assembly
|
|
238
|
+
const line = `${WORKSPACE_LINE_PREFIX}${cwd}.`
|
|
239
|
+
const persona = assembly.sections.find((section) =>
|
|
240
|
+
PERSONA_SECTION_NAMES.has(section?.name)
|
|
241
|
+
&& typeof section?.text === 'string'
|
|
242
|
+
&& !section.text.includes(line))
|
|
243
|
+
if (persona === undefined) return assembly
|
|
244
|
+
return {
|
|
245
|
+
...assembly,
|
|
246
|
+
sections: assembly.sections.map((section) => section === persona
|
|
247
|
+
? { ...section, text: `${persona.text}${line}` }
|
|
248
|
+
: section),
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Narrow the assembled catalog to a keep-set; validate required names. */
|
|
253
|
+
const keepTools = (assembled, keep, missingAllowsFullCatalog) => {
|
|
254
|
+
const available = new Set(assembled.tools.map((tool) => tool.name))
|
|
255
|
+
const missing = [...keep].filter((toolName) => !available.has(toolName))
|
|
256
|
+
if (missing.length > 0) {
|
|
257
|
+
warnOnce(
|
|
258
|
+
`${name}: expected every phase tool; missing=${JSON.stringify(missing)} — `
|
|
259
|
+
+ (missingAllowsFullCatalog ? 'bootstrap disabled, full catalog exposed' : 'continuing with what is available'),
|
|
260
|
+
)
|
|
261
|
+
if (missingAllowsFullCatalog) return assembled
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
...assembled,
|
|
265
|
+
tools: assembled.tools.filter((tool) => keep.has(tool.name)),
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
|
|
270
|
+
// Downstream errors propagate untouched; only this filter's own logic is guarded.
|
|
271
|
+
const assembled = await next()
|
|
272
|
+
try {
|
|
273
|
+
const agent = context.agent
|
|
274
|
+
if (agent === undefined) return assembled
|
|
275
|
+
if (agent.session === undefined) return assembled
|
|
276
|
+
agentBySession.set(agent.session, agent)
|
|
277
|
+
if ((agent.session?.header?.delegationDepth ?? 0) > 0 && !includeSubagents) {
|
|
278
|
+
// 默认:子代理继承完整目录(历史 prompt-tool 默认);
|
|
279
|
+
// includeSubagents=true 时落到下方正常相位逻辑(首轮裁剪 + 晋升)。
|
|
280
|
+
if (usePtcMode) applyCodePresentation(agent)
|
|
281
|
+
return assembled
|
|
282
|
+
}
|
|
283
|
+
const status = promotion.status(agent)
|
|
284
|
+
if (status.promoted) {
|
|
285
|
+
// prompt-tool patch: both modes keep the assembled catalog after promotion.
|
|
286
|
+
// usePtcMode switches the wire presentation instead of narrowing resident tools.
|
|
287
|
+
if (usePtcMode) applyCodePresentation(agent)
|
|
288
|
+
return workspaceLine ? withWorkspaceLine(assembled, agent) : assembled
|
|
289
|
+
}
|
|
290
|
+
// Controlled phase: the bootstrap pair; after a compaction, plus the
|
|
291
|
+
// compaction work set so mid-task work can continue. Context control is
|
|
292
|
+
// NOT here: the companion `context-gate` plugin owns it (see the header
|
|
293
|
+
// note), so this filter touches only the tool catalog.
|
|
294
|
+
const { boundary } = status
|
|
295
|
+
const keep = new Set(bootstrapTools)
|
|
296
|
+
if (boundary >= 0) for (const toolName of compactionTools) keep.add(toolName)
|
|
297
|
+
let next = keepTools(assembled, keep, true)
|
|
298
|
+
if (personaSectionsOnly) {
|
|
299
|
+
// Phase-1 提示词段只留 persona(plan-mode 策略等晋升后才恢复)。
|
|
300
|
+
const sections = Array.isArray(next.sections)
|
|
301
|
+
? next.sections.filter((section) => PERSONA_SECTION_NAMES.has(section?.name))
|
|
302
|
+
: undefined
|
|
303
|
+
if (sections !== undefined) {
|
|
304
|
+
const phase1Sections = phase1FirstCallInstruction === ''
|
|
305
|
+
? sections
|
|
306
|
+
: sections.map((section) => {
|
|
307
|
+
if (typeof section?.text !== 'string' || section.text.includes(phase1FirstCallInstruction)) return section
|
|
308
|
+
return { ...section, text: `${section.text}${phase1FirstCallInstruction}` }
|
|
309
|
+
})
|
|
310
|
+
next = { ...next, sections: phase1Sections }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return next
|
|
314
|
+
} catch (error) {
|
|
315
|
+
// A filter bug must never brick a session: degrade to the full catalog.
|
|
316
|
+
warnOnce(`${name}: bootstrap filter failed, exposing the full catalog: ${String((error && error.message) || error)}`)
|
|
317
|
+
return assembled
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
// Optionally cap the first model request's output budget while bootstrapping.
|
|
322
|
+
// Unset (`bootstrapMaxTokens` omitted) means the adapter default flows — the
|
|
323
|
+
// Minimal tool schema anchors at 256000 without a cap (issue #11).
|
|
324
|
+
if (bootstrapMaxTokens !== undefined) {
|
|
325
|
+
// Same registration discipline as the pre-step strip below: `prepend`
|
|
326
|
+
// keeps this listener the OUTERMOST transform of the agent/request
|
|
327
|
+
// waterfall for the same registration-order reasons (loader row
|
|
328
|
+
// application is concurrent; row order alone does not decide listener
|
|
329
|
+
// order — see issue #6 and upstream PR #13), so a later listener can
|
|
330
|
+
// never override the first-round budget after we set it.
|
|
331
|
+
ctx.on('agent/request', async (payload, next) => {
|
|
332
|
+
const resolved = await next()
|
|
333
|
+
const agent = payload.agent
|
|
334
|
+
if (promotion.status(agent).promoted) {
|
|
335
|
+
// The next request's seed proposal carries the previous header's
|
|
336
|
+
// maxTokens forward, so the injected cap must be stripped explicitly —
|
|
337
|
+
// otherwise it would persist for the whole session.
|
|
338
|
+
if (resolved.maxTokens === bootstrapMaxTokens) {
|
|
339
|
+
const { maxTokens: _bootstrap, ...rest } = resolved
|
|
340
|
+
return rest
|
|
341
|
+
}
|
|
342
|
+
return resolved
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
...resolved,
|
|
346
|
+
maxTokens: bootstrapMaxTokens,
|
|
347
|
+
}
|
|
348
|
+
}, { prepend: true })
|
|
349
|
+
}
|
|
350
|
+
}
|