dsh-connect-trae 4.5.1 → 4.5.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.5.2 (2026-10-25)
4
+
5
+ > **思考档位不再被吞,也不再被谎报。** 插件原先只认识三档,未知档位一律按 `high`
6
+ > 发出去——用户选 Max、实际收到 high,无报错也无日志。这一版把词表与 Trae 客户端
7
+ > 对齐(`low / medium / high / extra_high / max`),并把「不认识的档位」从谎报改成
8
+ > 丢弃,让上游用它自己的默认值。
9
+ >
10
+ > 词表变宽,**声称没变**:能选哪些档位仍然只取决于上游为该模型实际下发的
11
+ > `options`,所以今天只发三档的模型不会凭空多出 Max。
12
+
13
+ ### 修复
14
+
15
+ - **思考档位词表配平,未知档位不再静默降档成 high**(#60)。
16
+ - **三处吞档位**:解析侧 `EFFORT_MAP` / `TRAE_REASONING_EFFORTS` 缺 `medium` 与
17
+ `max`;`toPiModel` 的 `thinkingLevelMap` 把 `minimal / medium / max` 硬编码成
18
+ `null`;反向映射写成三元链
19
+ `effort === 'low' ? 'light' : effort === 'xhigh' ? 'extra_high' : 'high'`。
20
+ - **最坏的是第三处**:任何未预期档位都被映射成 `high` 发给上游。Trae 客户端自己
21
+ 认识五档(`reasoning_effort.{low,medium,high,extra_high,max}`),并按 agent model
22
+ 灰度下发,所以上游哪天发 `max`,用户选 Max 就会被静默降成 high。**静默降档比
23
+ 不给选择器更坏**——它给出一个便宜了、但用户没要的答案。
24
+ - **改法**:反向映射抽成 `WIRE_EFFORT_MAP` 查表 + `wireEffortsOf()`,三处调用点
25
+ 统一走它(报告人点到两处,第三处在 wire 行提升的分支里,一起改才不会有同一份
26
+ 目录两种行为)。查不到即**丢弃**,而不是猜一个默认值。
27
+ - **实测**(2026-10-25,本机 CN 凭据,98 行目录):当前 `options` 只有
28
+ `["light","high"]`、`["high","extra_high"]`、`["light","high","extra_high"]`
29
+ 三种,`max` 尚未下发——所以这是「今天就备好、明天才用上」,不是伪造档位。
30
+ - 测试:新增 10 例(词表、丢弃未知档位、既有三档逐字不变、`discoveredCatalog`
31
+ 同步、不伪造档位、`thinkingLevelMap` 按模型而非硬编码)。三处均做变异验证:
32
+ 恢复旧映射 → 5 例红;恢复硬编码 `null` → 1 例红;去掉解析侧新键 → 1 例红。
33
+ - 贡献者:[@ahian-lee](https://github.com/ahian-lee)(issue #60,附诊断与补丁)。
34
+
35
+ ### 文档
36
+
37
+ - **卡片模型栏写明「上下文预算只影响 DSH 侧」**(#60 的顺带问题)。
38
+ - 每行显示的是 Trae 自己公布的最大窗口,而下面的预算单选只写 DSH 的本地预算;
39
+ 请求体里既没有窗口字段、也没有 `max_mode`,所以把预算提到 1M 并不会让上游按
40
+ 1M 服务。原先的标签读起来像「已经放开到 1M」。
41
+ - 上游是否接受 `v2_max_mode_enabled` 这类字段**尚未取证**,所以这次不动请求体
42
+ (乱加字段可能让每次请求 400),只把事实说出来:常驻说明,不用悬停提示。
43
+
3
44
  ## 4.5.1 (2026-10-25)
4
45
 
5
46
  > **只在租户池上架的模型不再隐形。** 插件的模型目录只问了 `solo.trae.cn` 一处,
package/lib/client.js CHANGED
@@ -5659,6 +5659,7 @@ window.__ModuleLoader__.load({
5659
5659
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
5660
5660
  className: "dsm-trae-context-budget",
5661
5661
  "aria-label": t("row.contextBudget"),
5662
+ title: t("row.contextBudgetHint"),
5662
5663
  children: contextBudgetOptions(model).map((option) => {
5663
5664
  const selectable = model.maxContextWindow !== void 0;
5664
5665
  const checked = (activeContextBudgets[model.id] ?? model.contextWindow) === option;
@@ -5726,6 +5727,10 @@ window.__ModuleLoader__.load({
5726
5727
  })
5727
5728
  ]
5728
5729
  }),
5730
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5731
+ className: "dsm-trae-model-capability-note",
5732
+ children: t("row.contextBudgetHint")
5733
+ }),
5729
5734
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5730
5735
  className: "dsm-trae-model-capability-note",
5731
5736
  children: t("row.modelCapabilityPending")
@@ -6898,6 +6903,7 @@ window.__ModuleLoader__.load({
6898
6903
  "row.veilSaving": "Saving changes…",
6899
6904
  "row.modelContext": "Max context {context}",
6900
6905
  "row.contextBudget": "DSH context budget",
6906
+ "row.contextBudgetHint": "Affects the DSH-side context budget only. The plugin does not send a window or Max-mode field upstream, so this does not change the window Trae serves the request with.",
6901
6907
  "row.modelOutput": "Output {output}",
6902
6908
  "row.modelImage": "Image",
6903
6909
  "row.modelImageAuto": "Enabled automatically: Trae marks this model multimodal.",
@@ -7277,6 +7283,7 @@ window.__ModuleLoader__.load({
7277
7283
  "row.veilSaving": "正在保存更改…",
7278
7284
  "row.modelContext": "最大上下文 {context}",
7279
7285
  "row.contextBudget": "DSH 上下文预算",
7286
+ "row.contextBudgetHint": "只影响 DSH 侧的上下文预算。插件不会向上游发送窗口或 Max 模式字段,所以这不会改变 Trae 实际服务这次请求所用的窗口。",
7280
7287
  "row.modelOutput": "最大输出 {output}",
7281
7288
  "row.modelImage": "图片",
7282
7289
  "row.modelImageAuto": "已自动开启:Trae 把该模型标为多模态。",
package/lib/index.d.ts CHANGED
@@ -44,7 +44,23 @@ interface TraeActivityDiscount {
44
44
  }
45
45
  //#endregion
46
46
  //#region src/reasoning.d.ts
47
- declare const TRAE_REASONING_EFFORTS: readonly ["minimal", "low", "medium", "high", "xhigh"];
47
+ /**
48
+ * The DSH thinking levels this plugin can carry, in ascending order.
49
+ *
50
+ * `max` is the fifth level Trae's own client advertises — its bundle ships the
51
+ * i18n keys `trae-chat-core.model.reasoning_effort.{low,medium,high,extra_high,max}`
52
+ * and `ai.model.reasoning_effort.{low,medium,high,xhigh,max}`, and publishes the
53
+ * per-model list from a remote config (`AI.agent.model.reasoning_effort_level_by_agent_model_v2`).
54
+ * Leaving it out of this list meant a level the vendor could start serving was
55
+ * filtered away at PARSE time, before anything downstream could decide (issue
56
+ * #60). Listing it does not fabricate the level: `supported` is still built
57
+ * only from what the upstream response actually contains, so a model that does
58
+ * not advertise `max` never offers it.
59
+ *
60
+ * `minimal` is kept for the same reason in reverse — it is a DSH level, so the
61
+ * type must be able to represent it even though no CN model has advertised it.
62
+ */
63
+ declare const TRAE_REASONING_EFFORTS: readonly ["minimal", "low", "medium", "high", "xhigh", "max"];
48
64
  type TraeReasoningEffort = typeof TRAE_REASONING_EFFORTS[number];
49
65
  interface TraeReasoningCapability {
50
66
  supported: readonly TraeReasoningEffort[];
package/lib/index.js CHANGED
@@ -282,6 +282,53 @@ function displayKey(name) {
282
282
  return name.trim().toLowerCase();
283
283
  }
284
284
  /**
285
+ * DSH thinking level -> the `reasoning_effort` name Trae's wire accepts.
286
+ *
287
+ * The INVERSE of `EFFORT_MAP` in `model-metadata.ts`, and it must stay an exact
288
+ * mirror of it: a level this table does not name has no wire spelling, so it is
289
+ * DROPPED rather than guessed (issue #60).
290
+ *
291
+ * The previous form was a ternary chain ending in a bare `'high'`:
292
+ *
293
+ * effort === 'low' ? 'light' : effort === 'xhigh' ? 'extra_high' : 'high'
294
+ *
295
+ * so EVERY unnamed level was sent as `high`. That is not a harmless default: it
296
+ * is a silent downgrade. Trae's own vocabulary carries five levels
297
+ * (`low / medium / high / extra_high / max`, verified in the client bundle's
298
+ * `reasoning_effort` i18n keys), and its remote config publishes the list per
299
+ * model (`AI.agent.model.reasoning_effort_level_by_agent_model_v2`). The day
300
+ * upstream starts advertising `max`, a user picking Max would have sent `high`
301
+ * — no error, no log, and a cheaper answer than the one they asked for. A
302
+ * dropped field lets the upstream apply its own default, which is at least the
303
+ * model's real behaviour instead of a wrong claim about it.
304
+ *
305
+ * Measured 2026-10-25 on a live CN credential (98 directory rows): options are
306
+ * `["light","high"]`, `["high","extra_high"]`, `["light","high","extra_high"]`
307
+ * or absent — `max` is not published YET, which is exactly why this must not
308
+ * wait for it to be.
309
+ */
310
+ const WIRE_EFFORT_MAP = {
311
+ low: "light",
312
+ medium: "medium",
313
+ high: "high",
314
+ xhigh: "extra_high",
315
+ max: "max"
316
+ };
317
+ /**
318
+ * Map a model's advertised levels to their wire names, dropping unnamed ones.
319
+ *
320
+ * `flatMap` rather than `map` + a placeholder: an unmappable level must not
321
+ * produce an entry at all, because every consumer reads "is there a value here"
322
+ * as "may this level be sent" (see `thinkingLevelMap` in `adapter.ts` and the
323
+ * `delete input['reasoning_effort']` fallback in `solo-bridge.ts`).
324
+ */
325
+ function wireEffortsOf(supported) {
326
+ return Object.fromEntries(supported.flatMap((effort) => {
327
+ const wire = WIRE_EFFORT_MAP[effort];
328
+ return wire === void 0 ? [] : [[effort, wire]];
329
+ }));
330
+ }
331
+ /**
285
332
  * Merge the two Trae model sources into one authoritative catalog.
286
333
  *
287
334
  * `remote` (the solo.trae.cn `/models` directory) is the authoritative model
@@ -360,7 +407,7 @@ function mergeTraeModelSources(remote, wire) {
360
407
  reasoningSupported: model.reasoningSupported,
361
408
  ...model.reasoning === void 0 ? {} : {
362
409
  reasoning: model.reasoning,
363
- reasoningEfforts: Object.fromEntries(model.reasoning.supported.map((effort) => [effort, effort === "low" ? "light" : effort === "xhigh" ? "extra_high" : "high"]))
410
+ reasoningEfforts: wireEffortsOf(model.reasoning.supported)
364
411
  },
365
412
  ...wireModel !== void 0 && wireModel.id !== "" && wireModel.id !== model.id ? { wireConfigName: wireModel.id } : {},
366
413
  ...wireFunction === void 0 ? {} : { wireFunction }
@@ -433,7 +480,7 @@ function mergeTraeModelSources(remote, wire) {
433
480
  supported: [...wireModel.reasoning.supported],
434
481
  ...wireModel.reasoning.defaultEffort === void 0 ? {} : { defaultEffort: wireModel.reasoning.defaultEffort }
435
482
  },
436
- reasoningEfforts: Object.fromEntries(wireModel.reasoning.supported.map((effort) => [effort, effort === "low" ? "light" : effort === "xhigh" ? "extra_high" : "high"]))
483
+ reasoningEfforts: wireEffortsOf(wireModel.reasoning.supported)
437
484
  },
438
485
  ...wireModel.function === void 0 ? {} : { wireFunction: wireModel.function }
439
486
  });
@@ -507,7 +554,7 @@ function discoveredCatalog(models) {
507
554
  reasoningSupported: model.reasoningSupported,
508
555
  ...model.reasoning === void 0 ? {} : {
509
556
  reasoning: model.reasoning,
510
- reasoningEfforts: Object.fromEntries(model.reasoning.supported.map((effort) => [effort, effort === "low" ? "light" : effort === "xhigh" ? "extra_high" : "high"]))
557
+ reasoningEfforts: wireEffortsOf(model.reasoning.supported)
511
558
  }
512
559
  });
513
560
  return result;
@@ -836,14 +883,28 @@ function toPiModel(info, baseUrl, providerId, withRate = true) {
836
883
  cacheWrite: 0
837
884
  },
838
885
  reasoning: info.reasoningEfforts !== void 0,
839
- ...info.reasoningEfforts === void 0 ? {} : { thinkingLevelMap: {
886
+ ...info.reasoningEfforts === void 0 ? {} : {
887
+ /**
888
+ * Which DSH levels this model may be asked for, and under which wire name.
889
+ *
890
+ * Every level is read from the model's OWN advertised capability, never
891
+ * hardcoded. `minimal / medium / max` used to be pinned to `null`, so a
892
+ * level the upstream published could not be selected even after it was
893
+ * parsed — the second of the three places issue #60 reported. `null`
894
+ * still means "not offered", which is what a model that does not
895
+ * advertise the level reports: nothing here fabricates a level.
896
+ *
897
+ * `off` stays `null` deliberately: Trae's wire has no "no reasoning"
898
+ * spelling, so an explicit off is not something this provider can send.
899
+ */
900
+ thinkingLevelMap: {
840
901
  off: null,
841
- minimal: null,
902
+ minimal: info.reasoningEfforts.minimal ?? null,
842
903
  low: info.reasoningEfforts.low ?? null,
843
- medium: null,
904
+ medium: info.reasoningEfforts.medium ?? null,
844
905
  high: info.reasoningEfforts.high ?? null,
845
906
  xhigh: info.reasoningEfforts.xhigh ?? null,
846
- max: null
907
+ max: info.reasoningEfforts.max ?? null
847
908
  } },
848
909
  ...info.contextWindow === void 0 ? {} : { contextWindow: info.contextWindow },
849
910
  ...info.maxTokens === void 0 ? {} : { maxTokens: info.maxTokens },
@@ -4752,12 +4813,29 @@ function parseTraeActivityDiscount(value) {
4752
4813
  }
4753
4814
  //#endregion
4754
4815
  //#region src/reasoning.ts
4816
+ /**
4817
+ * The DSH thinking levels this plugin can carry, in ascending order.
4818
+ *
4819
+ * `max` is the fifth level Trae's own client advertises — its bundle ships the
4820
+ * i18n keys `trae-chat-core.model.reasoning_effort.{low,medium,high,extra_high,max}`
4821
+ * and `ai.model.reasoning_effort.{low,medium,high,xhigh,max}`, and publishes the
4822
+ * per-model list from a remote config (`AI.agent.model.reasoning_effort_level_by_agent_model_v2`).
4823
+ * Leaving it out of this list meant a level the vendor could start serving was
4824
+ * filtered away at PARSE time, before anything downstream could decide (issue
4825
+ * #60). Listing it does not fabricate the level: `supported` is still built
4826
+ * only from what the upstream response actually contains, so a model that does
4827
+ * not advertise `max` never offers it.
4828
+ *
4829
+ * `minimal` is kept for the same reason in reverse — it is a DSH level, so the
4830
+ * type must be able to represent it even though no CN model has advertised it.
4831
+ */
4755
4832
  const TRAE_REASONING_EFFORTS = [
4756
4833
  "minimal",
4757
4834
  "low",
4758
4835
  "medium",
4759
4836
  "high",
4760
- "xhigh"
4837
+ "xhigh",
4838
+ "max"
4761
4839
  ];
4762
4840
  function parseReasoningCapability(value) {
4763
4841
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -5863,10 +5941,31 @@ function parseFeatures(value) {
5863
5941
  return;
5864
5942
  }
5865
5943
  }
5944
+ /**
5945
+ * Trae's wire effort name -> the DSH thinking level.
5946
+ *
5947
+ * The FORWARD half of the mapping; `WIRE_EFFORT_MAP` in `catalog.ts` is its
5948
+ * exact inverse. Both halves must name the same levels, or a level survives one
5949
+ * direction and vanishes in the other.
5950
+ *
5951
+ * `medium` and `max` are here because Trae's own client advertises them
5952
+ * (`reasoning_effort.{low,medium,high,extra_high,max}` in its i18n bundle) and
5953
+ * publishes the per-model list remotely — so the day upstream starts sending
5954
+ * either, the parser must not filter it away before anything can use it (issue
5955
+ * #60). Measured 2026-10-25 on a live CN credential, the published options were
5956
+ * only `light / high / extra_high`; these entries are therefore inert today and
5957
+ * ready tomorrow, not a claim that the levels are served now.
5958
+ *
5959
+ * An unknown name is deliberately ABSENT rather than defaulted: `parse` drops
5960
+ * it, and the reverse map drops it too, so an unrecognised level can never be
5961
+ * reported to the user or sent upstream under a name the vendor never said.
5962
+ */
5866
5963
  const EFFORT_MAP = {
5867
5964
  light: "low",
5965
+ medium: "medium",
5868
5966
  high: "high",
5869
- extra_high: "xhigh"
5967
+ extra_high: "xhigh",
5968
+ max: "max"
5870
5969
  };
5871
5970
  /**
5872
5971
  * Parse only capabilities explicitly advertised by Trae's remote model API.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-trae",
3
3
  "displayName": "DSH Connect Trae",
4
4
  "description": "把本机登录的 Trae 模型接入 DeepSeek Harness:国内版与国际版双供应商并行,提供用量/积分概览与每日签到领取。",
5
- "version": "4.5.1",
5
+ "version": "4.5.2",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",