dsh-switchman 1.11.3 → 1.11.5

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
@@ -373,14 +373,13 @@ window.__ModuleLoader__.load({
373
373
  const routeKey = (route) => `${route.provider}\0${route.model}`;
374
374
 
375
375
  /** One well-formed {provider, model[, effort]} route, or null. */
376
- function sanitizeRoute(route) {
376
+ function sanitizeRoute(route, allowEffort = false) {
377
377
  if (route === null || typeof route !== 'object') return null;
378
378
  if (typeof route.provider !== 'string' || route.provider === '') return null;
379
379
  if (typeof route.model !== 'string' || route.model === '') return null;
380
- // Trim side routes may carry an optional effort pin; keep it on the
381
- // round-trip so a settings save never strips it (intent routes omit it).
380
+ // Preserve effort only for settings routes that expose a dedicated pin.
382
381
  const out = { provider: route.provider, model: route.model };
383
- if (typeof route.effort === 'string' && route.effort !== '') out.effort = route.effort;
382
+ if (allowEffort && typeof route.effort === 'string' && route.effort !== '') out.effort = route.effort;
384
383
  return out;
385
384
  }
386
385
 
@@ -532,10 +531,10 @@ window.__ModuleLoader__.load({
532
531
  trimLadderEnabled: bool('trimLadderEnabled', true),
533
532
  surfaceFold: bool('surfaceFold', true),
534
533
  trimSideModel:
535
- sanitizeRoute(value?.trimSideModel) ??
534
+ sanitizeRoute(value?.trimSideModel, true) ??
536
535
  ({ provider: '', model: '' }),
537
536
  trimFoldFallbackModel:
538
- sanitizeRoute(value?.trimFoldFallbackModel) ??
537
+ sanitizeRoute(value?.trimFoldFallbackModel, true) ??
539
538
  ({ provider: '', model: '' }),
540
539
  trimStateDir: string('trimStateDir'),
541
540
  ...Object.fromEntries(
@@ -565,7 +564,7 @@ window.__ModuleLoader__.load({
565
564
  INTENT_NUMBER_FIELDS.map((spec) => [spec.field, number(spec)]),
566
565
  ),
567
566
  intentFallbackRoute:
568
- sanitizeRoute(value?.intentFallbackRoute) ??
567
+ sanitizeRoute(value?.intentFallbackRoute, true) ??
569
568
  ({ provider: '', model: '' }),
570
569
  };
571
570
  }
@@ -619,8 +618,8 @@ window.__ModuleLoader__.load({
619
618
  trimEnabled: values.trimEnabled,
620
619
  trimLadderEnabled: values.trimLadderEnabled,
621
620
  surfaceFold: values.surfaceFold,
622
- trimSideModel: routeKey(values.trimSideModel),
623
- trimFoldFallbackModel: routeKey(values.trimFoldFallbackModel),
621
+ trimSideModel: `${routeKey(values.trimSideModel)}\0${values.trimSideModel.effort ?? ''}`,
622
+ trimFoldFallbackModel: `${routeKey(values.trimFoldFallbackModel)}\0${values.trimFoldFallbackModel.effort ?? ''}`,
624
623
  trimStateDir: values.trimStateDir,
625
624
  ...Object.fromEntries(
626
625
  TRIM_NUMBER_FIELDS.map(({ field }) => [field, values[field]]),
@@ -645,7 +644,7 @@ window.__ModuleLoader__.load({
645
644
  ...Object.fromEntries(
646
645
  INTENT_NUMBER_FIELDS.map(({ field }) => [field, values[field]]),
647
646
  ),
648
- intentFallbackRoute: routeKey(values.intentFallbackRoute),
647
+ intentFallbackRoute: `${routeKey(values.intentFallbackRoute)}\0${values.intentFallbackRoute.effort ?? ''}`,
649
648
  });
650
649
  }
651
650
 
@@ -845,6 +844,7 @@ window.__ModuleLoader__.load({
845
844
  '开启后按列表顺序派发(首个即首选);未开启按能力排序自动排。',
846
845
  poolManualSelected: '已选 · 派发优先序',
847
846
  poolManualAddable: '可添加',
847
+ poolMoveTop: '登顶',
848
848
  poolMoveUp: '上移',
849
849
  poolMoveDown: '下移',
850
850
  poolRemoveOne: '移除',
@@ -922,11 +922,11 @@ window.__ModuleLoader__.load({
922
922
  surfaceFoldLabel: '把折叠写入会话表面',
923
923
  surfaceFoldHelp: '追加宿主风格的替换事件,让折叠在冻结派发下也能到达线上。建议保持开启。',
924
924
  trimSideModelLabel: '摘要模型',
925
- trimSideModelHelp: '折叠摘要使用的模型路由;默认「跟随会话模型」。阶梯蒸馏同样使用该路由。',
925
+ trimSideModelHelp: '折叠摘要使用的模型路由;默认「跟随会话模型」。阶梯蒸馏同样使用该路由。未手动指定思考等级时,按模型元数据自动选择最低开启档;只有开/关思考时取关闭。模型不支持思考或元数据不可用时,不发送思考等级并沿用模型默认。',
926
926
  trimFollowSession: '跟随会话模型',
927
- trimEffortPinLow: '默认(pin low)',
927
+ trimEffortRecommended: '自动(按模型推荐规则)',
928
928
  trimFoldFallbackModelLabel: '折叠异常兜底模型',
929
- trimFoldFallbackModelHelp: '摘要模型调用失败时改用该模型重试;仍失败则压力折叠退回机械折叠。尽量选择没有冷却、风控或并发限制的模型,避免与主会话共用同一账号。留空 = 无兜底。',
929
+ trimFoldFallbackModelHelp: '摘要模型调用失败时改用该模型重试;仍失败则压力折叠退回机械折叠。思考等级可单独指定;留空时按该模型的推荐规则自动选择。尽量选择没有冷却、风控或并发限制的模型,避免与主会话共用同一账号。留空 = 无兜底。',
930
930
  trimFallbackUnset: '未设置(失败时直接机械折叠)',
931
931
  trimStateDirLabel: '账本目录',
932
932
  trimStateDirHelp: '留空 = 默认的裁剪状态目录。',
@@ -1021,7 +1021,7 @@ window.__ModuleLoader__.load({
1021
1021
  intentHistoryMaxCharsHelp: '对话摘录的总字符上限(200–20000)。',
1022
1022
  intentFallbackRouteLabel: '兜底模型',
1023
1023
  intentFallbackRouteHelp:
1024
- '分类失败时依次:上一轮实际路由 → 兜底模型 → 候选第一位;留空用候选第一位。',
1024
+ '分类失败时依次:上一轮实际路由 → 兜底模型 → 候选第一位;留空用候选第一位。可选固定思考等级;不指定时使用兜底模型默认等级。',
1025
1025
  intentRouteUnset: '未设置',
1026
1026
  intentEffortFollow: '跟随模型默认',
1027
1027
  intentDockAuto: '自动',
@@ -1113,6 +1113,7 @@ window.__ModuleLoader__.load({
1113
1113
  'When on, dispatch follows the listed order (head = first choice); when off, lanes are ordered by capability automatically.',
1114
1114
  poolManualSelected: 'Selected · dispatch priority',
1115
1115
  poolManualAddable: 'Add models',
1116
+ poolMoveTop: 'Move to top',
1116
1117
  poolMoveUp: 'Move up',
1117
1118
  poolMoveDown: 'Move down',
1118
1119
  poolRemoveOne: 'Remove',
@@ -1192,13 +1193,12 @@ window.__ModuleLoader__.load({
1192
1193
  surfaceFoldHelp:
1193
1194
  'Append host-style replace events so folds reach the wire even on frozen dispatches. Leave on.',
1194
1195
  trimSideModelLabel: 'Summarizer model',
1195
- trimSideModelHelp:
1196
- 'Route for fold summaries; defaults to following the session model. Also used by ladder distillations.',
1196
+ trimSideModelHelp: 'Route for fold summaries; defaults to following the session model. Also used by ladder distillations. Without a pinned effort, choose the lowest enabled level from model metadata; for a thinking on/off-only model, choose off. If reasoning is unsupported or metadata is unavailable, omit the effort and keep the model default.',
1197
1197
  trimFollowSession: 'Follow session model',
1198
- trimEffortPinLow: 'Default (pin low)',
1198
+ trimEffortRecommended: 'Automatic (model recommendation)',
1199
1199
  trimFoldFallbackModelLabel: 'Fold fallback model',
1200
1200
  trimFoldFallbackModelHelp:
1201
- 'When the summary route fails, the fold retries on this model before degrading a pressure fold to a mechanical one. Prefer a model with headroom on cooldowns, risk control, and concurrency limits — avoid sharing the pinned account with the main session. Empty = no fallback leg.',
1201
+ 'When the summary route fails, the fold retries on this model before degrading a pressure fold to a mechanical one. Its effort can be pinned separately; if unset, the model recommendation is used. Prefer a model with headroom on cooldowns, risk control, and concurrency limits — avoid sharing the pinned account with the main session. Empty = no fallback leg.',
1202
1202
  trimFallbackUnset: 'Unset (failed folds go straight to mechanical)',
1203
1203
  trimStateDirLabel: 'Ledger directory',
1204
1204
  trimStateDirHelp: 'Empty = the default per-host trim state directory.',
@@ -1303,7 +1303,7 @@ window.__ModuleLoader__.load({
1303
1303
  'Total character cap of the conversation excerpt (200–20000).',
1304
1304
  intentFallbackRouteLabel: 'Fallback model',
1305
1305
  intentFallbackRouteHelp:
1306
- 'On classification failure: last routed model → fallback → first candidate; empty = first candidate.',
1306
+ 'On classification failure: last routed model → fallback → first candidate; empty = first candidate. Optionally pin a thinking effort; when unset, the fallback model default is used.',
1307
1307
  intentRouteUnset: 'Not set',
1308
1308
  intentEffortFollow: 'model default',
1309
1309
  intentDockAuto: 'Auto',
@@ -1397,6 +1397,7 @@ window.__ModuleLoader__.load({
1397
1397
  poolManualHelp: '開啟後按清單順序派發(首個即首選);未開啟按能力排序自動排。',
1398
1398
  poolManualSelected: '已選 · 派發優先序',
1399
1399
  poolManualAddable: '可新增',
1400
+ poolMoveTop: '登頂',
1400
1401
  poolMoveUp: '上移',
1401
1402
  poolMoveDown: '下移',
1402
1403
  poolRemoveOne: '移除',
@@ -1467,11 +1468,11 @@ window.__ModuleLoader__.load({
1467
1468
  surfaceFoldLabel: '把摺疊寫入會話表面',
1468
1469
  surfaceFoldHelp: '追加宿主風格的替換事件,讓摺疊在凍結派發下也能到達線上。建議保持開啟。',
1469
1470
  trimSideModelLabel: '摘要模型',
1470
- trimSideModelHelp: '摺疊摘要使用的模型路由;預設「跟隨對話模型」。階梯蒸餾同樣使用該路由。',
1471
+ trimSideModelHelp: '摺疊摘要使用的模型路由;預設「跟隨對話模型」。階梯蒸餾同樣使用該路由。未手動指定思考等級時,依模型中繼資料自動選擇最低啟用等級;模型僅提供思考開/關切換時取關閉。不支援思考或中繼資料不可用時,不傳思考等級並沿用模型預設。',
1471
1472
  trimFollowSession: '跟隨對話模型',
1472
- trimEffortPinLow: '預設(pin low)',
1473
+ trimEffortRecommended: '自動(依模型推薦規則)',
1473
1474
  trimFoldFallbackModelLabel: '摺疊異常備援模型',
1474
- trimFoldFallbackModelHelp: '摘要模型呼叫失敗時改用該模型重試;仍失敗則壓力摺疊退回機械摺疊。盡量選擇沒有冷卻、風控或並行限制的模型,避免與主對話共用同一帳號。留空 = 無備援。',
1475
+ trimFoldFallbackModelHelp: '摘要模型呼叫失敗時改用該模型重試;仍失敗則壓力摺疊退回機械摺疊。思考等級可單獨指定;留空時依該模型推薦規則自動選擇。盡量選擇沒有冷卻、風控或並行限制的模型,避免與主對話共用同一帳號。留空 = 無備援。',
1475
1476
  trimFallbackUnset: '未設定(失敗時直接機械摺疊)',
1476
1477
  trimStateDirLabel: '帳本目錄',
1477
1478
  trimStateDirHelp: '留空 = 預設的裁剪狀態目錄。',
@@ -1563,7 +1564,7 @@ window.__ModuleLoader__.load({
1563
1564
  intentHistoryMaxCharsHelp: '對話摘錄的總字元上限(200–20000)。',
1564
1565
  intentFallbackRouteLabel: '備援模型',
1565
1566
  intentFallbackRouteHelp:
1566
- '分類失敗時依次:上一輪實際路由 → 備援模型 → 候選第一位;留空用候選第一位。',
1567
+ '分類失敗時依次:上一輪實際路由 → 備援模型 → 候選第一位;留空用候選第一位。可選固定思考等級;未指定時使用備援模型預設等級。',
1567
1568
  intentRouteUnset: '未設定',
1568
1569
  intentEffortFollow: '跟隨模型預設',
1569
1570
  intentDockAuto: '自動',
@@ -1644,6 +1645,7 @@ window.__ModuleLoader__.load({
1644
1645
  poolManualHelp: 'オンにするとリストの順序でディスパッチします(先頭=第一候補)。オフなら能力順で自動的に並びます。',
1645
1646
  poolManualSelected: '選択済み · ディスパッチ優先順',
1646
1647
  poolManualAddable: 'モデルを追加',
1648
+ poolMoveTop: '先頭へ',
1647
1649
  poolMoveUp: '上へ',
1648
1650
  poolMoveDown: '下へ',
1649
1651
  poolRemoveOne: '削除',
@@ -1792,10 +1794,16 @@ window.__ModuleLoader__.load({
1792
1794
  '会話抜粋の合計文字上限(200–20000)。',
1793
1795
  intentFallbackRouteLabel: 'フォールバックモデル',
1794
1796
  intentFallbackRouteHelp:
1795
- '分類失敗時:前回ルート → フォールバック → 候補の先頭。空欄は候補の先頭。',
1797
+ '分類失敗時:前回ルート → フォールバック → 候補の先頭。空欄は候補の先頭。思考レベルを固定できます。未指定ならモデル既定を使用します。',
1796
1798
  intentRouteUnset: '未設定',
1797
1799
  intentEffortFollow: 'モデルのデフォルト',
1798
- trimEffortPinLow: 'デフォルト(pin low)',
1800
+ trimEffortRecommended: '自動(モデル推奨)',
1801
+ trimSideModelLabel: '要約モデル',
1802
+ trimSideModelHelp: '要約に使うモデルルート。既定ではセッションモデルに従いますが、その呼び出しの思考レベルは引き継ぎません。段階的蒸留も同じルートを使います。手動指定がなければ、モデル情報から有効な思考レベルの最低段階を選びます。思考のオン/オフだけのモデルは off を選択します。非対応またはモデル情報がない場合はレベルを送らず、モデルの既定値を使います。',
1803
+ trimFollowSession: 'セッションモデルに従う',
1804
+ trimFoldFallbackModelLabel: '折りたたみ時のフォールバックモデル',
1805
+ trimFoldFallbackModelHelp: '要約モデルの呼び出しに失敗した場合、このモデルで再試行します。これも失敗すると、圧力折りたたみは機械的な折りたたみに切り替わります。思考レベルは個別に指定でき、未指定ならこのモデルの推奨設定を使います。冷却・リスク管理・同時実行の制限に余裕のあるモデルを選び、メインセッションと同じアカウントの共有は避けてください。空欄 = フォールバックなし。',
1806
+ trimFallbackUnset: '未設定(失敗時は機械的な折りたたみに直接移行)',
1799
1807
  intentDockAuto: '自動',
1800
1808
  intentDockUnrouted: '未ルーティング',
1801
1809
  intentDockUsing: '使用中',
@@ -1875,6 +1883,7 @@ window.__ModuleLoader__.load({
1875
1883
  poolManualHelp: '켜면 목록 순서대로 디스패치합니다(맨 앞이 제1순위). 끄면 능력 순으로 자동 정렬됩니다.',
1876
1884
  poolManualSelected: '선택됨 · 디스패치 우선순위',
1877
1885
  poolManualAddable: '모델 추가',
1886
+ poolMoveTop: '맨 위로',
1878
1887
  poolMoveUp: '위로',
1879
1888
  poolMoveDown: '아래로',
1880
1889
  poolRemoveOne: '제거',
@@ -1934,7 +1943,13 @@ window.__ModuleLoader__.load({
1934
1943
  '서브에이전트 자체 모델 창에 맞춰 3단계 축소: force = min(정적 기준, 창×비율). soft/hard도 같은 비율로 축소. 완화 없이 강화만.',
1935
1944
  wmSubagentFollowForce: 'force 단계 따르기(0)',
1936
1945
  wmCommands: '명령: /ctx-pause 수위 개입 일시 중지 · /ctx-resume 재개 · /ctx-handover 지금 핸드오버 문서를 작성하고 압축.',
1937
- trimEffortPinLow: '기본 (low 고정)',
1946
+ trimEffortRecommended: '자동 (모델 권장)',
1947
+ trimSideModelLabel: '요약 모델',
1948
+ trimSideModelHelp: '요약에 사용할 모델 경로입니다. 기본값은 세션 모델을 따르지만 해당 호출의 추론 수준은 상속하지 않습니다. 단계별 증류도 같은 경로를 사용합니다. 수동 지정이 없으면 모델 메타데이터에서 활성화된 가장 낮은 추론 단계를 선택합니다. 추론 켜기/끄기만 지원하는 모델은 off를 선택합니다. 추론을 지원하지 않거나 메타데이터가 없으면 수준을 보내지 않고 모델 기본값을 사용합니다.',
1949
+ trimFollowSession: '세션 모델 따르기',
1950
+ trimFoldFallbackModelLabel: '접기 대체 모델',
1951
+ trimFoldFallbackModelHelp: '요약 모델 호출이 실패하면 이 모델로 재시도합니다. 이것도 실패하면 압력 접기는 기계적 접기로 전환됩니다. 추론 수준은 별도로 지정할 수 있으며, 비워 두면 이 모델의 권장 설정을 사용합니다. 쿨다운, 위험 제어, 동시성 제한에 여유가 있는 모델을 선택하고 주 세션과 같은 계정을 공유하지 않는 편이 좋습니다. 비우면 대체 경로가 없습니다.',
1952
+ trimFallbackUnset: '미설정(실패 시 바로 기계적 접기)',
1938
1953
  trimSectionTitle: '컨텍스트 트림',
1939
1954
  trimModeLabel: '트림 모드',
1940
1955
  trimModeHelp:
@@ -2023,7 +2038,7 @@ window.__ModuleLoader__.load({
2023
2038
  '대화 발췌의 총 문자 상한(200–20000).',
2024
2039
  intentFallbackRouteLabel: '폴백 모델',
2025
2040
  intentFallbackRouteHelp:
2026
- '분류 실패 시: 이전 라우트 → 폴백 → 첫 후보. 빈 값은 첫 후보.',
2041
+ '분류 실패 시: 이전 라우트 → 폴백 → 첫 후보. 비어 있으면 첫 후보입니다. 추론 수준을 선택적으로 고정할 수 있으며, 지정하지 않으면 폴백 모델 기본값을 따릅니다.',
2027
2042
  intentRouteUnset: '미설정',
2028
2043
  intentEffortFollow: '모델 기본값',
2029
2044
  intentDockAuto: '자동',
@@ -2105,6 +2120,7 @@ window.__ModuleLoader__.load({
2105
2120
  poolManualHelp: 'Ein: Der Versand folgt der gelisteten Reihenfolge (oben = erste Wahl); aus: Lanes werden automatisch nach Fähigkeit sortiert.',
2106
2121
  poolManualSelected: 'Ausgewählt · Dispatch-Priorität',
2107
2122
  poolManualAddable: 'Modelle hinzufügen',
2123
+ poolMoveTop: 'Ganz nach oben',
2108
2124
  poolMoveUp: 'Nach oben',
2109
2125
  poolMoveDown: 'Nach unten',
2110
2126
  poolRemoveOne: 'Entfernen',
@@ -2164,7 +2180,13 @@ window.__ModuleLoader__.load({
2164
2180
  'Skaliert die Stufen auf das eigene Modellfenster des Subagenten: force = min(statische Stufe, Fenster × Quote); soft/hard schrumpfen im selben Faktor. Verengt nur, lockert nie.',
2165
2181
  wmSubagentFollowForce: 'Der Force-Stufe folgen (0)',
2166
2182
  wmCommands: 'Befehle: /ctx-pause pausiert Wasserstand-Aktionen · /ctx-resume setzt sie fort · /ctx-handover schreibt das Übergabedokument jetzt und komprimiert sofort.',
2167
- trimEffortPinLow: 'Standard (pin low)',
2183
+ trimEffortRecommended: 'Automatisch (Modell-Empfehlung)',
2184
+ trimSideModelLabel: 'Zusammenfassungsmodell',
2185
+ trimSideModelHelp: 'Modellroute für Zusammenfassungen. Standardmäßig folgt sie dem Sitzungsmodell, übernimmt aber nicht dessen Denkstufe. Leiter-Destillationen verwenden dieselbe Route. Ohne manuelle Vorgabe wird anhand der Modellmetadaten die niedrigste aktivierte Denkstufe gewählt; bei Modellen mit reinem Ein/Aus-Schalter wird off gewählt. Bei fehlender Unterstützung oder Metadaten wird keine Denkstufe gesendet und der Modellstandard verwendet.',
2186
+ trimFollowSession: 'Sitzungsmodell folgen',
2187
+ trimFoldFallbackModelLabel: 'Fallback-Modell für Faltungen',
2188
+ trimFoldFallbackModelHelp: 'Wenn der Zusammenfassungsaufruf fehlschlägt, wird mit diesem Modell erneut versucht; schlägt auch das fehl, wird ein Druck-Fold mechanisch ausgeführt. Die Denkstufe kann unabhängig festgelegt werden; ohne Angabe greift die Modellempfehlung. Möglichst ein Modell ohne Cooldown-, Risiko- oder Parallelitätsbeschränkungen wählen und nicht dasselbe Konto wie die Hauptsitzung verwenden. Leer = kein Fallback.',
2189
+ trimFallbackUnset: 'Nicht festgelegt (bei Fehler direkt mechanisch falten)',
2168
2190
  trimSectionTitle: 'Kontext-Trim',
2169
2191
  trimModeLabel: 'Trim-Modus',
2170
2192
  trimModeHelp:
@@ -2256,7 +2278,7 @@ window.__ModuleLoader__.load({
2256
2278
  'Gesamtzeichengrenze des Gesprächsauszugs (200–20000).',
2257
2279
  intentFallbackRouteLabel: 'Fallback-Modell',
2258
2280
  intentFallbackRouteHelp:
2259
- 'Bei Klassifikationsfehler: zuletzt geroutet → Fallback → erster Kandidat; leer = erster Kandidat.',
2281
+ 'Bei Klassifikationsfehler: zuletzt geroutet → Fallback → erster Kandidat; leer = erster Kandidat. Der Thinking-Aufwand kann optional festgelegt werden; andernfalls gilt der Modellstandard des Fallbacks.',
2260
2282
  intentRouteUnset: 'Nicht festgelegt',
2261
2283
  intentEffortFollow: 'Modellstandard',
2262
2284
  intentDockAuto: 'Auto',
@@ -2338,6 +2360,7 @@ window.__ModuleLoader__.load({
2338
2360
  poolManualHelp: 'Activado, el despacho sigue el orden de la lista (el primero es la primera opción); desactivado, los carriles se ordenan automáticamente por capacidad.',
2339
2361
  poolManualSelected: 'Seleccionados · prioridad de despacho',
2340
2362
  poolManualAddable: 'Añadir modelos',
2363
+ poolMoveTop: 'Mover al principio',
2341
2364
  poolMoveUp: 'Subir',
2342
2365
  poolMoveDown: 'Bajar',
2343
2366
  poolRemoveOne: 'Quitar',
@@ -2397,7 +2420,13 @@ window.__ModuleLoader__.load({
2397
2420
  'Escala los niveles a la ventana del propio modelo del subagente: force = min(nivel estático, ventana × ratio); soft/hard se reducen por el mismo factor. Solo restringe, nunca amplía.',
2398
2421
  wmSubagentFollowForce: 'Seguir el nivel forzado (0)',
2399
2422
  wmCommands: 'Comandos: /ctx-pause suspende las acciones del nivel · /ctx-resume las reanuda · /ctx-handover escribe el documento y compacta ahora.',
2400
- trimEffortPinLow: 'Predeterminado (pin low)',
2423
+ trimEffortRecommended: 'Automático (recomendación del modelo)',
2424
+ trimSideModelLabel: 'Modelo de resumen',
2425
+ trimSideModelHelp: 'Ruta del modelo para los resúmenes. De forma predeterminada sigue el modelo de la sesión, pero no hereda su nivel de razonamiento. Las destilaciones por niveles usan la misma ruta. Sin una selección manual, los metadatos del modelo determinan el nivel habilitado más bajo; si solo ofrece activar o desactivar el razonamiento, se elige off. Si no admite razonamiento o faltan metadatos, no se envía ningún nivel y se usa el valor predeterminado del modelo.',
2426
+ trimFollowSession: 'Seguir el modelo de la sesión',
2427
+ trimFoldFallbackModelLabel: 'Modelo de respaldo para el plegado',
2428
+ trimFoldFallbackModelHelp: 'Si falla la llamada al modelo de resumen, se reintenta con este modelo; si también falla, el plegado por presión pasa a ser mecánico. El nivel de razonamiento se puede fijar por separado; si se deja vacío, se aplica la recomendación de este modelo. Elige un modelo con margen en límites de enfriamiento, riesgo y concurrencia, y evita compartir cuenta con la sesión principal. Vacío = sin respaldo.',
2429
+ trimFallbackUnset: 'Sin configurar (si falla, pasar directamente al plegado mecánico)',
2401
2430
  trimSectionTitle: 'Recorte de contexto',
2402
2431
  trimModeLabel: 'Modo de recorte',
2403
2432
  trimModeHelp:
@@ -2490,7 +2519,7 @@ window.__ModuleLoader__.load({
2490
2519
  'Límite total de caracteres del extracto de conversación (200–20000).',
2491
2520
  intentFallbackRouteLabel: 'Modelo de reserva',
2492
2521
  intentFallbackRouteHelp:
2493
- 'Si falla la clasificación: último modelo enrutado → reserva → primer candidato; vacío = primer candidato.',
2522
+ 'Si falla la clasificación: último modelo enrutado → reserva → primer candidato; vacío = primer candidato. Puedes fijar opcionalmente el nivel de razonamiento; si no, se usa el valor predeterminado del modelo de reserva.',
2494
2523
  intentRouteUnset: 'Sin establecer',
2495
2524
  intentEffortFollow: 'predeterminado del modelo',
2496
2525
  intentDockAuto: 'Auto',
@@ -2572,6 +2601,7 @@ window.__ModuleLoader__.load({
2572
2601
  poolManualHelp: 'Activé, le dispatch suit l’ordre affiché (la tête = premier choix) ; désactivé, les couloirs sont ordonnés par capacité automatiquement.',
2573
2602
  poolManualSelected: 'Sélectionnés · priorité de dispatch',
2574
2603
  poolManualAddable: 'Ajouter des modèles',
2604
+ poolMoveTop: 'Placer en tête',
2575
2605
  poolMoveUp: 'Monter',
2576
2606
  poolMoveDown: 'Descendre',
2577
2607
  poolRemoveOne: 'Retirer',
@@ -2631,7 +2661,13 @@ window.__ModuleLoader__.load({
2631
2661
  'Ajuste les paliers à la fenêtre propre du modèle du sous-agent : force = min(palier statique, fenêtre × ratio) ; soft/hard réduits du même facteur. Ne fait que resserrer, jamais relâcher.',
2632
2662
  wmSubagentFollowForce: 'Suivre le palier force (0)',
2633
2663
  wmCommands: 'Commandes : /ctx-pause suspend les actions de niveau d’eau · /ctx-resume les reprend · /ctx-handover écrit le document et compacte maintenant.',
2634
- trimEffortPinLow: 'Par défaut (pin low)',
2664
+ trimEffortRecommended: 'Automatique (recommandation du modèle)',
2665
+ trimSideModelLabel: 'Modèle de résumé',
2666
+ trimSideModelHelp: 'Route du modèle pour les résumés. Par défaut, elle suit le modèle de la session sans reprendre son niveau de raisonnement. Les distillations en cascade utilisent la même route. Sans choix manuel, les métadonnées sélectionnent le niveau activé le plus faible ; pour un modèle avec seulement un interrupteur de raisonnement, le niveau choisi est off. Si le raisonnement est indisponible ou que les métadonnées manquent, aucun niveau n’est envoyé et le défaut du modèle s’applique.',
2667
+ trimFollowSession: 'Suivre le modèle de la session',
2668
+ trimFoldFallbackModelLabel: 'Modèle de secours pour le pliage',
2669
+ trimFoldFallbackModelHelp: 'Si l’appel au modèle de résumé échoue, une nouvelle tentative utilise ce modèle ; en cas de nouvel échec, un pliage sous pression devient mécanique. Le niveau de raisonnement se règle séparément ; sans choix, la recommandation de ce modèle s’applique. Choisissez un modèle avec de la marge sur le refroidissement, le contrôle des risques et la concurrence, et évitez le même compte que la session principale. Vide = aucun secours.',
2670
+ trimFallbackUnset: 'Non défini (en cas d’échec, passer directement au pliage mécanique)',
2635
2671
  trimSectionTitle: 'Rognage du contexte',
2636
2672
  trimModeLabel: 'Mode de rognage',
2637
2673
  trimModeHelp:
@@ -2724,7 +2760,7 @@ window.__ModuleLoader__.load({
2724
2760
  'Plafond total de caractères de l’extrait de conversation (200–20000).',
2725
2761
  intentFallbackRouteLabel: 'Modèle de secours',
2726
2762
  intentFallbackRouteHelp:
2727
- 'En cas d’échec : dernier modèle routé → secours → premier candidat ; vide = premier candidat.',
2763
+ 'En cas d’échec : dernier modèle routé → secours → premier candidat ; vide = premier candidat. Vous pouvez épingler le niveau de raisonnement ; sinon, le niveau par défaut du modèle de secours est utilisé.',
2728
2764
  intentRouteUnset: 'Non défini',
2729
2765
  intentEffortFollow: 'par défaut du modèle',
2730
2766
  intentDockAuto: 'Auto',
@@ -2806,6 +2842,7 @@ window.__ModuleLoader__.load({
2806
2842
  poolManualHelp: 'Attivo, il dispatch segue l’ordine elencato (il primo della lista = prima scelta); spento, le corsie vengono ordinate per capacità automaticamente.',
2807
2843
  poolManualSelected: 'Selezionati · priorità di dispatch',
2808
2844
  poolManualAddable: 'Aggiungi modelli',
2845
+ poolMoveTop: 'Porta in cima',
2809
2846
  poolMoveUp: 'Sposta su',
2810
2847
  poolMoveDown: 'Sposta giù',
2811
2848
  poolRemoveOne: 'Rimuovi',
@@ -2865,7 +2902,13 @@ window.__ModuleLoader__.load({
2865
2902
  'Ridimensiona i livelli alla finestra del modello del subagente: force = min(livello statico, finestra × rapporto); soft/hard si riducono dello stesso fattore. Solo restrizione, mai allentamento.',
2866
2903
  wmSubagentFollowForce: 'Segui la soglia force (0)',
2867
2904
  wmCommands: 'Comandi: /ctx-pause sospende le azioni da livello dell’acqua · /ctx-resume le riprende · /ctx-handover scrive il documento e compatta subito.',
2868
- trimEffortPinLow: 'Predefinito (pin low)',
2905
+ trimEffortRecommended: 'Automatico (consigliato dal modello)',
2906
+ trimSideModelLabel: 'Modello di riepilogo',
2907
+ trimSideModelHelp: 'Percorso del modello per i riepiloghi. Per impostazione predefinita segue il modello della sessione, senza ereditarne il livello di ragionamento. Anche le distillazioni a livelli usano lo stesso percorso. Senza una scelta manuale, i metadati determinano il livello attivo più basso; per i modelli con il solo interruttore on/off si usa off. Se il ragionamento non è supportato o mancano i metadati, non viene inviato alcun livello e si usa il valore predefinito del modello.',
2908
+ trimFollowSession: 'Segui il modello della sessione',
2909
+ trimFoldFallbackModelLabel: 'Modello di riserva per la piegatura',
2910
+ trimFoldFallbackModelHelp: 'Se la chiamata al modello di riepilogo fallisce, si riprova con questo modello; se fallisce anche questo, la piegatura sotto pressione diventa meccanica. Il livello di ragionamento si può impostare separatamente; se vuoto, si usa la raccomandazione del modello. Scegli un modello con margine per cooldown, controllo del rischio e concorrenza, evitando di condividere l’account con la sessione principale. Vuoto = nessun fallback.',
2911
+ trimFallbackUnset: 'Non impostato (in caso di errore, passa direttamente alla piegatura meccanica)',
2869
2912
  trimSectionTitle: 'Riduzione del contesto',
2870
2913
  trimModeLabel: 'Modalità di riduzione',
2871
2914
  trimModeHelp:
@@ -2958,7 +3001,7 @@ window.__ModuleLoader__.load({
2958
3001
  'Limite totale di caratteri dell’estratto della conversazione (200–20000).',
2959
3002
  intentFallbackRouteLabel: 'Modello di riserva',
2960
3003
  intentFallbackRouteHelp:
2961
- 'In caso di errore: ultimo modello instradato → riserva → primo candidato; vuoto = primo candidato.',
3004
+ 'In caso di errore: ultimo modello instradato → riserva → primo candidato; vuoto = primo candidato. Puoi fissare il livello di ragionamento; se omesso, si usa il valore predefinito del modello di riserva.',
2962
3005
  intentRouteUnset: 'Non impostato',
2963
3006
  intentEffortFollow: 'predefinito del modello',
2964
3007
  intentDockAuto: 'Auto',
@@ -3040,6 +3083,7 @@ window.__ModuleLoader__.load({
3040
3083
  poolManualHelp: 'Ligado, o despacho segue a ordem listada (o primeiro = primeira escolha); desligado, as faixas são ordenadas por capacidade automaticamente.',
3041
3084
  poolManualSelected: 'Selecionados · prioridade de despacho',
3042
3085
  poolManualAddable: 'Adicionar modelos',
3086
+ poolMoveTop: 'Mover para o topo',
3043
3087
  poolMoveUp: 'Mover para cima',
3044
3088
  poolMoveDown: 'Mover para baixo',
3045
3089
  poolRemoveOne: 'Remover',
@@ -3099,7 +3143,13 @@ window.__ModuleLoader__.load({
3099
3143
  'Escala os níveis para a janela do próprio modelo do subagente: force = min(nível estático, janela × razão); soft/hard encolhem pelo mesmo fator. Apenas aperta, nunca afrouxa.',
3100
3144
  wmSubagentFollowForce: 'Seguir o limiar force (0)',
3101
3145
  wmCommands: 'Comandos: /ctx-pause suspende as ações de nível de água · /ctx-resume as retoma · /ctx-handover escreve o documento e compacta agora.',
3102
- trimEffortPinLow: 'Padrão (pin low)',
3146
+ trimEffortRecommended: 'Automático (recomendação do modelo)',
3147
+ trimSideModelLabel: 'Modelo de resumo',
3148
+ trimSideModelHelp: 'Rota do modelo para os resumos. Por padrão, segue o modelo da sessão, mas não herda o nível de raciocínio dessa chamada. As destilações em níveis usam a mesma rota. Sem escolha manual, os metadados selecionam o nível ativo mais baixo; para modelos com apenas a opção de ligar/desligar o raciocínio, usa-se off. Se o raciocínio não for compatível ou faltarem metadados, nenhum nível é enviado e vale o padrão do modelo.',
3149
+ trimFollowSession: 'Seguir o modelo da sessão',
3150
+ trimFoldFallbackModelLabel: 'Modelo de reserva para dobra',
3151
+ trimFoldFallbackModelHelp: 'Se a chamada do modelo de resumo falhar, tenta novamente com este modelo; se também falhar, a dobra por pressão passa a ser mecânica. O nível de raciocínio pode ser definido separadamente; se ficar vazio, usa-se a recomendação deste modelo. Escolha um modelo com margem para cooldown, controle de risco e concorrência, evitando compartilhar a conta com a sessão principal. Vazio = sem fallback.',
3152
+ trimFallbackUnset: 'Não configurado (em caso de falha, usar dobra mecânica diretamente)',
3103
3153
  trimSectionTitle: 'Corte de contexto',
3104
3154
  trimModeLabel: 'Modo de corte',
3105
3155
  trimModeHelp:
@@ -3192,7 +3242,7 @@ window.__ModuleLoader__.load({
3192
3242
  'Limite total de caracteres do trecho da conversa (200–20000).',
3193
3243
  intentFallbackRouteLabel: 'Modelo de reserva',
3194
3244
  intentFallbackRouteHelp:
3195
- 'Se a classificação falhar: último modelo roteado → reserva → primeiro candidato; vazio = primeiro candidato.',
3245
+ 'Se a classificação falhar: último modelo roteado → reserva → primeiro candidato; vazio = primeiro candidato. É possível fixar o nível de raciocínio; se não definido, usa-se o padrão do modelo de reserva.',
3196
3246
  intentRouteUnset: 'Não definido',
3197
3247
  intentEffortFollow: 'padrão do modelo',
3198
3248
  intentDockAuto: 'Auto',
@@ -3274,6 +3324,7 @@ window.__ModuleLoader__.load({
3274
3324
  poolManualHelp: 'Включено — диспетчеризация идёт по списку (голова списка = первый выбор); выключено — полосы упорядочиваются по силе автоматически.',
3275
3325
  poolManualSelected: 'Выбрано · приоритет диспетчеризации',
3276
3326
  poolManualAddable: 'Добавить модели',
3327
+ poolMoveTop: 'В начало',
3277
3328
  poolMoveUp: 'Выше',
3278
3329
  poolMoveDown: 'Ниже',
3279
3330
  poolRemoveOne: 'Убрать',
@@ -3333,7 +3384,13 @@ window.__ModuleLoader__.load({
3333
3384
  'Масштабирует уровни под собственное окно модели субагента: force = min(статический уровень, окно × доля); soft/hard сжимаются на тот же коэффициент. Только ужесточает, не смягчает.',
3334
3385
  wmSubagentFollowForce: 'Следовать линии force (0)',
3335
3386
  wmCommands: 'Команды: /ctx-pause приостанавливает действия по уровню воды · /ctx-resume возобновляет их · /ctx-handover записывает документ передачи и сжимает прямо сейчас.',
3336
- trimEffortPinLow: 'По умолчанию (pin low)',
3387
+ trimEffortRecommended: 'Авто (рекомендация модели)',
3388
+ trimSideModelLabel: 'Модель сводки',
3389
+ trimSideModelHelp: 'Маршрут модели для сводок. По умолчанию следует модели сессии, но не наследует уровень рассуждения этого вызова. Для ступенчатой дистилляции используется тот же маршрут. Без ручного выбора метаданные задают самый низкий включённый уровень; для модели только с переключателем рассуждений выбирается off. Если рассуждения не поддерживаются или метаданных нет, уровень не передаётся и используется значение модели по умолчанию.',
3390
+ trimFollowSession: 'Следовать модели сессии',
3391
+ trimFoldFallbackModelLabel: 'Резервная модель сворачивания',
3392
+ trimFoldFallbackModelHelp: 'Если вызов модели сводки завершается ошибкой, повтор выполняется на этой модели; при повторном сбое сворачивание под давлением становится механическим. Уровень рассуждения задаётся отдельно; если не указан, действует рекомендация этой модели. Выбирайте модель с запасом по охлаждению, контролю рисков и параллельным запросам и не используйте общий аккаунт с главной сессией. Пусто = без резервного маршрута.',
3393
+ trimFallbackUnset: 'Не задано (при сбое сразу перейти к механическому сворачиванию)',
3337
3394
  trimSectionTitle: 'Обрезка контекста',
3338
3395
  trimModeLabel: 'Режим обрезки',
3339
3396
  trimModeHelp:
@@ -3425,7 +3482,7 @@ window.__ModuleLoader__.load({
3425
3482
  'Общий лимит символов выборки диалога (200–20000).',
3426
3483
  intentFallbackRouteLabel: 'Резервная модель',
3427
3484
  intentFallbackRouteHelp:
3428
- 'При сбое классификации: прошлая модель → резерв → первый кандидат; пусто = первый кандидат.',
3485
+ 'При сбое классификации: прошлая модель → резерв → первый кандидат; пусто = первый кандидат. Можно задать уровень reasoning; если он не указан, используется значение резервной модели по умолчанию.',
3429
3486
  intentRouteUnset: 'Не задано',
3430
3487
  intentEffortFollow: 'по умолчанию модели',
3431
3488
  intentDockAuto: 'Авто',
@@ -4613,6 +4670,7 @@ window.__ModuleLoader__.load({
4613
4670
  onToggleManual,
4614
4671
  onSetEffort,
4615
4672
  onMove,
4673
+ onMoveTop,
4616
4674
  onRemove,
4617
4675
  onSelectAll,
4618
4676
  onClear,
@@ -4774,6 +4832,20 @@ window.__ModuleLoader__.load({
4774
4832
  h(
4775
4833
  'div',
4776
4834
  { style: STYLE.rankActions },
4835
+ h(
4836
+ 'button',
4837
+ {
4838
+ type: 'button',
4839
+ onClick: () => {
4840
+ onMoveTop(index);
4841
+ },
4842
+ disabled: !editable || index === 0,
4843
+ title: t('poolMoveTop'),
4844
+ 'aria-label': t('poolMoveTop'),
4845
+ style: STYLE.iconButton,
4846
+ },
4847
+ '⤒',
4848
+ ),
4777
4849
  h(
4778
4850
  'button',
4779
4851
  {
@@ -5268,6 +5340,18 @@ window.__ModuleLoader__.load({
5268
5340
  })));
5269
5341
  }
5270
5342
 
5343
+ /** Manual order: move the route at index to the head of the
5344
+ * pool in one step (boundary-guarded: a no-op when already first). */
5345
+ function movePoolRouteToTop(field, index) {
5346
+ const pool = values[field];
5347
+ if (index <= 0 || index >= pool.length) return;
5348
+ const next = [pool[index], ...pool.slice(0, index), ...pool.slice(index + 1)];
5349
+ edit(field, next.map((route) => ({
5350
+ provider: route.provider,
5351
+ model: route.model,
5352
+ })));
5353
+ }
5354
+
5271
5355
  /** Manual order: remove the route at index. */
5272
5356
  function removePoolRoute(field, index) {
5273
5357
  const next = [...values[field]];
@@ -5395,10 +5479,12 @@ window.__ModuleLoader__.load({
5395
5479
  trimSideModel: {
5396
5480
  provider: values.trimSideModel.provider,
5397
5481
  model: values.trimSideModel.model,
5482
+ ...(values.trimSideModel.effort ? { effort: values.trimSideModel.effort } : {}),
5398
5483
  },
5399
5484
  trimFoldFallbackModel: {
5400
5485
  provider: values.trimFoldFallbackModel.provider,
5401
5486
  model: values.trimFoldFallbackModel.model,
5487
+ ...(values.trimFoldFallbackModel.effort ? { effort: values.trimFoldFallbackModel.effort } : {}),
5402
5488
  },
5403
5489
  trimStateDir: values.trimStateDir,
5404
5490
  ...Object.fromEntries(
@@ -5407,9 +5493,9 @@ window.__ModuleLoader__.load({
5407
5493
  teamsMode: values.teamsMode,
5408
5494
  syncWhitelist: values.syncWhitelist,
5409
5495
  // Intent recognition (design: intent-routing.md §2): route
5410
- // objects materialize {provider, model} like the pool fields;
5411
- // candidate effort/note are always sent (blank string = unset,
5412
- // the Host coerces per its schema).
5496
+ // Route objects materialize {provider, model[, effort]} as configured;
5497
+ // the classifier route remains effort-free. Candidate effort/note are
5498
+ // always sent (blank string = unset; Host coerces per schema).
5413
5499
  intentEnabled: values.intentEnabled,
5414
5500
  intentClassifierRoute: {
5415
5501
  provider: values.intentClassifierRoute.provider,
@@ -5434,6 +5520,7 @@ window.__ModuleLoader__.load({
5434
5520
  intentFallbackRoute: {
5435
5521
  provider: values.intentFallbackRoute.provider,
5436
5522
  model: values.intentFallbackRoute.model,
5523
+ ...(values.intentFallbackRoute.effort ? { effort: values.intentFallbackRoute.effort } : {}),
5437
5524
  },
5438
5525
  };
5439
5526
  if (sameValues(current, desired)) {
@@ -5837,9 +5924,10 @@ window.__ModuleLoader__.load({
5837
5924
  (meta) => meta.model === route.model,
5838
5925
  );
5839
5926
  edit(field, {
5840
- provider: next,
5841
- model: kept ? route.model : '',
5842
- });
5927
+ provider: next,
5928
+ model: kept ? route.model : '',
5929
+ effort: '',
5930
+ });
5843
5931
  },
5844
5932
  ),
5845
5933
  'aria-label': `${label} · provider`,
@@ -5853,7 +5941,7 @@ window.__ModuleLoader__.load({
5853
5941
  `${intentId}-${field}-model`,
5854
5942
  route.model,
5855
5943
  (next) => {
5856
- edit(field, { ...route, model: next });
5944
+ edit(field, { ...route, model: next, effort: '' });
5857
5945
  },
5858
5946
  ),
5859
5947
  disabled: !editable || route.provider === '',
@@ -5861,6 +5949,16 @@ window.__ModuleLoader__.load({
5861
5949
  },
5862
5950
  ...intentModelOptions(route.provider, route.model),
5863
5951
  ),
5952
+ field === 'intentFallbackRoute' && route.provider !== '' && route.model !== ''
5953
+ ? intentEffortSelect(
5954
+ `${intentId}-${field}-effort`,
5955
+ route,
5956
+ route.effort ?? '',
5957
+ (next) => edit(field, { ...route, effort: next }),
5958
+ `${label} · effort`,
5959
+ t('intentEffortFollow'),
5960
+ )
5961
+ : null,
5864
5962
  );
5865
5963
  };
5866
5964
  /** Trim route rows reuse the intent route picker machinery — the
@@ -5912,6 +6010,7 @@ window.__ModuleLoader__.load({
5912
6010
  ...route,
5913
6011
  provider: next,
5914
6012
  model: kept ? route.model : '',
6013
+ effort: '',
5915
6014
  });
5916
6015
  },
5917
6016
  ),
@@ -5925,10 +6024,10 @@ window.__ModuleLoader__.load({
5925
6024
  : route.model,
5926
6025
  (next) => {
5927
6026
  if (followMode && next === TRIM_ROUTE_FOLLOW) {
5928
- edit(field, { ...route, model: '' });
6027
+ edit(field, { ...route, model: '', effort: '' });
5929
6028
  return;
5930
6029
  }
5931
- edit(field, { ...route, model: next });
6030
+ edit(field, { ...route, model: next, effort: '' });
5932
6031
  },
5933
6032
  ),
5934
6033
  disabled: !editable || route.provider === '',
@@ -5938,10 +6037,10 @@ window.__ModuleLoader__.load({
5938
6037
  ? intentEffortSelect(
5939
6038
  `${trimId}-${field}-effort`,
5940
6039
  route,
5941
- route.effort,
6040
+ route.effort ?? '',
5942
6041
  (next) => edit(field, { ...route, effort: next }),
5943
6042
  `${label} · effort`,
5944
- t('trimEffortPinLow'),
6043
+ t('trimEffortRecommended'),
5945
6044
  )
5946
6045
  : null,
5947
6046
  );
@@ -5950,8 +6049,8 @@ window.__ModuleLoader__.load({
5950
6049
  * wire efforts when the catalog reports them, the EFFORTS
5951
6050
  * fallback otherwise; '' = adapter default. A stored value
5952
6051
  * missing from the list stays selectable. emptyLabel replaces
5953
- * the '' option label (trim rows pin low instead of following
5954
- * the model default). */
6052
+ * the '' option label. Trim rows use their automatic recommendation;
6053
+ * an empty intent-fallback pin follows the model default. */
5955
6054
  const intentEffortSelect = (
5956
6055
  id,
5957
6056
  route,
@@ -6383,6 +6482,9 @@ window.__ModuleLoader__.load({
6383
6482
  onMove: (index, delta) => {
6384
6483
  movePoolRoute(field, index, delta);
6385
6484
  },
6485
+ onMoveTop: (index) => {
6486
+ movePoolRouteToTop(field, index);
6487
+ },
6386
6488
  onRemove: (index) => {
6387
6489
  removePoolRoute(field, index);
6388
6490
  },
package/host/config.js CHANGED
@@ -67,10 +67,9 @@ export const IntentCandidateSchema = z.object({
67
67
  * ModelRouteSchema this accepts the "unset" `{provider:"", model:""}`
68
68
  * pair the settings page round-trips — ModelRouteSchema's `.min(1)`
69
69
  * would reject that exact declared default on every settings write.
70
- * Consumers validate non-empty before trusting the route. effort follows
71
- * the optional no-`.required()` style (schemastery passes undefined
72
- * through): the trim summarizer reads it as a reasoning-effort pin;
73
- * intent-layer routes leave it unset. */
70
+ * Consumers validate non-empty before trusting the route. `effort` is an
71
+ * optional override for summary routes and intent fallback; the classifier
72
+ * route remains effort-free in its route cleaner. */
74
73
  const IntentRouteSchema = z.object({
75
74
  provider: z.string(),
76
75
  model: z.string(),
@@ -240,8 +239,9 @@ export const Config = z.object({
240
239
  * the 0.8×-window auto-compaction (see effectiveContextWindow in
241
240
  * host/intent.js). */
242
241
  intentContextWindow: z.number().min(1024).default(200000).volatile(),
243
- /** Optional last-resort route when the classifier fails and no previous
244
- * routed model exists; empty = the first candidate. */
242
+ /** Optional fallback route after classification fails when no prior route
243
+ * is available; empty = the first candidate. An optional effort pin
244
+ * overrides the fallback model's default. */
245
245
  intentFallbackRoute: IntentRouteSchema.default({ provider: "", model: "" }).volatile(),
246
246
 
247
247
  // --- Context trim: fully-automatic conversation compression ------------
@@ -292,12 +292,14 @@ export const Config = z.object({
292
292
  * budget and the ladder starved. */
293
293
  trimMaxFoldsPerHour: z.number().min(1).max(60).default(30).volatile(),
294
294
  /** Route for the side-model summarizer (L3). Empty = inherit the
295
- * session's current model. */
295
+ * session's current provider/model; an optional effort pin overrides
296
+ * the model's automatic recommended effort. */
296
297
  trimSideModel: IntentRouteSchema.default({ provider: "", model: "" }).volatile(),
297
298
  /** Route for the fold-fallback summarizer. When the primary side route
298
299
  * (trimSideModel or the inherited session model) fails, raw folds retry
299
300
  * on this route before degrading to a mechanical fold; ladder folds
300
- * retry but never go mechanical. Pick a route with headroom on cooldown,
301
+ * retry but never go mechanical. An optional effort pin overrides the
302
+ * model's automatic recommendation. Pick a route with headroom on cooldown,
301
303
  * risk-control, and concurrency limits. Empty = no fallback leg. */
302
304
  trimFoldFallbackModel: IntentRouteSchema.default({ provider: "", model: "" }).volatile(),
303
305
  /** T1/T2/T3 ladder folding (docs/ladder-fold.md): master switch for the