dsh-plugin-prompt-tool 0.6.2 → 0.7.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 +11 -9
- package/engine/anchor-match.mjs +38 -12
- package/engine/anchor-turn.mjs +68 -0
- package/engine/classify-task.mjs +35 -0
- package/engine/code-presentation.mjs +103 -0
- package/engine/compositions/library/anchor-turn.yml +10 -0
- package/engine/compositions/library/bootstrap-filesystem.yml +1 -1
- package/engine/compositions/library/code-presentation.yml +8 -0
- package/engine/compositions/library/command-goal.yml +11 -0
- package/engine/compositions/library/compaction.yml +1 -1
- package/engine/compositions/library/context-gate.yml +1 -2
- package/engine/compositions/library/cot-drip.yml +11 -0
- package/engine/compositions/library/custom-bash.yml +1 -1
- package/engine/compositions/library/delegation.yml +11 -7
- package/engine/compositions/library/deliberation-gate.yml +11 -0
- package/engine/compositions/library/persistent-shell.yml +25 -2
- package/engine/compositions/library/persona.yml +6 -2
- package/engine/compositions/library/planning.yml +1 -1
- package/engine/compositions/library/prompt-config-engine.yml +1 -1
- package/engine/compositions/library/run-code-env.yml +1 -1
- package/engine/compositions/library/skill-filesystem.yml +1 -1
- package/engine/compositions/library/skill-search.yml +1 -1
- package/engine/compositions/library/str-replace-editor.yml +2 -2
- package/engine/compositions/library/tool-ask-user.yml +1 -1
- package/engine/compositions/library/tool-bash.yml +1 -1
- package/engine/compositions/library/tool-bootstrap.yml +1 -3
- package/engine/compositions/library/tool-config-engine.yml +9 -0
- package/engine/compositions/library/tool-filter.yml +3 -7
- package/engine/compositions/library/tool-fs-search.yml +1 -1
- package/engine/compositions/library/tool-fs.yml +1 -1
- package/engine/compositions/library/tool-goal.yml +1 -9
- package/engine/compositions/library/tool-jobs.yml +1 -1
- package/engine/compositions/library/tool-pwsh.yml +1 -1
- package/engine/compositions/library/tool-todo.yml +1 -1
- package/engine/compositions/library/tool-web.yml +2 -2
- package/engine/compositions/source/local/code-presentation.yml +8 -0
- package/engine/compositions/source/local/context-gate.yml +0 -1
- package/engine/compositions/source/local/str-replace-editor.yml +10 -0
- package/engine/compositions/source/local/tool-bootstrap.yml +1 -3
- package/engine/compositions/source/local/tool-config-engine.yml +9 -0
- package/engine/compositions/source/local/tool-filter.yml +8 -0
- package/engine/cot-drip.mjs +137 -0
- package/engine/custom-bash.mjs +73 -21
- package/engine/deliberation-gate.mjs +127 -0
- package/engine/layers.mjs +223 -198
- package/engine/run-code-env.mjs +8 -2
- package/engine/schema.mjs +1 -1
- package/engine/strategies.mjs +25 -14
- package/engine/tool-bootstrap.mjs +185 -59
- package/engine/tool-config-engine.mjs +390 -0
- package/lib/client.js +3840 -2311
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +185 -79
- package/lib/index.mjs +1000 -356
- package/lib/preset-core.d.mts +1 -1
- package/lib/preset-core.mjs +29 -3
- package/lib/{prompt-configs-BgFw_bFf.mjs → prompt-configs-BnQKRHVb.mjs} +211 -136
- package/lib/prompt-configs-CN0sJTUL.d.mts +193 -0
- package/package.json +121 -105
- package/preset/anchored/preset.yml +262 -308
- package/preset/creative/preset.yml +34 -32
- package/preset/custom/preset.yml +0 -1
- package/preset/liangshen/preset.yml +53 -50
- package/preset/minimal/preset.yml +18 -17
- package/preset/ptc/preset.yml +35 -33
- package/preset/standard/preset.yml +34 -32
- package/templates/10-pre-step.yml +18 -15
- package/templates/14-first-turn-anchor.yml +13 -13
- package/templates/15-guide-auto.yml +13 -13
- package/templates/16-custom-fallback.yml +19 -18
- package/templates/18-placeholder-env-facts.yml +13 -2
- package/templates/30-runtime-context.yml +3 -0
- package/templates/40-agent-request.yml +9 -9
- package/templates/60-tool-pipeline.yml +9 -8
- package/templates/tools/10-shell-command.yml +20 -0
- package/templates/tools/20-http-fetch.yml +24 -0
- package/templates/tools/30-fs-read.yml +20 -0
- package/templates/tools/40-delegate-character.yml +15 -0
- package/templates/tools/50-delegate-worldbook.yml +15 -0
- package/templates/tools/60-delegate-session-var.yml +15 -0
- package/templates/tools/70-delegate-worldbook-upsert.yml +36 -0
- package/lib/prompt-configs-B4vH09wx.d.mts +0 -100
- package/templates/11-merged-a.yml +0 -11
- package/templates/13-anchor.yml +0 -19
- package/templates/17-instruction-hint.yml +0 -23
- package/templates/19-placeholder-skill-catalog.yml +0 -19
- package/templates/31-runtime-context-placeholder.yml +0 -18
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Anchored tool bootstrap — keep the FIRST model request on the Minimal
|
|
3
3
|
* preset's REAL tool schema (persistent `bash` + `str_replace_editor`), then
|
|
4
4
|
* keep the assembled catalog once the session has produced its first durable
|
|
5
|
-
* promotion signal
|
|
6
|
-
*
|
|
7
|
-
* `context-gate` plugin
|
|
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
8
|
*
|
|
9
9
|
* GATE MODE (liangshen stabilize 扩展, source: xiaobright/dsh-anchored-standard
|
|
10
10
|
* MIT + phase-1 quarantine): `promoteGate: true` gates the promotion on the
|
|
@@ -19,6 +19,15 @@
|
|
|
19
19
|
* appended to the phase-1 persona (test builds, issue #274) — unset keeps
|
|
20
20
|
* the exact one-line Minimal anchor.
|
|
21
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
|
+
*
|
|
22
31
|
* The phase is derived from durable session events, so resume and reload
|
|
23
32
|
* preserve it. By default (`promoteOn: 'either'`) a session promotes after the
|
|
24
33
|
* first `tool/call` OR the first `assistant/message`, whichever comes first:
|
|
@@ -70,10 +79,8 @@
|
|
|
70
79
|
* context-gate row's flag when both rows are present.
|
|
71
80
|
*
|
|
72
81
|
* POST-PROMOTION CATALOG (prompt-tool patch): after promotion both modes
|
|
73
|
-
* keep the assembled catalog.
|
|
74
|
-
*
|
|
75
|
-
* The controlled phase below still narrows the catalog before promotion and
|
|
76
|
-
* after compaction.
|
|
82
|
+
* keep the assembled catalog (no narrowing). The controlled phase below
|
|
83
|
+
* still narrows the catalog before promotion and after compaction.
|
|
77
84
|
* COMPACTION (local addition): a compaction rewrites the whole surface, so the
|
|
78
85
|
* first post-compaction request is a "second first request". Promotion is
|
|
79
86
|
* epoch-aware (see compaction-epoch.mjs): after `compaction/end` the session
|
|
@@ -114,9 +121,10 @@ export const inject = []
|
|
|
114
121
|
/** Every config key this plugin accepts — anything else is a typo. */
|
|
115
122
|
const ALLOWED_KEYS = new Set([
|
|
116
123
|
'bootstrapTools', 'promoteOn', 'bootstrapMaxTokens', 'compactionTools',
|
|
117
|
-
'includeSubagents',
|
|
124
|
+
'includeSubagents',
|
|
118
125
|
'promoteGate', 'maxPromoteSteps', 'promoteAfterFirstResponse',
|
|
119
126
|
'personaSectionsOnly', 'workspaceLine', 'phase1FirstCallInstruction',
|
|
127
|
+
'stages', 'stagePreUnlock', 'stageAdvanceTool', 'stageAdvanceDescription', 'stageSectionTemplate',
|
|
120
128
|
])
|
|
121
129
|
|
|
122
130
|
/** 预设 persona section 名(官方注册名 + 旧名)。 */
|
|
@@ -127,8 +135,8 @@ const WORKSPACE_LINE_PREFIX = '\n\nYour working directory is '
|
|
|
127
135
|
|
|
128
136
|
|
|
129
137
|
/** 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)) {
|
|
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)) {
|
|
132
140
|
throw new TypeError(`${name}: ${field} must be a non-empty array of non-empty strings`)
|
|
133
141
|
}
|
|
134
142
|
return [...new Set(value)]
|
|
@@ -139,6 +147,25 @@ function stringListOrEmpty(value, field) {
|
|
|
139
147
|
return stringList(value, field)
|
|
140
148
|
}
|
|
141
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
|
+
|
|
142
169
|
|
|
143
170
|
/**
|
|
144
171
|
* Validate the optional first-request output cap. `undefined` means NO cap:
|
|
@@ -163,16 +190,84 @@ export function apply(ctx, config) {
|
|
|
163
190
|
&& source.promoteOn !== undefined && source.promoteOn !== 'either') {
|
|
164
191
|
throw new TypeError(`${name}: promoteGate/promoteAfterFirstResponse 门控模式固定 either 晋升语义,promoteOn 必须省略或为 "either"`)
|
|
165
192
|
}
|
|
166
|
-
|
|
193
|
+
// bootstrapTools 允许空数组 = 零工具模式(上游 zero-tool-bootstrap 等价:
|
|
194
|
+
// 首请求 tools: [] 产生最深 "we" 轨迹,assistant/message 后晋升)。
|
|
195
|
+
const bootstrapTools = stringList(source.bootstrapTools, 'bootstrapTools', true)
|
|
167
196
|
const promoteEvents = parsePromoteOn(name, source.promoteOn)
|
|
168
197
|
const bootstrapMaxTokens = optionalPositiveInt(source.bootstrapMaxTokens, 'bootstrapMaxTokens')
|
|
169
198
|
const includeSubagents = booleanOption(name, source.includeSubagents, 'includeSubagents', false)
|
|
170
|
-
const usePtcMode = booleanOption(name, source.usePtcMode, 'usePtcMode', true)
|
|
171
199
|
const personaSectionsOnly = booleanOption(name, source.personaSectionsOnly, 'personaSectionsOnly', false)
|
|
172
200
|
const workspaceLine = booleanOption(name, source.workspaceLine, 'workspaceLine', false)
|
|
173
201
|
const phase1FirstCallInstruction = typeof source.phase1FirstCallInstruction === 'string'
|
|
174
202
|
? source.phase1FirstCallInstruction
|
|
175
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
|
+
}
|
|
176
271
|
// Core work set exposed after a compaction, before re-promotion. Empty
|
|
177
272
|
// means "no compaction recovery catalog": the session stays on the
|
|
178
273
|
// bootstrap pair until a new promotion signal.
|
|
@@ -185,48 +280,14 @@ export function apply(ctx, config) {
|
|
|
185
280
|
maxPromoteSteps: source.maxPromoteSteps,
|
|
186
281
|
})
|
|
187
282
|
|
|
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
283
|
ctx.on('session/event', (session, event) => promotion.observe(session, event))
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
})
|
|
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
|
+
}
|
|
230
291
|
|
|
231
292
|
const warnOnce = createWarnOnce(ctx, name)
|
|
232
293
|
|
|
@@ -273,18 +334,44 @@ export function apply(ctx, config) {
|
|
|
273
334
|
const agent = context.agent
|
|
274
335
|
if (agent === undefined) return assembled
|
|
275
336
|
if (agent.session === undefined) return assembled
|
|
276
|
-
agentBySession.set(agent.session, agent)
|
|
277
337
|
if ((agent.session?.header?.delegationDepth ?? 0) > 0 && !includeSubagents) {
|
|
278
338
|
// 默认:子代理继承完整目录(历史 prompt-tool 默认);
|
|
279
339
|
// includeSubagents=true 时落到下方正常相位逻辑(首轮裁剪 + 晋升)。
|
|
280
|
-
if (usePtcMode) applyCodePresentation(agent)
|
|
281
340
|
return assembled
|
|
282
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
|
+
}
|
|
283
371
|
const status = promotion.status(agent)
|
|
284
372
|
if (status.promoted) {
|
|
285
|
-
// prompt-tool patch:
|
|
286
|
-
//
|
|
287
|
-
if (usePtcMode) applyCodePresentation(agent)
|
|
373
|
+
// prompt-tool patch: keep the assembled catalog after promotion
|
|
374
|
+
// (Code Mode presentation is the companion code-presentation row's job).
|
|
288
375
|
return workspaceLine ? withWorkspaceLine(assembled, agent) : assembled
|
|
289
376
|
}
|
|
290
377
|
// Controlled phase: the bootstrap pair; after a compaction, plus the
|
|
@@ -293,7 +380,15 @@ export function apply(ctx, config) {
|
|
|
293
380
|
// note), so this filter touches only the tool catalog.
|
|
294
381
|
const { boundary } = status
|
|
295
382
|
const keep = new Set(bootstrapTools)
|
|
296
|
-
if (boundary >= 0)
|
|
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
|
+
}
|
|
297
392
|
let next = keepTools(assembled, keep, true)
|
|
298
393
|
if (personaSectionsOnly) {
|
|
299
394
|
// Phase-1 提示词段只留 persona(plan-mode 策略等晋升后才恢复)。
|
|
@@ -347,4 +442,35 @@ export function apply(ctx, config) {
|
|
|
347
442
|
}
|
|
348
443
|
}, { prepend: true })
|
|
349
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
|
+
}
|
|
350
476
|
}
|