dsh-switchman 1.10.3 → 1.11.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.
package/cordis.patch.yml CHANGED
@@ -28,11 +28,13 @@
28
28
  #
29
29
  # Provenance & maintenance: the four `plugins` lists below are copied verbatim
30
30
  # from @deepseek-ai/dsh-web-app presets/{standard,ptc,minimal,cordis}.patch.yml
31
- # (dsh 0.2.1-rc.1: time-context + tool-schedule + the disabled tool-plugin-market
32
- # row joined every list, the subagent rows lost `backgroundMode` under the
33
- # unified activation lifecycles, the ralph/codex/claude-code rows left the
34
- # presets, minimal gained image-only fs tools and a compaction group, and the
35
- # plan-mode text was reworded). A Loader override replaces the target row's
31
+ # (dsh 0.2.2-rc.1: tool-fs ships `applyPatch: false`, tool-fs-search ships
32
+ # `sampleOverCapGlobResults: false`, the subagent spawn row carries
33
+ # `modelSelection` + `modelDiscovery` (the 0.2.1 unified-activation shape),
34
+ # tool-plugin-manager/market disabled guards use `!!js "!ctx.get('profileContext')"`
35
+ # expression strings, and no list-agents row ships; dsh 0.2.1-rc.1 had added
36
+ # time-context + tool-schedule, dropped `backgroundMode`, and reworded the
37
+ # plan-mode text). A Loader override replaces the target row's
36
38
  # `config` wholesale, so every field is restated here. After a DSH upgrade that
37
39
  # changes the shipped presets, re-sync these lists from the new preset files,
38
40
  # then reinstall this bundle; scripts/validate.mjs compares this patch against
@@ -45,7 +47,7 @@
45
47
  # scope, and a second instance makes session composition throw
46
48
  # `tool "list_subagent_models" is already registered in this scope` — breaking
47
49
  # creation of ALL new sessions and the recomposition of old ones. The shipped
48
- # presets (verified against installed dsh-web-app 0.2.0-rc.2, the fork's
50
+ # presets (verified against installed dsh-web-app 0.2.2-rc.1, the fork's
49
51
  # 0.2.1-alpha.1, and 0.2.1-rc.1) enable it on the spawn row only; fork-side
50
52
  # explicit routing is not a factory capability.
51
53
  #
@@ -97,7 +99,7 @@
97
99
  - 禁止复活死会话:子智能体达 cap 终止后,用自包含 prompt 新派一个,不 resume 旧壳。
98
100
  - 广搜先问:首次整项目范围的搜索先向用户澄清范围,定点检索不受此限。
99
101
  - 大额派发先估成本:预计 ≥3 个成员或总消耗 >100k token 时,先报规模与预估请用户确认。
100
- - 子路由两通道:显式传 provider/model/effort 任一字段会经 DSH 会话级授权白名单校验(设置页「允许 Agent 为子智能体选择模型」勾选的授权列表,与泳道池无关);被拒时用 list_subagent_models 查实际可用路由再派,或改为完全不传三字段走隐式继承(不受校验,子智能体用默认值或继承主会话路由)。泳道池只是偏好排序,授权边界以 DSH 设置为准,不试图改写它。
102
+ - 子路由两通道:派发 subagent/workflow 默认显式传 provider/model/effort(按泳道表逐字段拆填,经 DSH 会话级授权白名单校验——设置页「允许 Agent 为子智能体选择模型」的授权列表,与泳道池无关);隐式继承(完全不传三字段)不是省事通道,仅当显式尝试被拒且 list_subagent_models 确认无可用路由后才可兜底,并在回报中声明 IMPLICIT-DOWNGRADED——隐式拿到的是主会话路由,泳道模型丢失。泳道池只是偏好排序,授权边界以 DSH 设置为准,不试图改写它。
101
103
 
102
104
  ## 三、分级验证(质量下限,不可省)
103
105
  - 改动 >20 行或涉及多个调用点:交 tester 验证。
@@ -122,6 +124,8 @@
122
124
  disabled: !!js process.platform !== 'win32'
123
125
  - id: tool-fs
124
126
  name: '@deepseek-ai/dsh-tool-fs'
127
+ config:
128
+ applyPatch: false
125
129
  - id: tool-fs-search
