dsh-switchman 1.11.6 → 1.11.7

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/client.js CHANGED
@@ -190,15 +190,16 @@ window.__ModuleLoader__.load({
190
190
  { field: 'poolReview', manualField: 'poolReviewManual', effortsField: 'poolReviewEfforts', lane: 'review' },
191
191
  ];
192
192
 
193
- /** Numeric watermark fields: settings field, schema minimum, and the
194
- * Host-side default (also the fallback for malformed stored values). */
193
+ /** Numeric watermark fields: settings field, schema bounds, and the
194
+ * Host-side default (also the fallback for malformed stored values).
195
+ * Bounds MUST mirror host/routes.js WM_BOUNDED_NUMBER_FIELDS. */
195
196
  const WM_NUMBER_FIELDS = [
196
- { field: 'wmSoftTokens', min: 1000, def: 80000 },
197
- { field: 'wmHardTokens', min: 2000, def: 95000 },
198
- { field: 'wmForceTokens', min: 3000, def: 150000 },
199
- { field: 'wmReadBudgetTokens', min: 200, def: 3000 },
200
- { field: 'wmSubagentForceTokens', min: 0, def: 0 },
201
- { field: 'wmSubagentWindowRatio', min: 0.1, def: 0.75 },
197
+ { field: 'wmSoftTokens', min: 1000, max: 10000000, def: 80000 },
198
+ { field: 'wmHardTokens', min: 2000, max: 10000000, def: 100000 },
199
+ { field: 'wmForceTokens', min: 3000, max: 10000000, def: 200000 },
200
+ { field: 'wmReadBudgetTokens', min: 200, max: 10000000, def: 3500 },
201
+ { field: 'wmSubagentForceTokens', min: 0, max: 10000000, def: 0 },
202
+ { field: 'wmSubagentWindowRatio', min: 0.1, max: 1, def: 0.75 },
202
203
  ];
203
204
 
204
205
  /** The three thresholds that must keep a strict ascending order. */
@@ -234,7 +235,7 @@ window.__ModuleLoader__.load({
234
235
  { field: 'intentTimeoutMs', min: 200, max: Infinity, def: 60000 },
235
236
  { field: 'intentHistoryMessages', min: 1, max: 20, def: 6 },
236
237
  { field: 'intentHistoryMaxChars', min: 200, max: 20000, def: 2400 },
237
- { field: 'intentContextWindow', min: 1024, max: Infinity, def: 200000 },
238
+ { field: 'intentContextWindow', min: 1024, max: Infinity, def: 250000 },
238
239
  ];
239
240
 
240
241
  /** Dictionary keys per numeric intent row. */
@@ -276,13 +277,14 @@ window.__ModuleLoader__.load({
276
277
  * write (and vice versa). */
277
278
  const TRIM_NUMBER_FIELDS = [
278
279
  { field: 'trimGrowthTokens', min: 1000, def: 22500 },
279
- { field: 'trimIncrementTokens', min: 1000, def: 25000 },
280
- { field: 'trimPressurePercent', min: 5, max: 100, def: 65 },
281
- { field: 'trimEmergencyPercent', min: 5, max: 100, def: 95 },
282
- { field: 'trimAdvisoryPercent', min: 5, max: 100, def: 65 },
283
- { field: 'trimAdvisoryCriticalPercent', min: 5, max: 100, def: 95 },
280
+ { field: 'trimIncrementTokens', min: 1000, def: 45000 },
281
+ { field: 'trimPressurePercent', min: 5, max: 100, def: 45 },
282
+ { field: 'trimEmergencyPercent', min: 5, max: 100, def: 90 },
283
+ { field: 'trimAdvisoryPercent', min: 5, max: 100, def: 50 },
284
+ { field: 'trimAdvisoryCriticalPercent', min: 5, max: 100, def: 80 },
285
+ { field: 'trimAdvisoryRepeatMs', min: 5000, max: 3600000, def: 120000 },
284
286
  { field: 'trimWindowTokens', min: 0, def: 0 },
285
- { field: 'trimProtectedTail', min: 0, max: 100, def: 10 },
287
+ { field: 'trimProtectedTail', min: 0, max: 100, def: 6 },
286
288
  { field: 'trimMaxFoldsPerHour', min: 1, max: 60, def: 30 },
287
289
  { field: 'trimLadderT2Tokens', min: 1000, def: 8000 },
288
290
  { field: 'trimLadderT2MinBlocks', min: 2, max: 200, def: 3 },
@@ -313,6 +315,10 @@ window.__ModuleLoader__.load({
313
315
  label: 'trimAdvisoryCriticalLabel',
314
316
  help: 'trimAdvisoryCriticalHelp',
315
317
  },
318
+ trimAdvisoryRepeatMs: {
319
+ label: 'trimAdvisoryRepeatLabel',
320
+ help: 'trimAdvisoryRepeatHelp',
321
+ },
316
322
  trimWindowTokens: {
317
323
  label: 'trimWindowLabel',
318
324
  help: 'trimWindowHelp',
@@ -361,6 +367,7 @@ window.__ModuleLoader__.load({
361
367
  'trimEnabled',
362
368
  'trimLadderEnabled',
363
369
  'trimProtectTaskIntent',
370
+ 'trimCriticalRelief',
364
371
  'surfaceFold',
365
372
  'trimSideModel',
366
373
  'trimFoldFallbackModel',
@@ -541,6 +548,8 @@ window.__ModuleLoader__.load({
541
548
  trimEnabled: bool('trimEnabled', true),
542
549
  trimLadderEnabled: bool('trimLadderEnabled', true),
543
550
  trimProtectTaskIntent: bool('trimProtectTaskIntent', true),
551
+ trimCriticalRelief: bool('trimCriticalRelief', true),
552
+ trimVerboseLog: bool('trimVerboseLog', false),
544
553
  surfaceFold: bool('surfaceFold', true),
545
554
  trimSideModel:
546
555
  sanitizeRoute(value?.trimSideModel, true) ??
@@ -630,6 +639,8 @@ window.__ModuleLoader__.load({
630
639
  trimEnabled: values.trimEnabled,
631
640
  trimLadderEnabled: values.trimLadderEnabled,
632
641
  trimProtectTaskIntent: values.trimProtectTaskIntent,
642
+ trimCriticalRelief: values.trimCriticalRelief,
643
+ trimVerboseLog: values.trimVerboseLog,
633
644
  surfaceFold: values.surfaceFold,
634
645
  trimSideModel: `${routeKey(values.trimSideModel)}\0${values.trimSideModel.effort ?? ''}`,
635
646
  trimFoldFallbackModel: `${routeKey(values.trimFoldFallbackModel)}\0${values.trimFoldFallbackModel.effort ?? ''}`,
@@ -903,6 +914,7 @@ window.__ModuleLoader__.load({
903
914
  wmReadBudgetLabel: '单次读取预算',
904
915
  wmReadBudgetHelp: '每次读取的 token 预算;每轮上限为其两倍。',
905
916
  wmNumberInvalid: '请输入不小于 {min} 的整数。',
917
+ wmNumberInvalidMax: '请输入 {min} 到 {max} 之间的数。',
906
918
  wmOrderInvalid: '三档水位需严格递增:soft < hard < force。',
907
919
  wmDenyModeLabel: '硬档行为',
908
920
  wmDenyModeCap: '限流放行',
@@ -928,6 +940,16 @@ window.__ModuleLoader__.load({
928
940
  trimModeLabel: '自动裁剪模式',
929
941
  trimModeHelp:
930
942
  '空闲时何时把历史折叠为摘要:off = 从不折叠,advisory = 模型自主折叠(压力达线时引擎在系统提示注入一次性提醒),auto = 自动折叠。',
943
+ trimCriticalReliefLabel: '临界救援折叠',
944
+ trimCriticalReliefHelp: '开启后(默认),advisory 模式压力到达临界线(trimAdvisoryCriticalPercent)时引擎自动执行一次机械折叠(90 秒冷却),不再只依赖提示。',
945
+ trimVerboseLogLabel: '调试模式(详细日志)',
946
+ trimVerboseLogHelp: '默认关闭。开启后引擎的每个执行步骤、闸门判定与决策路径都会写入 decisions.log(debug 行),便于问题追溯;关闭时只记录关键决策行。无论开关与否,异常始终记录完整细节。',
947
+ trimDockLabel: '折叠 {pressure}%(线 {line}%)',
948
+ trimDockTipDomain: '折叠引擎口径:最近一次实付 prompt(max(view, wire))÷ 有效窗口(含上限)——不是输入框旁上下文面板的预测口径,两者本就不同、不应收敛。',
949
+ trimDockTipMass: '实付 {mass} / 窗口 {window} tokens',
950
+ trimDockTipMode: '模式:{mode}(advisory=模型自行折叠;auto=引擎自动折叠)',
951
+ trimDockTipReady: '临界纾缓就绪:达到 80% 线即由引擎机械折叠一次',
952
+ trimDockTipCooldown: '临界纾缓冷却中,剩 {seconds}s',
931
953
  trimEnabledLabel: '启用自动裁剪',
932
954
  trimEnabledHelp: '上下文裁剪层的总开关。',
933
955
  trimLadderEnabledLabel: '阶梯折叠(T1/T2/T3)',
@@ -969,6 +991,8 @@ window.__ModuleLoader__.load({
969
991
  trimAdvisoryHelp: '仅 advisory 模式:压力达到窗口的这一百分比时,提醒模型自行折叠。',
970
992
  trimAdvisoryCriticalLabel: 'Advisory 升级线(%)',
971
993
  trimAdvisoryCriticalHelp: '仅 advisory 模式:达到这一百分比时,提醒升级为本轮必须折叠的指令。',
994
+ trimAdvisoryRepeatLabel: 'Advisory 重贴间隔(ms)',
995
+ trimAdvisoryRepeatHelp: '仅 advisory 模式:提醒贴在会话尾部后,每隔该毫秒数在新尾部重贴一次(临界档取其一半,下限 45 秒);默认 120000。',
972
996
  trimModeOff: '关闭',
973
997
  trimWindowLabel: '窗口上限(token)',
974
998
  trimWindowHelp: '压力与紧急档按 min(模型窗口, 上限) 计算;0 = 跟随强制水位(wmForceTokens)。',
@@ -1178,6 +1202,7 @@ window.__ModuleLoader__.load({
1178
1202
  wmReadBudgetLabel: 'Read budget',
1179
1203
  wmReadBudgetHelp: 'Token budget per read; each round caps at twice this.',
1180
1204
  wmNumberInvalid: 'Enter a whole number of {min} or more.',
1205
+ wmNumberInvalidMax: 'Enter a number between {min} and {max}.',
1181
1206
  wmOrderInvalid: 'The three tiers must strictly ascend: soft < hard < force.',
1182
1207
  wmDenyModeLabel: 'Hard-tier behavior',
1183
1208
  wmDenyModeCap: 'Cap and warn',
@@ -1203,6 +1228,16 @@ window.__ModuleLoader__.load({
1203
1228
  trimModeLabel: 'Trim mode',
1204
1229
  trimModeHelp:
1205
1230
  'When to fold history into summaries at idle: off = never, advisory = the model folds itself (the engine appends a one-off nudge line at the pressure line), auto = automatic folds.',
1231
+ trimCriticalReliefLabel: 'Critical relief fold',
1232
+ trimCriticalReliefHelp: 'When on (default), advisory mode performs one mechanical fold itself at the critical line (trimAdvisoryCriticalPercent, 90 s cooldown) instead of only nudging.',
1233
+ trimVerboseLogLabel: 'Debug mode (verbose log)',
1234
+ trimVerboseLogHelp: 'Off by default. When on, every engine step, gate evaluation and decision path is written to decisions.log (debug rows) for issue tracing; when off, only the key decision rows are kept. Exceptions always log full detail regardless of this switch.',
1235
+ trimDockLabel: 'fold {pressure}% (line {line}%)',
1236
+ trimDockTipDomain: 'Fold-engine domain: last PAID prompt (max of view/wire) over the effective window (cap included) — not the composer context meter projection; the two answer different questions and are not meant to converge.',
1237
+ trimDockTipMass: 'paid {mass} / window {window} tokens',
1238
+ trimDockTipMode: 'mode: {mode} (advisory = the model folds itself; auto = the engine folds)',
1239
+ trimDockTipReady: 'critical relief ready: one mechanical engine fold fires at the critical line',
1240
+ trimDockTipCooldown: 'critical relief cooling down, {seconds}s left',
1206
1241
  trimEnabledLabel: 'Enable auto-trim',
1207
1242
  trimEnabledHelp: 'Master switch for the context-trim layer.',
1208
1243
  trimLadderEnabledLabel: 'Ladder folding (T1/T2/T3)',
@@ -1252,6 +1287,8 @@ window.__ModuleLoader__.load({
1252
1287
  trimAdvisoryHelp: 'Advisory mode only: the model is nudged to fold itself at this percent of the window.',
1253
1288
  trimAdvisoryCriticalLabel: 'Advisory critical line (%)',
1254
1289
  trimAdvisoryCriticalHelp: 'Advisory mode only: at this percent the nudge escalates to a fold-down-this-turn order.',
1290
+ trimAdvisoryRepeatLabel: 'Advisory re-pin interval (ms)',
1291
+ trimAdvisoryRepeatHelp: 'Advisory mode only: after a nudge is pinned to the session tail, it re-pins at the new tail every this many ms (critical band halves it, 45 s floor). Default 120000.',
1255
1292
  trimWindowLabel: 'Window cap (tokens)',
1256
1293
  trimWindowHelp: 'Pressure and emergency tiers use min(model window, cap); 0 = follow wmForceTokens.',
1257
1294
  trimModeOff: 'Off',
@@ -1464,6 +1501,7 @@ window.__ModuleLoader__.load({
1464
1501
  wmReadBudgetLabel: '單次讀取預算',
1465
1502
  wmReadBudgetHelp: '每次讀取的 token 預算;每輪上限為其兩倍。',
1466
1503
  wmNumberInvalid: '請輸入不小於 {min} 的整數。',
1504
+ wmNumberInvalidMax: '請輸入 {min} 到 {max} 之間的數。',
1467
1505
  wmOrderInvalid: '三檔水位需嚴格遞增:soft < hard < force。',
1468
1506
  wmDenyModeLabel: '硬檔行為',
1469
1507
  wmDenyModeCap: '限流放行',
@@ -1486,6 +1524,16 @@ window.__ModuleLoader__.load({
1486
1524
  trimModeLabel: '自動裁剪模式',
1487
1525
  trimModeHelp:
1488
1526
  '閒置時何時將歷史摺疊為摘要:off = 從不摺疊,advisory = 模型自主摺疊(達壓力線時引擎於系統提示注入一次性提醒),auto = 自動摺疊。',
1527
+ trimCriticalReliefLabel: '臨界救援摺疊',
1528
+ trimCriticalReliefHelp: '開啟後(預設),advisory 模式壓力到達臨界線(trimAdvisoryCriticalPercent)時引擎自動執行一次機械摺疊(90 秒冷卻),不再只依賴提示。',
1529
+ trimVerboseLogLabel: '調試模式(詳細日誌)',
1530
+ trimVerboseLogHelp: '預設關閉。開啟後引擎的每個執行步驟、閘門判定與決策路徑都會寫入 decisions.log(debug 行),便於問題追溯;關閉時只記錄關鍵決策行。無論開關與否,異常始終記錄完整細節。',
1531
+ trimDockLabel: '摺疊 {pressure}%(線 {line}%)',
1532
+ trimDockTipDomain: '摺疊引擎口徑:最近一次實付 prompt(max(view, wire))÷ 有效視窗(含上限)——不是輸入框旁上下文面板的預測口徑,兩者本就不同、不應收斂。',
1533
+ trimDockTipMass: '實付 {mass} / 視窗 {window} tokens',
1534
+ trimDockTipMode: '模式:{mode}(advisory=模型自行摺疊;auto=引擎自動摺疊)',
1535
+ trimDockTipReady: '臨界紓緩就緒:達到 80% 線即由引擎機械摺疊一次',
1536
+ trimDockTipCooldown: '臨界紓緩冷卻中,剩 {seconds}s',
1489
1537
  trimEnabledLabel: '啟用自動裁剪',
1490
1538
  trimEnabledHelp: '上下文裁剪層的總開關。',
1491
1539
  trimLadderEnabledLabel: '階梯摺疊(T1/T2/T3)',
@@ -1718,6 +1766,7 @@ window.__ModuleLoader__.load({
1718
1766
  wmReadBudgetLabel: '読み取り予算',
1719
1767
  wmReadBudgetHelp: '1 回の読み取りあたりの token 予算です。各ラウンドの上限はその 2 倍です。',
1720
1768
  wmNumberInvalid: '{min} 以上の整数を入力してください。',
1769
+ wmNumberInvalidMax: '{min} から {max} の間の数値を入力してください。',
1721
1770
  wmOrderInvalid: '3 段階の水位は厳密に昇順にしてください:soft < hard < force。',
1722
1771
  wmDenyModeLabel: 'ハード段階の動作',
1723
1772
  wmDenyModeCap: '制限して警告',
@@ -1740,6 +1789,16 @@ window.__ModuleLoader__.load({
1740
1789
  trimModeLabel: 'トリムモード',
1741
1790
  trimModeHelp:
1742
1791
  'アイドル時に履歴を要約へ折りたたむタイミング:off = 折りたたまない、advisory = モデルが自発的に折りたたむ(圧力しきい値でエンジンがシステムプロンプトに一度だけ注意喚起を追記)、auto = 自動で折りたたむ。',
1792
+ trimCriticalReliefLabel: 'クリティカル救済フォールド',
1793
+ trimCriticalReliefHelp: 'オン(既定)では、advisory モードで圧力が臨界線(trimAdvisoryCriticalPercent)に達した時点でエンジン自身が機械的フォールドを 1 回実行します(90 秒クールダウン)。',
1794
+ trimVerboseLogLabel: 'デバッグモード(詳細ログ)',
1795
+ trimVerboseLogHelp: '既定ではオフ。オンにすると、エンジンの各ステップ・ゲート判定・決定経路が decisions.log に(debug 行として)記録され、問題の追跡に役立ちます。オフ時は主要な決定行のみ記録。例外はこの設定に関係なく常に完全な詳細を記録します。',
1796
+ trimDockLabel: 'fold {pressure}%(線 {line}%)',
1797
+ trimDockTipDomain: 'fold エンジンの基準:最後に支払った prompt(max(view, wire))÷ 実効ウィンドウ(上限込み)——入力欄のコンテキストメーターの推定基準とは別物で、収束しないのが正常です。',
1798
+ trimDockTipMass: '支払い済み {mass} / ウィンドウ {window} トークン',
1799
+ trimDockTipMode: 'モード:{mode}(advisory=モデル自身が fold、auto=エンジンが fold)',
1800
+ trimDockTipReady: 'critical relief 待機中:critical 線に達するとエンジンが 1 回機械的に fold します',
1801
+ trimDockTipCooldown: 'critical relief クールダウン中、残り {seconds}秒',
1743
1802
  trimEnabledLabel: '自動トリムを有効化',
1744
1803
  trimEnabledHelp: 'コンテキストトリム層のマスタースイッチ。',
1745
1804
  trimGrowthLabel: '増加しきい値(token)',
@@ -1960,6 +2019,7 @@ window.__ModuleLoader__.load({
1960
2019
  wmReadBudgetLabel: '읽기 예산',
1961
2020
  wmReadBudgetHelp: '한 번 읽기당 토큰 예산입니다. 매 라운드 상한은 그 두 배입니다.',
1962
2021
  wmNumberInvalid: '{min} 이상의 정수를 입력하세요.',
2022
+ wmNumberInvalidMax: '{min}부터 {max}까지의 숫자를 입력하세요.',
1963
2023
  wmOrderInvalid: '세 수위는 엄격히 증가해야 합니다: soft < hard < force.',
1964
2024
  wmDenyModeLabel: '하드 단계 동작',
1965
2025
  wmDenyModeCap: '제한하고 경고',
@@ -1989,6 +2049,16 @@ window.__ModuleLoader__.load({
1989
2049
  trimModeLabel: '트림 모드',
1990
2050
  trimModeHelp:
1991
2051
  '유휴 시 언제 기록을 요약으로 접을지: off = 접지 않음, advisory = 모델이 스스로 접음(압력 임계값에서 엔진이 시스템 프롬프트에 일회성 알림 추가), auto = 자동 접기.',
2052
+ trimCriticalReliefLabel: '임계 구제 폴드',
2053
+ trimCriticalReliefHelp: '켜면(기본) advisory 모드에서 압력이 임계선(trimAdvisoryCriticalPercent)에 닿을 때 엔진이 직접 기계 폴드를 한 번 실행합니다(90초 쿨다운).',
2054
+ trimVerboseLogLabel: '디버그 모드(상세 로그)',
2055
+ trimVerboseLogHelp: '기본값은 끔입니다. 켜면 엔진의 모든 실행 단계, 게이트 판정, 결정 경로가 decisions.log에(debug 행) 기록되어 문제 추적에 도움이 됩니다. 끄면 핵심 결정 행만 기록됩니다. 예외는 이 설정과 무관하게 항상 전체 세부 정보를 기록합니다.',
2056
+ trimDockLabel: 'fold {pressure}%(선 {line}%)',
2057
+ trimDockTipDomain: 'fold 엔진 기준: 마지막 실제 결제 prompt(max(view, wire)) ÷ 실효 윈도우(상한 포함) — 입력창 컨텍스트 미터의 추정 기준과 다르며 수렴하지 않는 것이 정상입니다.',
2058
+ trimDockTipMass: '결제 {mass} / 윈도우 {window} 토큰',
2059
+ trimDockTipMode: '모드: {mode} (advisory=모델이 직접 fold, auto=엔진이 fold)',
2060
+ trimDockTipReady: 'critical relief 대기 중: critical 선 도달 시 엔진이 한 번 기계적으로 fold합니다',
2061
+ trimDockTipCooldown: 'critical relief 쿨다운 중, {seconds}초 남음',
1992
2062
  trimEnabledLabel: '자동 트림 사용',
1993
2063
  trimEnabledHelp: '컨텍스트 트림 계층의 마스터 스위치.',
1994
2064
  trimGrowthLabel: '증가 임계값(token)',
@@ -2201,6 +2271,7 @@ window.__ModuleLoader__.load({
2201
2271
  wmReadBudgetLabel: 'Lese-Budget',
2202
2272
  wmReadBudgetHelp: 'Token-Budget pro Lesevorgang; jede Runde ist auf das Doppelte gedeckelt.',
2203
2273
  wmNumberInvalid: 'Geben Sie eine ganze Zahl von mindestens {min} ein.',
2274
+ wmNumberInvalidMax: 'Geben Sie eine Zahl zwischen {min} und {max} ein.',
2204
2275
  wmOrderInvalid: 'Die drei Stufen müssen strikt aufsteigen: soft < hard < force.',
2205
2276
  wmDenyModeLabel: 'Verhalten der Hard-Stufe',
2206
2277
  wmDenyModeCap: 'Deckeln und warnen',
@@ -2230,6 +2301,16 @@ window.__ModuleLoader__.load({
2230
2301
  trimModeLabel: 'Trim-Modus',
2231
2302
  trimModeHelp:
2232
2303
  'Wann der Verlauf im Leerlauf zu Zusammenfassungen gefaltet wird: off = nie, advisory = das Modell faltet selbst (der Motor hängt eine einmalige Erinnerung an den Systemprompt an), auto = automatisches Falten.',
2304
+ trimCriticalReliefLabel: 'Kritische Notfaltung',
2305
+ trimCriticalReliefHelp: 'Ein (Standard): Erreicht der Druck im Advisory-Modus die kritische Linie (trimAdvisoryCriticalPercent), faltet die Engine selbst einmal mechanisch (90 s Abklingzeit).',
2306
+ trimVerboseLogLabel: 'Debugmodus (ausführliches Protokoll)',
2307
+ trimVerboseLogHelp: 'Standardmäßig aus. Bei Aktivierung werden jeder Engine-Schritt, jede Gate-Prüfung und jeder Entscheidungspfad in die decisions.log geschrieben (Debug-Zeilen) zur Fehlerverfolgung; bei Deaktivierung bleiben nur die wichtigsten Entscheidungszeilen. Ausnahmen werden unabhängig von dieser Einstellung immer mit vollständigen Details protokolliert.',
2308
+ trimDockLabel: 'Falten {pressure}% (Linie {line}%)',
2309
+ trimDockTipDomain: 'Fold-Engine-Domäne: zuletzt bezahlter Prompt (max aus view/wire) über das effektive Fenster (inkl. Limit) — nicht die Projektion des Kontextmessers; beide beantworten verschiedene Fragen und sollen nicht konvergieren.',
2310
+ trimDockTipMass: 'bezahlt {mass} / Fenster {window} Token',
2311
+ trimDockTipMode: 'Modus: {mode} (advisory = das Modell faltet selbst; auto = die Engine faltet)',
2312
+ trimDockTipReady: 'Critical Relief bereit: an der Critical-Linie faltet die Engine einmal mechanisch',
2313
+ trimDockTipCooldown: 'Critical-Relief-Cooldown, noch {seconds}s',
2233
2314
  trimEnabledLabel: 'Auto-Trim aktivieren',
2234
2315
  trimEnabledHelp: 'Hauptschalter für die Kontext-Trim-Schicht.',
2235
2316
  trimGrowthLabel: 'Wachstumsschwelle (Token)',
@@ -2445,6 +2526,7 @@ window.__ModuleLoader__.load({
2445
2526
  wmReadBudgetLabel: 'Presupuesto de lectura',
2446
2527
  wmReadBudgetHelp: 'Presupuesto de tokens por lectura; cada ronda se limita al doble.',
2447
2528
  wmNumberInvalid: 'Introduce un número entero de {min} o más.',
2529
+ wmNumberInvalidMax: 'Introduzca un número entre {min} y {max}.',
2448
2530
  wmOrderInvalid: 'Los tres niveles deben ascender estrictamente: soft < hard < force.',
2449
2531
  wmDenyModeLabel: 'Comportamiento del nivel duro',
2450
2532
  wmDenyModeCap: 'Limitar y avisar',
@@ -2474,6 +2556,16 @@ window.__ModuleLoader__.load({
2474
2556
  trimModeLabel: 'Modo de recorte',
2475
2557
  trimModeHelp:
2476
2558
  'Cuándo plegar el historial en resúmenes en reposo: off = nunca, advisory = el modelo pliega por sí mismo (el motor añade un aviso puntual al system prompt), auto = plegado automático.',
2559
+ trimCriticalReliefLabel: 'Alivio crítico mecánico',
2560
+ trimCriticalReliefHelp: 'Activado (predeterminado): en modo advisory, al llegar a la línea crítica (trimAdvisoryCriticalPercent), el motor pliega mecánicamente una vez por sí mismo (enfriamiento de 90 s).',
2561
+ trimVerboseLogLabel: 'Modo de depuración (registro detallado)',
2562
+ trimVerboseLogHelp: 'Desactivado por defecto. Al activarlo, cada paso del motor, evaluación de compuerta y ruta de decisión se escribe en decisions.log (líneas debug) para rastrear problemas; al desactivarlo solo se guardan las líneas de decisión clave. Las excepciones siempre se registran con detalle completo, independientemente de esta opción.',
2563
+ trimDockLabel: 'plegar {pressure}% (línea {line}%)',
2564
+ trimDockTipDomain: 'Dominio del motor de plegado: último prompt pagado (máx de view/wire) sobre la ventana efectiva (límite incluido) — no la proyección del medidor de contexto; ambos responden preguntas distintas y no están hechos para converger.',
2565
+ trimDockTipMass: 'pagado {mass} / ventana {window} tokens',
2566
+ trimDockTipMode: 'modo: {mode} (advisory = el modelo pliega; auto = pliega el motor)',
2567
+ trimDockTipReady: 'alivio crítico listo: en la línea crítica el motor pliega una vez mecánicamente',
2568
+ trimDockTipCooldown: 'alivio crítico enfriando, faltan {seconds}s',
2477
2569
  trimEnabledLabel: 'Activar recorte automático',
2478
2570
  trimEnabledHelp:
2479
2571
  'Interruptor maestro de la capa de recorte de contexto.',
@@ -2690,6 +2782,7 @@ window.__ModuleLoader__.load({
2690
2782
  wmReadBudgetLabel: 'Budget de lecture',
2691
2783
  wmReadBudgetHelp: 'Budget de tokens par lecture ; chaque tour plafonne au double.',
2692
2784
  wmNumberInvalid: 'Saisissez un nombre entier d’au moins {min}.',
2785
+ wmNumberInvalidMax: 'Saisissez un nombre compris entre {min} et {max}.',
2693
2786
  wmOrderInvalid: 'Les trois paliers doivent strictement monter : soft < hard < force.',
2694
2787
  wmDenyModeLabel: 'Comportement au palier hard',
2695
2788
  wmDenyModeCap: 'Plafonner et avertir',
@@ -2719,6 +2812,16 @@ window.__ModuleLoader__.load({
2719
2812
  trimModeLabel: 'Mode de rognage',
2720
2813
  trimModeHelp:
2721
2814
  'Quand replier l’historique en résumés au repos : off = jamais, advisory = le modèle replie lui-même (le moteur ajoute un rappel ponctuel au system prompt), auto = repli automatique.',
2815
+ trimCriticalReliefLabel: 'Secours mécanique critique',
2816
+ trimCriticalReliefHelp: 'Activé (par défaut) : en mode advisory, quand la pression atteint la ligne critique (trimAdvisoryCriticalPercent), le moteur plie lui-même une fois mécaniquement (délai de 90 s).',
2817
+ trimVerboseLogLabel: 'Mode débogage (journal détaillé)',
2818
+ trimVerboseLogHelp: 'Désactivé par défaut. Une fois activé, chaque étape du moteur, chaque évaluation de garde et chaque chemin de décision est consigné dans decisions.log (lignes debug) pour faciliter le traçage ; sinon, seules les lignes de décision clés sont conservées. Les exceptions sont toujours journalisées en détail, quoi qu’il arrive.',
2819
+ trimDockLabel: 'replier {pressure}% (ligne {line}%)',
2820
+ trimDockTipDomain: 'Domaine du moteur de repli : dernier prompt payé (max de view/wire) sur la fenêtre effective (plafond inclus) — pas la projection du compteur de contexte ; les deux répondent à des questions différentes et ne sont pas faits pour converger.',
2821
+ trimDockTipMass: 'payé {mass} / fenêtre {window} jetons',
2822
+ trimDockTipMode: 'mode : {mode} (advisory = le modèle replie lui-même ; auto = le moteur replie)',
2823
+ trimDockTipReady: 'soulagement critique prêt : à la ligne critique, le moteur replie une fois mécaniquement',
2824
+ trimDockTipCooldown: 'soulagement critique en refroidissement, {seconds}s restantes',
2722
2825
  trimEnabledLabel: 'Activer le rognage automatique',
2723
2826
  trimEnabledHelp:
2724
2827
  'Interrupteur principal de la couche de rognage de contexte.',
@@ -2935,6 +3038,7 @@ window.__ModuleLoader__.load({
2935
3038
  wmReadBudgetLabel: 'Budget di lettura',
2936
3039
  wmReadBudgetHelp: 'Budget di token per lettura; ogni turno arriva al massimo al doppio.',
2937
3040
  wmNumberInvalid: 'Inserisci un numero intero di almeno {min}.',
3041
+ wmNumberInvalidMax: 'Inserisci un numero compreso tra {min} e {max}.',
2938
3042
  wmOrderInvalid: 'Le tre soglie devono salire rigorosamente: soft < hard < force.',
2939
3043
  wmDenyModeLabel: 'Comportamento alla soglia hard',
2940
3044
  wmDenyModeCap: 'Tetto e avviso',
@@ -2964,6 +3068,16 @@ window.__ModuleLoader__.load({
2964
3068
  trimModeLabel: 'Modalità di riduzione',
2965
3069
  trimModeHelp:
2966
3070
  'Quando comprimere la cronologia in riepiloghi a riposo: off = mai, advisory = il modello comprime da solo (il motore aggiunge un promemoria una tantum al system prompt), auto = riduzione automatica.',
3071
+ trimCriticalReliefLabel: 'Soccorso critico meccanico',
3072
+ trimCriticalReliefHelp: 'Attivo (predefinito): in modalità advisory, al raggiungimento della linea critica (trimAdvisoryCriticalPercent), il motore esegue una piegatura meccanica da sé (cooldown di 90 s).',
3073
+ trimVerboseLogLabel: 'Modalità debug (log dettagliato)',
3074
+ trimVerboseLogHelp: 'Disattivato per impostazione predefinita. Se attivo, ogni passo del motore, ogni valutazione di gate e ogni percorso decisionale viene scritto in decisions.log (righe debug) per la tracciabilità dei problemi; se disattivo restano solo le righe decisionali chiave. Le eccezioni vengono sempre registrate per intero, indipendentemente da questa opzione.',
3075
+ trimDockLabel: 'ripiega {pressure}% (linea {line}%)',
3076
+ trimDockTipDomain: 'Dominio del motore di ripiegamento: ultimo prompt pagato (max di view/wire) sulla finestra effettiva (limite incluso) — non la proiezione del misuratore di contesto; i due rispondono a domande diverse e non sono fatti per convergere.',
3077
+ trimDockTipMass: 'pagato {mass} / finestra {window} token',
3078
+ trimDockTipMode: 'modalità: {mode} (advisory = il modello ripiega da solo; auto = ripiega il motore)',
3079
+ trimDockTipReady: 'sollievo critico pronto: alla linea critica il motore ripiega una volta meccanicamente',
3080
+ trimDockTipCooldown: 'sollievo critico in raffreddamento, {seconds}s rimanenti',
2967
3081
  trimEnabledLabel: 'Attiva riduzione automatica',
2968
3082
  trimEnabledHelp:
2969
3083
  'Interruttore principale del livello di riduzione del contesto.',
@@ -3180,6 +3294,7 @@ window.__ModuleLoader__.load({
3180
3294
  wmReadBudgetLabel: 'Orçamento de leitura',
3181
3295
  wmReadBudgetHelp: 'Orçamento de tokens por leitura; cada turno se limita ao dobro disso.',
3182
3296
  wmNumberInvalid: 'Informe um número inteiro de {min} ou mais.',
3297
+ wmNumberInvalidMax: 'Informe um número entre {min} e {max}.',
3183
3298
  wmOrderInvalid: 'Os três limiares devem subir estritamente: soft < hard < force.',
3184
3299
  wmDenyModeLabel: 'Comportamento no limiar hard',
3185
3300
  wmDenyModeCap: 'Limitar e avisar',
@@ -3209,6 +3324,16 @@ window.__ModuleLoader__.load({
3209
3324
  trimModeLabel: 'Modo de corte',
3210
3325
  trimModeHelp:
3211
3326
  'Quando dobrar o histórico em resumos na ociosidade: off = nunca, advisory = o modelo dobra por si (o motor acrescenta um aviso pontual ao system prompt), auto = dobra automática.',
3327
+ trimCriticalReliefLabel: 'Alívio crítico mecânico',
3328
+ trimCriticalReliefHelp: 'Ligado (padrão): no modo advisory, ao atingir a linha crítica (trimAdvisoryCriticalPercent), o motor dobra mecanicamente uma vez por si (arrefecimento de 90 s).',
3329
+ trimVerboseLogLabel: 'Modo de depuração (registro detalhado)',
3330
+ trimVerboseLogHelp: 'Desligado por padrão. Ativado, cada passo do motor, avaliação de portão e caminho de decisão é gravado no decisions.log (linhas debug) para rastrear problemas; desligado, apenas as linhas-chave de decisão são mantidas. Exceções sempre registram detalhes completos, independentemente desta opção.',
3331
+ trimDockLabel: 'dobrar {pressure}% (linha {line}%)',
3332
+ trimDockTipDomain: 'Domínio do motor de dobra: último prompt pago (máx de view/wire) sobre a janela efetiva (limite incluído) — não a projeção do medidor de contexto; ambos respondem perguntas diferentes e não estão feitos para convergir.',
3333
+ trimDockTipMass: 'pago {mass} / janela {window} tokens',
3334
+ trimDockTipMode: 'modo: {mode} (advisory = o modelo dobra; auto = o motor dobra)',
3335
+ trimDockTipReady: 'alívio crítico pronto: na linha crítica o motor dobra uma vez mecanicamente',
3336
+ trimDockTipCooldown: 'alívio crítico em arrefecimento, faltam {seconds}s',
3212
3337
  trimEnabledLabel: 'Ativar corte automático',
3213
3338
  trimEnabledHelp:
3214
3339
  'Interruptor principal da camada de corte de contexto.',
@@ -3425,6 +3550,7 @@ window.__ModuleLoader__.load({
3425
3550
  wmReadBudgetLabel: 'Бюджет чтения',
3426
3551
  wmReadBudgetHelp: 'Бюджет токенов на одно чтение; каждый ход ограничен удвоенным значением.',
3427
3552
  wmNumberInvalid: 'Введите целое число не меньше {min}.',
3553
+ wmNumberInvalidMax: 'Введите число от {min} до {max}.',
3428
3554
  wmOrderInvalid: 'Три линии должны строго возрастать: soft < hard < force.',
3429
3555
  wmDenyModeLabel: 'Поведение на линии hard',
3430
3556
  wmDenyModeCap: 'Урезать и предупредить',
@@ -3454,6 +3580,16 @@ window.__ModuleLoader__.load({
3454
3580
  trimModeLabel: 'Режим обрезки',
3455
3581
  trimModeHelp:
3456
3582
  'Когда сворачивать историю в сводки в простое: off = никогда, advisory = модель сворачивает сама (движок добавляет разовое напоминание в системный промпт), auto = автоматически.',
3583
+ trimCriticalReliefLabel: 'Критическое авто-сворачивание',
3584
+ trimCriticalReliefHelp: 'Включено (по умолчанию): в режиме advisory при достижении критической линии (trimAdvisoryCriticalPercent) движок сам один раз сворачивает механически (куладдаун 90 с).',
3585
+ trimVerboseLogLabel: 'Режим отладки (подробный журнал)',
3586
+ trimVerboseLogHelp: 'По умолчанию выключено. При включении каждый шаг движка, проверка шлюза и путь решения записываются в decisions.log (строки debug) для отслеживания проблем; при выключении сохраняются только ключевые строки решений. Исключения всегда записываются с полными деталями независимо от этого переключателя.',
3587
+ trimDockLabel: 'свернуть {pressure}% (линия {line}%)',
3588
+ trimDockTipDomain: 'Область движка сворачивания: последний оплаченный prompt (max от view/wire) делённый на эффективное окно (с лимитом) — не прогноз счётчика контекста; эти числа отвечают на разные вопросы и не должны сходиться.',
3589
+ trimDockTipMass: 'оплачено {mass} / окно {window} токенов',
3590
+ trimDockTipMode: 'режим: {mode} (advisory = модель сворачивает сама; auto = сворачивает движок)',
3591
+ trimDockTipReady: 'критическое relief готово: у критической линии движок один раз свернёт механически',
3592
+ trimDockTipCooldown: 'критическое relief на кулдауне, осталось {seconds}с',
3457
3593
  trimEnabledLabel: 'Включить автообрезку',
3458
3594
  trimEnabledHelp: 'Главный выключатель слоя обрезки контекста.',
3459
3595
  trimGrowthLabel: 'Порог роста (токенов)',
@@ -4054,6 +4190,147 @@ window.__ModuleLoader__.load({
4054
4190
  );
4055
4191
  }
4056
4192
 
4193
+ /** Composer dock chip for trim pressure (conversation.input.dock,
4194
+ * order 88 — one slot below the intent chip's 89): shows the fold
4195
+ * engine's own pressure figure — the LAST PAID prompt (max of view/
4196
+ * wire) over the effective window (cap included) — next to the
4197
+ * composer. This is the number the trim nudges and tiers act on, and
4198
+ * it is deliberately NOT the composer context meter's projection:
4199
+ * different numerator (paid vs projected) and denominator (effective
4200
+ * window vs model window), so the two figures answer different
4201
+ * questions and legitimately diverge (the tooltip spells this out).
4202
+ * Polls the Host trim-pressure route every 15s; disabled trim, an
4203
+ * unknown window, or any fetch failure renders null (fail-silent).
4204
+ * Pure display — no interactive controls. */
4205
+ function TrimDockEntry({ t, sessionId, useProjection }) {
4206
+ const [snap, setSnap] = React.useState(null);
4207
+ // Realtime trigger ([2026-11-15] user report: after a fold ships the chip
4208
+ // kept the pre-fold percentage for up to one 15s poll — the Host snapshot
4209
+ // recomputes per request, so the lag was entirely client-side). The
4210
+ // contextPressure projection face folds EVERY surface event, including
4211
+ // the trim engine's fold-commit replacements, so its value changes the
4212
+ // moment a fold lands; it is also stable-primitive-safe to observe with
4213
+ // useSyncExternalStore (the shipped ContextMeter does exactly this).
4214
+ // Subscribing to that face makes the poll loop fetch immediately on
4215
+ // each pressure change (fold commit AND normal turn growth) and keeps
4216
+ // the 15s timer only as the drift fallback (cooldown countdown, model
4217
+ // window changes — inputs the projection does not see).
4218
+ const projectionHook =
4219
+ typeof useProjection === 'function' ? useProjection : () => undefined;
4220
+ const pressureFace = projectionHook('contextPressure');
4221
+ const pressureSignal =
4222
+ pressureFace == null
4223
+ ? null
4224
+ : `${pressureFace.projectedTokens ?? pressureFace.pressureTokens ?? ''}|${pressureFace.contextWindow ?? ''}`;
4225
+ const [pulse, setPulse] = React.useState(0);
4226
+ React.useEffect(() => {
4227
+ setPulse((n) => n + 1);
4228
+ }, [pressureSignal]);
4229
+ React.useEffect(() => {
4230
+ let stopped = false;
4231
+ let timer = null;
4232
+ const query =
4233
+ typeof sessionId === 'string' && sessionId !== ''
4234
+ ? `?session=${encodeURIComponent(sessionId)}`
4235
+ : '';
4236
+ const tick = async () => {
4237
+ let next = null;
4238
+ try {
4239
+ const reply = await api.get(`trim-pressure${query}`);
4240
+ const value = reply?.ok ? reply.value : null;
4241
+ if (
4242
+ value &&
4243
+ typeof value === 'object' &&
4244
+ value.available === true &&
4245
+ typeof value.pressure === 'number' &&
4246
+ typeof value.window === 'number' &&
4247
+ value.window > 0
4248
+ )
4249
+ next = {
4250
+ mode: value.mode === 'advisory' || value.mode === 'auto' ? value.mode : null,
4251
+ pressure: Math.round(value.pressure),
4252
+ mass: typeof value.mass === 'number' ? value.mass : null,
4253
+ window: value.window,
4254
+ advisoryLine:
4255
+ typeof value.advisoryLine === 'number' ? value.advisoryLine : null,
4256
+ criticalLine:
4257
+ typeof value.criticalLine === 'number' ? value.criticalLine : null,
4258
+ reliefCooldown:
4259
+ typeof value.reliefCooldownRemaining === 'number'
4260
+ ? Math.max(0, Math.round(value.reliefCooldownRemaining / 1000))
4261
+ : null,
4262
+ reliefReady: value.reliefReady === true,
4263
+ };
4264
+ } catch {
4265
+ next = null;
4266
+ }
4267
+ if (stopped) return;
4268
+ setSnap(next);
4269
+ timer = setTimeout(tick, 15_000);
4270
+ };
4271
+ void tick();
4272
+ return () => {
4273
+ stopped = true;
4274
+ if (timer !== null) clearTimeout(timer);
4275
+ };
4276
+ }, [sessionId, pulse]);
4277
+ if (snap === null) return null;
4278
+ // Same dock-column formula as the sibling chips: keep the chip's
4279
+ // left edge tracking the centered composer card.
4280
+ const tipLines = [
4281
+ t('trimDockTipDomain'),
4282
+ snap.mass !== null
4283
+ ? t('trimDockTipMass', { mass: snap.mass, window: snap.window })
4284
+ : null,
4285
+ snap.mode !== null ? t('trimDockTipMode', { mode: snap.mode }) : null,
4286
+ snap.reliefCooldown !== null && snap.reliefCooldown > 0
4287
+ ? t('trimDockTipCooldown', { seconds: snap.reliefCooldown })
4288
+ : null,
4289
+ snap.reliefReady ? t('trimDockTipReady') : null,
4290
+ ].filter((line) => line !== null);
4291
+ const nearCritical =
4292
+ snap.criticalLine !== null && snap.pressure >= snap.criticalLine;
4293
+ const nearLine =
4294
+ !nearCritical &&
4295
+ snap.advisoryLine !== null &&
4296
+ snap.pressure >= snap.advisoryLine;
4297
+ return h(
4298
+ 'div',
4299
+ {
4300
+ 'data-dsh-switchman': 'trim-dock-column',
4301
+ style: {
4302
+ boxSizing: 'border-box',
4303
+ width: 'calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px))',
4304
+ maxWidth: 'var(--dsh-composer-card-max-width, 780px)',
4305
+ margin: '0 auto',
4306
+ flex: 'none',
4307
+ display: 'flex',
4308
+ flexDirection: 'column',
4309
+ gap: 'var(--dsh-composer-stack-gap, 6px)',
4310
+ },
4311
+ },
4312
+ h(
4313
+ 'p',
4314
+ {
4315
+ 'data-dsh-switchman': 'trim-dock-chip',
4316
+ title: tipLines.join(' · '),
4317
+ style: {
4318
+ margin: '0',
4319
+ padding: '2px 0',
4320
+ color: nearCritical
4321
+ ? 'var(--dsw-alias-state-error-primary, inherit)'
4322
+ : nearLine
4323
+ ? 'var(--dsw-alias-state-business-primary, inherit)'
4324
+ : 'var(--dsw-alias-label-tertiary, inherit)',
4325
+ fontSize: '12px',
4326
+ lineHeight: '1.6',
4327
+ },
4328
+ },
4329
+ '⚙ ',
4330
+ t('trimDockLabel', { pressure: snap.pressure, line: snap.advisoryLine }),
4331
+ ),
4332
+ );
4333
+ }
4057
4334
  /** Composer dock chip for intent routing (conversation.input.dock,
4058
4335
  * order 89 — one slot above the vision entry's 90): shows where the
4059
4336
  * session's auto-selected pseudo-model currently routes AND the
@@ -5269,7 +5546,7 @@ window.__ModuleLoader__.load({
5269
5546
  (values.langConversation !== '' &&
5270
5547
  !isCandidate(values.langConversation));
5271
5548
  /** Per-field numeric validity: raw text (when present) must be a
5272
- * whole number at or above the schema minimum. The trim and intent rows
5549
+ * plain number within the schema bounds. The trim and intent rows
5273
5550
  * share the same raw-text overlay and save gate. */
5274
5551
  const numberInvalid = Object.fromEntries(
5275
5552
  [
@@ -5282,7 +5559,7 @@ window.__ModuleLoader__.load({
5282
5559
  return [
5283
5560
  field,
5284
5561
  raw !== undefined &&
5285
- (!/^\d+$/.test(raw.trim()) ||
5562
+ (!/^\d+(?:\.\d+)?$/.test(raw.trim()) ||
5286
5563
  Number(raw.trim()) < min ||
5287
5564
  Number(raw.trim()) > (max ?? Infinity)),
5288
5565
  ];
@@ -5317,12 +5594,13 @@ window.__ModuleLoader__.load({
5317
5594
  setFailed(false);
5318
5595
  }
5319
5596
 
5320
- /** Number-input onChange: stage the raw text, commit only whole
5321
- * numbers (the draft always holds parseable values). */
5597
+ /** Number-input onChange: stage the raw text, commit only plain
5598
+ * numbers (integers or decimal fractions — the draft always holds
5599
+ * parseable values). */
5322
5600
  function editNumber(field, raw) {
5323
5601
  setNumText((previous) => ({ ...previous, [field]: raw }));
5324
5602
  const trimmed = raw.trim();
5325
- if (/^\d+$/.test(trimmed)) edit(field, Number(trimmed));
5603
+ if (/^\d+(?:\.\d+)?$/.test(trimmed)) edit(field, Number(trimmed));
5326
5604
  }
5327
5605
 
5328
5606
  /** Number-input onBlur: drop the overlay so the display falls back
@@ -5539,6 +5817,8 @@ window.__ModuleLoader__.load({
5539
5817
  trimEnabled: values.trimEnabled,
5540
5818
  trimLadderEnabled: values.trimLadderEnabled,
5541
5819
  trimProtectTaskIntent: values.trimProtectTaskIntent,
5820
+ trimCriticalRelief: values.trimCriticalRelief,
5821
+ trimVerboseLog: values.trimVerboseLog,
5542
5822
  surfaceFold: values.surfaceFold,
5543
5823
  trimSideModel: {
5544
5824
  provider: values.trimSideModel.provider,
@@ -5837,12 +6117,17 @@ window.__ModuleLoader__.load({
5837
6117
  });
5838
6118
  };
5839
6119
 
5840
- /** Status lines under one number row: minimum + order hints. */
6120
+ /** Shared invalid-number hint: a finite max gets the range text. */
6121
+ const numberInvalidText = (spec) =>
6122
+ spec.max !== undefined && Number.isFinite(spec.max)
6123
+ ? t('wmNumberInvalidMax', { min: spec.min, max: spec.max })
6124
+ : t('wmNumberInvalid', { min: spec.min });
6125
+ /** Status lines under one number row: bounds + order hints. */
5841
6126
  const wmNumberLines = (spec) => {
5842
6127
  const lines = [];
5843
6128
  if (numberInvalid[spec.field])
5844
6129
  lines.push({
5845
- text: t('wmNumberInvalid', { min: spec.min }),
6130
+ text: numberInvalidText(spec),
5846
6131
  invalid: true,
5847
6132
  });
5848
6133
  if (orderInvalid && WM_ORDER_FIELDS.includes(spec.field))
@@ -5878,10 +6163,10 @@ window.__ModuleLoader__.load({
5878
6163
  });
5879
6164
  };
5880
6165
 
5881
- /** Status lines under one intent number row: minimum hint. */
6166
+ /** Status lines under one intent number row: bounds hint. */
5882
6167
  const intentNumberLines = (spec) =>
5883
6168
  numberInvalid[spec.field]
5884
- ? [{ text: t('wmNumberInvalid', { min: spec.min }), invalid: true }]
6169
+ ? [{ text: numberInvalidText(spec), invalid: true }]
5885
6170
  : [];
5886
6171
  /** One trim number input: the watermark rows' raw-text overlay
5887
6172
  * pattern under the trim section id (invalid text shows the
@@ -5910,10 +6195,10 @@ window.__ModuleLoader__.load({
5910
6195
  });
5911
6196
  };
5912
6197
 
5913
- /** Status lines under one trim number row: minimum hint. */
6198
+ /** Status lines under one trim number row: bounds hint. */
5914
6199
  const trimNumberLines = (spec) =>
5915
6200
  numberInvalid[spec.field]
5916
- ? [{ text: t('wmNumberInvalid', { min: spec.min }), invalid: true }]
6201
+ ? [{ text: numberInvalidText(spec), invalid: true }]
5917
6202
  : [];
5918
6203
 
5919
6204
  /** Intent option sets fed by the DSH model catalog (the same
@@ -7130,6 +7415,37 @@ window.__ModuleLoader__.load({
7130
7415
  style: STYLE.checkbox,
7131
7416
  }),
7132
7417
  lines: [],
7418
+ }),
7419
+ h(SettingsRow, {
7420
+ key: 'trimCriticalRelief',
7421
+ id: `${trimId}-relief`,
7422
+ label: t('trimCriticalReliefLabel'),
7423
+ help: t('trimCriticalReliefHelp'),
7424
+ control: h('input', {
7425
+ type: 'checkbox',
7426
+ id: `${trimId}-relief`,
7427
+ checked: values.trimCriticalRelief,
7428
+ onChange: () => {
7429
+ edit('trimCriticalRelief', !values.trimCriticalRelief);
7430
+ },
7431
+ disabled: !editable,
7432
+ style: STYLE.checkbox,
7433
+ }),
7434
+ key: 'trimVerboseLog',
7435
+ id: `${trimId}-verbose`,
7436
+ label: t('trimVerboseLogLabel'),
7437
+ help: t('trimVerboseLogHelp'),
7438
+ control: h('input', {
7439
+ type: 'checkbox',
7440
+ id: `${trimId}-verbose`,
7441
+ checked: values.trimVerboseLog,
7442
+ onChange: () => {
7443
+ edit('trimVerboseLog', !values.trimVerboseLog);
7444
+ },
7445
+ disabled: !editable,
7446
+ style: STYLE.checkbox,
7447
+ }),
7448
+ lines: [],
7133
7449
  }),
7134
7450
  h(SettingsRow, {
7135
7451
  key: 'surfaceFold',
@@ -7509,6 +7825,19 @@ window.__ModuleLoader__.load({
7509
7825
  },
7510
7826
  (kit) => h(IntentDockEntry, { ...kit, t }),
7511
7827
  ),
7828
+ ); // Trim-pressure chip (same dock list, order 88 — one slot below
7829
+ // the intent chip): the fold engine's paid-prompt pressure, the
7830
+ // number its nudges/tiers act on. Deliberately NOT the composer
7831
+ // context meter's projection — the tooltip explains the domains.
7832
+ ctx.slots.inject('conversation.input.dock', () =>
7833
+ ctx.slots.register(
7834
+ {
7835
+ name: 'conversation.input.dock',
7836
+ id: 'dsh-switchman-trim',
7837
+ order: 88,
7838
+ },
7839
+ (kit) => h(TrimDockEntry, { ...kit, t }),
7840
+ ),
7512
7841
  );
7513
7842
  // Composer model label (conversation.input.model.label): the
7514
7843
  // ui-model-selection shell dispatches EVERY model's label