@magpie-community/opencode-trae-auth 0.2.6 → 0.2.8

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.
Files changed (3) hide show
  1. package/README.md +9 -1
  2. package/index.mjs +58 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -138,7 +138,15 @@ a bigger `context_window_tokens.max` is listed again as its Max,
138
138
  can name the `__max` model, a SOLO list the windows). It asks the
139
139
  `__max` model through the function that names it, with `max_tokens` set
140
140
  and `user_message_context.model_info.prompt_max_tokens` the window less
141
- it, as Max mode does.
141
+ it, as Max mode does. The lists needn't agree on a model's *name*: chat_v3
142
+ calls deepseek-v4.1-flash "DeepSeek-V4-Flash 正式版", as it once called
143
+ DeepSeek-V4-Flash-Official, where the SOLO lists call it
144
+ "DeepSeek-V4.1-Flash" (yetone/magpie#681). Trae's usage page books a
145
+ request under the name of the list it was served through, so the plugin
146
+ takes the name most of a model's lists give — the one Trae's own client is
147
+ served under — and asks that model, and its Max, through a list carrying
148
+ it. Trae renamed its official entries on 2026-10-09, leaving the name its
149
+ lists mostly agree on the only thing that tells a misnamed entry apart.
142
150
 
143
151
  Reasoning: the models reason, but Trae's request takes no effort or
144
152
  thinking level, so the plugin lists no variants and a level an agent
package/index.mjs CHANGED
@@ -1338,6 +1338,31 @@ function toolNamed(name, tools) {
1338
1338
  return names.find((n) => norm(n) === norm(name)) ?? name
1339
1339
  }
1340
1340
 
1341
+ // unwrapped is a call's arguments with the layer a model put around them
1342
+ // taken off: asked for {"name":…,"arguments":{…}}, some write the
1343
+ // arguments as {"arguments":{…}} again, natively or in the block, and the
1344
+ // agent got {"arguments":{"content":…,"path":…}} for its write (bee2an on
1345
+ // X). A lone arguments, input, parameters or args key holding an object is
1346
+ // that layer when the tool has no parameter of that name.
1347
+ const WRAPS = ["arguments", "input", "parameters", "args"]
1348
+ function unwrapped(args, name, tools) {
1349
+ const t = tools.map((x) => x.function ?? x).find((f) => f?.name === name)
1350
+ if (!t) return args
1351
+ const props = t.parameters?.properties ?? {}
1352
+ let v = looseJSON(args)
1353
+ let changed = false
1354
+ for (let k = 0; k < 3 && v && typeof v === "object" && !Array.isArray(v); k++) {
1355
+ const keys = Object.keys(v)
1356
+ if (keys.length !== 1 || !WRAPS.includes(keys[0]) || keys[0] in props) break
1357
+ let inner = v[keys[0]]
1358
+ if (typeof inner === "string") inner = looseJSON(inner)
1359
+ if (!inner || typeof inner !== "object" || Array.isArray(inner)) break
1360
+ v = inner
1361
+ changed = true
1362
+ }
1363
+ return changed ? JSON.stringify(v) : args
1364
+ }
1365
+
1341
1366
  // DeepSeek's tags, with its full-width bar or a plain one
1342
1367
  const DSML = String.raw`<\s*\/?\s*[||][\s||]*DSML[\s||]*`
1343
1368
  const DSML_AT = new RegExp(DSML)
@@ -1569,7 +1594,10 @@ async function* parts(events, tools = []) {
1569
1594
  const tt = new TextTools(tools)
1570
1595
  const nc = new NativeCalls()
1571
1596
  const th = new Thoughts()
1572
- const call = (c) => ({ call: { ...c, name: toolNamed(c.name, tools) } })
1597
+ const call = (c) => {
1598
+ const name = toolNamed(c.name, tools)
1599
+ return { call: { ...c, name, arguments: unwrapped(c.arguments, name, tools) } }
1600
+ }
1573
1601
  let finish = ""
1574
1602
  for await (const { event, data } of events) {
1575
1603
  const name = eventName(event)
@@ -2042,24 +2070,40 @@ const makePlugin = (site) => async ({ client }) => {
2042
2070
  // deepseek-v4.1-flash "DeepSeek-V4-Flash 正式版", as it does
2043
2071
  // DeepSeek-V4-Flash-Official, where SOLO's lists call it
2044
2072
  // DeepSeek-V4.1-Flash (yetone/magpie#681) — Trae's usage page then books
2045
- // it under that name too. Such an entry is misnamed: its name is another
2046
- // model's in the same list, and other lists name it otherwise.
2073
+ // a request under the name of the list it was served through. Such an
2074
+ // entry is misnamed: its name is another model's in the same list, and
2075
+ // other lists name it otherwise.
2047
2076
  const nameOf = (m) => String(m.display_config?.display_name || m.display_name || m.display_model_name || m.config_name)
2048
- const names = new Map() // id → the names its lists give it
2077
+ const names = new Map() // id → the names its lists give it, and how many give each
2049
2078
  for (const fn of site.functions) {
2050
2079
  for (const m of lists[fn] ?? []) {
2051
2080
  if (!chatModel(m, site)) continue
2052
- const id = String(m.config_name)
2053
- names.set(id, (names.get(id) ?? new Set()).add(nameOf(m)))
2081
+ const id = String(m.config_name), name = nameOf(m)
2082
+ const by = names.get(id) ?? new Map()
2083
+ by.set(name, (by.get(name) ?? 0) + 1)
2084
+ names.set(id, by)
2054
2085
  }
2055
2086
  }
2087
+ // the name most of a model's lists give it: the one Trae's own client is
2088
+ // served under, and so the one its usage page books it as. Trae renames
2089
+ // entries when it likes, and its 2026-10-09 rename of
2090
+ // DeepSeek-V4-Flash-Official left chat_v3 the only list still calling
2091
+ // deepseek-v4.1-flash by another's name — which neither signal above
2092
+ // catches. A tie leaves the name to its entry.
2093
+ const agreed = new Map() // id → that name, "" when the lists tie
2094
+ for (const [id, by] of names) {
2095
+ const top = Math.max(...by.values())
2096
+ const most = [...by].filter(([, n]) => n === top)
2097
+ agreed.set(id, most.length === 1 ? most[0][0] : "")
2098
+ }
2056
2099
  const misnamed = (fn, m) => {
2057
2100
  const id = String(m.config_name), name = nameOf(m)
2058
2101
  return names.get(id).size > 1 && (lists[fn] ?? []).some((o) => chatModel(o, site) && String(o.config_name) !== id && nameOf(o) === name)
2059
2102
  }
2060
2103
  // a function's entry is worth more when it names a __dev model, then
2061
- // when it isn't misnamed; the first of the best is taken
2062
- const worth = (fn, m) => (devModel(m) ? 2 : 0) + (misnamed(fn, m) ? 0 : 1)
2104
+ // when it isn't misnamed, then when it carries the name its lists agree
2105
+ // on; the first of the best is taken
2106
+ const worth = (fn, m) => (devModel(m) ? 2 : 0) + (misnamed(fn, m) ? 0 : 1) + (nameOf(m) === agreed.get(String(m.config_name)) ? 1 : 0)
2063
2107
  const out = new Map()
2064
2108
  for (const fn of site.functions) {
2065
2109
  for (const m of lists[fn] ?? []) {
@@ -2075,8 +2119,11 @@ const makePlugin = (site) => async ({ client }) => {
2075
2119
  if (dev) modelNames.set(id, { fn, name: dev.model_name, most: Number(dev.max_tokens) || 0 })
2076
2120
  else modelNames.delete(id)
2077
2121
  if (out.has(id + MAX)) continue
2078
- // its Max is asked through the function whose entry names the __max model
2079
- const entries = [{ m, fn }, ...site.functions.flatMap((f) => (lists[f] ?? []).filter((o) => o !== m && chatModel(o, site) && String(o.config_name) === id).map((o) => ({ m: o, fn: f })))]
2122
+ // its Max is asked through the function whose entry names the __max
2123
+ // model, the entries carrying the agreed name first: Trae books the
2124
+ // Max under that name too
2125
+ const plain = (e) => (nameOf(e.m) === agreed.get(id) ? 0 : 1)
2126
+ const entries = [{ m, fn }, ...site.functions.flatMap((f) => (lists[f] ?? []).filter((o) => o !== m && chatModel(o, site) && String(o.config_name) === id).map((o) => ({ m: o, fn: f })))].sort((a, b) => plain(a) - plain(b))
2080
2127
  const max = maxModel(...entries.map((e) => e.m))
2081
2128
  maxes.set(id, max)
2082
2129
  if (max) {
@@ -2282,4 +2329,4 @@ const makePlugin = (site) => async ({ client }) => {
2282
2329
  export const TraeCNAuthPlugin = makePlugin(SITES["trae-cn"])
2283
2330
  export const TraeGlobalAuthPlugin = makePlugin(SITES["trae-global"])
2284
2331
 
2285
- export const _internal = { HOSTS: SITES["trae-cn"].hosts, MODELS: SITES["trae-cn"].models, TextTools, NativeCalls, looseJSON, glmCall, toolNamed, traeMessages, chatBody, credits, dollarUsageOf, whenOf, newDevice, proofOf, SITES, SESSION, conversationID }
2332
+ export const _internal = { HOSTS: SITES["trae-cn"].hosts, MODELS: SITES["trae-cn"].models, TextTools, NativeCalls, looseJSON, glmCall, toolNamed, unwrapped, traeMessages, chatBody, credits, dollarUsageOf, whenOf, newDevice, proofOf, SITES, SESSION, conversationID }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-trae-auth",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "OpenCode provider plugins for Trae: Trae CN (trae.cn) and Trae Global (trae.ai), free tier included, with Trae's own browser sign-in",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",