126
130
  name: '@deepseek-ai/dsh-tool-fs-search'
127
131
  config:
@@ -192,14 +196,13 @@
192
196
  config:
193
197
  - id: tool-subagent-control
194
198
  name: '@deepseek-ai/dsh-tool-subagent-control'
195
- - id: tool-subagent-list-agents
196
- name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
197
199
  - id: tool-subagent
198
200
  name: '@deepseek-ai/dsh-tool-subagent'
199
201
  config:
200
202
  provider: spawn
201
203
  toolName: subagent
202
- modelSelectionSettings: true
204
+ modelSelection: true
205
+ modelDiscovery: true
203
206
  - id: tool-subagent-fork
204
207
  name: '@deepseek-ai/dsh-tool-subagent'
205
208
  config:
@@ -226,7 +229,7 @@
226
229
  name: '@deepseek-ai/dsh-tool-present'
227
230
  - id: tool-plugin-manager
228
231
  name: '@deepseek-ai/dsh-plugin-manager/tools'
229
- disabled: true
232
+ disabled: !!js "!ctx.get('profileContext')"
230
233
  - id: tool-plugin-market
231
234
  name: '@deepseek-ai/dsh-plugin-market/tools'
232
235
  disabled: true
@@ -260,6 +263,8 @@
260
263
  disabled: !!js process.platform !== 'win32'
261
264
  - id: tool-fs
262
265
  name: '@deepseek-ai/dsh-tool-fs'
266
+ config:
267
+ applyPatch: false
263
268
  - id: tool-fs-search
264
269
  name: '@deepseek-ai/dsh-tool-fs-search'
265
270
  config:
@@ -330,14 +335,13 @@
330
335
  config:
331
336
  - id: tool-subagent-control
332
337
  name: '@deepseek-ai/dsh-tool-subagent-control'
333
- - id: tool-subagent-list-agents
334
- name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
335
338
  - id: tool-subagent
336
339
  name: '@deepseek-ai/dsh-tool-subagent'
337
340
  config:
338
341
  provider: spawn
339
342
  toolName: subagent
340
- modelSelectionSettings: true
343
+ modelSelection: true
344
+ modelDiscovery: true
341
345
  - id: tool-subagent-fork
342
346
  name: '@deepseek-ai/dsh-tool-subagent'
343
347
  config:
@@ -362,10 +366,8 @@
362
366
  config:
363
367
  fetch: true
364
368
  searchTimeoutMs: 60000
365
- - id: tool-presentation
366
- name: '@deepseek-ai/dsh-agent-tool-presentation'
367
- config:
368
- mode: ptc
369
+ - id: tool-executor-ptc
370
+ name: '@deepseek-ai/dsh-tool-executor-ptc'
369
371
  - id: present
370
372
  name: '@deepseek-ai/dsh-tool-present'
371
373
  - id: tool-plugin-manager
@@ -502,6 +504,8 @@
502
504
  disabled: !!js process.platform !== 'win32'
503
505
  - id: tool-fs
504
506
  name: '@deepseek-ai/dsh-tool-fs'
507
+ config:
508
+ applyPatch: false
505
509
  - id: tool-fs-search
506
510
  name: '@deepseek-ai/dsh-tool-fs-search'
507
511
  config:
@@ -568,14 +572,13 @@
568
572
  config:
569
573
  - id: tool-subagent-control
570
574
  name: '@deepseek-ai/dsh-tool-subagent-control'
571
- - id: tool-subagent-list-agents
572
- name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
573
575
  - id: tool-subagent
574
576
  name: '@deepseek-ai/dsh-tool-subagent'
575
577
  config:
576
578
  provider: spawn
577
579
  toolName: subagent
578
- modelSelectionSettings: true
580
+ modelSelection: true
581
+ modelDiscovery: true
579
582
  - id: tool-subagent-fork
580
583
  name: '@deepseek-ai/dsh-tool-subagent'
581
584
  config:
package/host/config.js CHANGED
@@ -129,11 +129,21 @@ export const Config = z.object({
129
129
  // --- Phase 3: context watermark control --------------------------------
130
130
  /** Soft watermark: advisory banner + delegation nudge (tokens). */
131
131
  wmSoftTokens: z.number().min(1000).default(80000).volatile(),
132
- /** Hard watermark: read budget tightens, wrap-up advised (tokens). */
133
- wmHardTokens: z.number().min(2000).default(120000).volatile(),
132
+ /** Hard watermark: read budget tightens, wrap-up advised (tokens). 95k
133
+ * sits just under the trim pressure line (65% of the 150k window ≈
134
+ * 97.5k): past
135
+ * this point the fold engine is already folding, so per-call read
136
+ * budgets must tighten at the SAME boundary — agent sessions grow in
137
+ * PULSES (one large file read = tens of thousands of tokens injected
138
+ * at once, faster than any fold can commit), and the read budget is
139
+ * the only preventive valve on the pulse source. */
140
+ wmHardTokens: z.number().min(2000).default(95000).volatile(),
134
141
  /** Force watermark: handover document + compaction (tokens). */
135
142
  wmForceTokens: z.number().min(3000).default(150000).volatile(),
136
- /** Per-call read budget for read/glob/grep (tokens). */
143
+ /** Per-TURN read budget (inspection C16 — the name predates the turn
144
+ * accounting): read/glob/grep/bash results accumulate against it within
145
+ * one turn; the account resets after 15 idle minutes and the pre/post
146
+ * gates act at 2.4× this value (CEILING_MULTIPLE in context-watch.js). */
137
147
  wmReadBudgetTokens: z.number().min(200).default(3000).volatile(),
138
148
  /** Hard-tier behavior: cap reads and advise, or deny outright. */
139
149
  wmDenyMode: z.union(["cap", "deny"]).default("cap").volatile(),
@@ -188,7 +198,7 @@ export const Config = z.object({
188
198
  * kind fails open to the fallback chain. */
189
199
  intentClassifierEndpoint: z.string().default("").volatile(),
190
200
  /** Model name sent to the decision endpoint for kind "systemone"
191
- * (free text, e.g. tev1:4b-mxfp8). Independent of the llm provider
201
+ * (free text, e.g. d3:4b-mxfp8). Independent of the llm provider
192
202
  * catalog — no provider registration needed. Empty = unset; that
193
203
  * kind fails open to the fallback chain. */
194
204
  intentClassifierSystemoneModel: z.string().default("").volatile(),
@@ -238,26 +248,63 @@ export const Config = z.object({
238
248
  trimGrowthTokens: z.number().min(1000).default(22500).volatile(),
239
249
  /** Growth gate companion: the contiguous compressible increment must
240
250
  * also reach this many estimated tokens (chars/4 heuristic). */
241
- trimIncrementTokens: z.number().min(1000).default(50000).volatile(),
251
+ trimIncrementTokens: z.number().min(1000).default(25000).volatile(),
242
252
  /** Pressure tier (tier b): fold when view usage reaches this percent of
243
253
  * the context window (requires a resolvable window; otherwise skipped). */
244
- trimPressurePercent: z.number().min(1).max(100).default(60).volatile(),
254
+ trimPressurePercent: z.number().min(1).max(100).default(65).volatile(),
245
255
  /** Emergency tier (tier c): forced MECHANICAL fold (no side-model call)
246
256
  * at this percent of the window; cooldown-gated. */
247
257
  trimEmergencyPercent: z.number().min(1).max(100).default(95).volatile(),
248
- /** Effective-window cap for the tier math: tier points are computed
258
+ /** [2026-11-07]-[growth/increment constants are absolute tokens
259
+ * calibrated for the default 150k effective window]-[scaling
260
+ * trimWindowTokens down without scaling trimGrowthTokens /
261
+ * trimIncrementTokens proportionally makes the growth tier unreachable
262
+ * (a 30k window can never accumulate a 50k increment; observed live:
263
+ * growth fired once while emergency fired 17 times). Rule of thumb:
264
+ * increment ≈ window/6, growth ≈ window × 0.15 (~25k/22.5k at 150k,
265
+ * 2026-11-09 steady-growth recalibration — one fold per ~22.5k of new
266
+ * work instead of one every ~2 minutes; pressure pins the 50-100k band
267
+ * ceiling).]
268
+ * Effective-window cap for the tier math: tier points are computed
249
269
  * against min(model-resolved window, cap). 0 = auto (cap follows
250
270
  * wmForceTokens, so folds always fire ahead of the handover tier). */
251
271
  trimWindowTokens: z.number().min(0).default(0).volatile(),
252
272
  /** Last N messages of a history are never folded (the live tail the
253
273
  * next model call reasons over). */
254
- trimProtectedTail: z.number().min(0).max(100).default(6).volatile(),
274
+ trimProtectedTail: z.number().min(0).max(100).default(10).volatile(),
255
275
  /** Side-model fold cadence cap per rolling hour (emergency mechanical
256
- * folds are exempt; they have their own cooldown). */
257
- trimMaxFoldsPerHour: z.number().min(1).max(60).default(6).volatile(),
276
+ * folds are exempt; they have their own cooldown). 30 sits just above
277
+ * the pure T1 demand of continuous full-rate generation (~27 folds/hour
278
+ * at the 22.5k growth cadence and ~170 tok/s output), so it only bites
279
+ * on runaway churn; at the old cap of 16 growth consumed the whole
280
+ * budget and the ladder starved. */
281
+ trimMaxFoldsPerHour: z.number().min(1).max(60).default(30).volatile(),
258
282
  /** Route for the side-model summarizer (L3). Empty = inherit the
259
283
  * session's current model. */
260
284
  trimSideModel: IntentRouteSchema.default({ provider: "", model: "" }).volatile(),
285
+ /** Route for the fold-fallback summarizer. When the primary side route
286
+ * (trimSideModel or the inherited session model) fails, raw folds retry
287
+ * on this route before degrading to a mechanical fold; ladder folds
288
+ * retry but never go mechanical. Pick a route with headroom on cooldown,
289
+ * risk-control, and concurrency limits. Empty = no fallback leg. */
290
+ trimFoldFallbackModel: IntentRouteSchema.default({ provider: "", model: "" }).volatile(),
291
+ /** T1/T2/T3 ladder folding (docs/ladder-fold.md): master switch for the
292
+ * T2/T3 distillation tiers. T1 raw-range folds are governed by
293
+ * trimMode; the ladder only adds the summary-maintenance tiers. */
294
+ trimLadderEnabled: z.boolean().default(true).volatile(),
295
+ /** Ladder T2 gate: minimum active-T1 summary token mass before a T2
296
+ * distillation fires (the need signal — bili #379: block count alone
297
+ * is not a need signal). */
298
+ trimLadderT2Tokens: z.number().min(1000).default(8000).volatile(),
299
+ /** Ladder T2 companion floor: a T2 distillation merges at least this
300
+ * many active T1 blocks (filters degenerate two-block rewrites). */
301
+ trimLadderT2MinBlocks: z.number().min(2).max(200).default(3).volatile(),
302
+ /** Ladder T3 gate: minimum active-T2 summary token mass before a T3
303
+ * condensation fires. */
304
+ trimLadderT3Tokens: z.number().min(1000).default(6000).volatile(),
305
+ /** Ladder T3 companion floor: a T3 condensation merges at least this
306
+ * many active T2 blocks. */
307
+ trimLadderT3MinBlocks: z.number().min(2).max(200).default(2).volatile(),
261
308
  /** Explicit ledger state directory. Empty = <dshHome>/.dsh-switchman/
262
309
  * state/trim/. */
263
310
  trimStateDir: z.string().default("").volatile(),
@@ -159,7 +159,11 @@ export function resolveInheritance(parentLedger, parentId, childId, childMessage
159
159
  version: LEDGER_VERSION,
160
160
  sessionId: childId,
161
161
  blocks: blocks.map((block) => {
162
- const clone = { ...block, refRange: { ...block.refRange } };
162
+ // [2026-11-07]-[v2 ladder blocks carry refRange: null]-[spreading
163
+ // null produced {} on the clone, which slipped past the view's
164
+ // `refRange !== null` T1 filter and inflated the unlocated count
165
+ // (a phantom epoch signal); ladder clones must keep null]
166
+ const clone = { ...block, refRange: block.refRange !== null && block.refRange !== undefined ? { ...block.refRange } : null };
163
167
  delete clone.surfaceSeqs;
164
168
  delete clone.surfaceReplaced;
165
169
  delete clone.surfaceSkip;