@magpie-community/opencode-trae-auth 0.2.22 → 0.2.24
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/README.md +10 -1
- package/index.mjs +156 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,6 +134,13 @@ agent's `POST https://trae-api-cn.mchost.guru/api/agent/v3/llm_utils_chat`:
|
|
|
134
134
|
error (plugins#38). Trae takes no two messages of one role in a row,
|
|
135
135
|
so an assistant turn's text is folded into the calls turn after it
|
|
136
136
|
and consecutive calls turns join one `tool_calls` list.
|
|
137
|
+
- For a Kimi (Moonshot) model the tools' schemas are first rewritten for
|
|
138
|
+
Moonshot's "moonshot flavored json schema", which Trae's 4027 "tool call
|
|
139
|
+
failed" quotes when it refuses them: an `enum` or `const` with no `type`
|
|
140
|
+
gets the one its values have, a `$ref` beside a `type` and a `type` beside
|
|
141
|
+
an `anyOf` are written as Kimi holds them (the same rules as kimi-work's).
|
|
142
|
+
The request's own tools are not changed, and a schema Kimi takes as it is
|
|
143
|
+
goes byte for byte. Other models' tools go as they are.
|
|
137
144
|
- A call with no name is dropped, and the `tool` result answering it
|
|
138
145
|
with it: Trae requires the name, and an empty `tool_calls` list with a
|
|
139
146
|
result naming no call is not a turn it takes.
|
|
@@ -231,7 +238,9 @@ which levels it takes — its `reasoning_effort_config`, with the options
|
|
|
231
238
|
(`light`, `high`, `extra_high`) and the default. The request takes the same
|
|
232
239
|
names as `reasoning_effort`, so a level an agent picks is sent as Trae
|
|
233
240
|
spells it: the plugin offers each model's own levels as `variants`
|
|
234
|
-
(`low`/`high`/`xhigh` for `light`/`high`/`extra_high`
|
|
241
|
+
(`low`/`high`/`xhigh` for `light`/`high`/`extra_high`; `medium` and `max`
|
|
242
|
+
keep their names, offered only where the entry names them, as for DeepSeek
|
|
243
|
+
V4.1 Flash's `max` in Trae's app), and sends nothing
|
|
235
244
|
for a level the model doesn't name, or for a model whose entry names none —
|
|
236
245
|
Trae then serves it at its own default. An entry naming levels this map
|
|
237
246
|
can't spell offers none, rather than being sent one Trae would reject. A
|
package/index.mjs
CHANGED
|
@@ -1126,11 +1126,157 @@ function traeMessages(req, sees = false) {
|
|
|
1126
1126
|
})
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
|
-
//
|
|
1130
|
-
|
|
1129
|
+
// Moonshot (Kimi) validates tools against its own "moonshot flavored json
|
|
1130
|
+
// schema" and refuses the whole request (Trae answers code 4027, "tool call
|
|
1131
|
+
// failed: Invalid request: tools.function.parameters is not a valid moonshot
|
|
1132
|
+
// flavored json schema, details: <At path 'properties.ops.items.properties.op':
|
|
1133
|
+
// type is not defined>") for shapes a JSON Schema allows and agents send: an
|
|
1134
|
+
// enum or const with no type (Claude Code's tools carry them), a $ref beside a
|
|
1135
|
+
// type, a type beside an anyOf. Trae passes the tools to Kimi as they are, so
|
|
1136
|
+
// they are rewritten here for the rules Kimi has been seen to hold, for a
|
|
1137
|
+
// Kimi/Moonshot model only (moonshotModel). This is kimi-work's moonshotSchema,
|
|
1138
|
+
// which carries the reasons for each rule, ported. The tools sent are
|
|
1139
|
+
// a copy: the request's own stay as they were, and a schema Kimi takes as it is
|
|
1140
|
+
// goes byte for byte.
|
|
1141
|
+
const jsIsObj=x=>x!==null&&typeof x==='object'&&!Array.isArray(x);
|
|
1142
|
+
const msMAP_KEYS=['properties','$defs','definitions','dependentSchemas','patternProperties'];
|
|
1143
|
+
const msSUB_KEYS=['items','additionalProperties','contains','propertyNames','not','if','then','else','allOf','anyOf','oneOf','prefixItems','unevaluatedProperties','unevaluatedItems','contentSchema'];
|
|
1144
|
+
const msALL_TYPES=['string','number','integer','boolean','object','array','null'];
|
|
1145
|
+
|
|
1146
|
+
function msEachSchema(node,f) {
|
|
1147
|
+
if(Array.isArray(node)) {for(const v of node)msEachSchema(v,f);return;}
|
|
1148
|
+
if(!jsIsObj(node))return;
|
|
1149
|
+
f(node);
|
|
1150
|
+
for(const k of msMAP_KEYS) if(jsIsObj(node[k])) for(const v of Object.values(node[k])) msEachSchema(v,f);
|
|
1151
|
+
for(const k of msSUB_KEYS) if(k in node) msEachSchema(node[k],f);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function msRefTarget(root,ref) {
|
|
1155
|
+
if(typeof ref!=='string'||(ref!=='#'&&!ref.startsWith('#/')))return undefined;
|
|
1156
|
+
let cur=root;
|
|
1157
|
+
for(let tok of ref.split('/').slice(1)) {
|
|
1158
|
+
tok=tok.replace(/~1/g,'/').replace(/~0/g,'~');
|
|
1159
|
+
try{tok=decodeURIComponent(tok);}catch{}
|
|
1160
|
+
if(!jsIsObj(cur)&&!Array.isArray(cur))return undefined;
|
|
1161
|
+
if(!Object.hasOwn(cur,tok))return undefined;
|
|
1162
|
+
cur=cur[tok];
|
|
1163
|
+
}
|
|
1164
|
+
return cur;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
const msHasRefInside=s=>JSON.stringify(s).includes('"$ref"');
|
|
1168
|
+
|
|
1169
|
+
function msValueType(v) {
|
|
1170
|
+
if(v===null)return 'null';
|
|
1171
|
+
switch(typeof v){
|
|
1172
|
+
case 'string':return 'string';
|
|
1173
|
+
case 'boolean':return 'boolean';
|
|
1174
|
+
case 'number':return Number.isInteger(v)?'integer':'number';
|
|
1175
|
+
}
|
|
1176
|
+
return Array.isArray(v)?'array':'object';
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// the type an enum of vals is given: the one all non-null values have, with
|
|
1180
|
+
// "null" when a null is among them; undefined when there are none or they
|
|
1181
|
+
// are of more than one kind
|
|
1182
|
+
function msEnumType(vals) {
|
|
1183
|
+
let t,nul=false;
|
|
1184
|
+
for(const v of vals) {
|
|
1185
|
+
const vt=msValueType(v);
|
|
1186
|
+
if(vt==='null'){nul=true;continue;}
|
|
1187
|
+
if(vt==='array'||vt==='object')return undefined;
|
|
1188
|
+
if(t===undefined||t===vt)t=vt;
|
|
1189
|
+
else if((t==='integer'&&vt==='number')||(t==='number'&&vt==='integer'))t='number';
|
|
1190
|
+
else return undefined;
|
|
1191
|
+
}
|
|
1192
|
+
if(t===undefined)return undefined;
|
|
1193
|
+
return nul?[t,'null']:t;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// the types s can have, as its type, keywords, values or branches say;
|
|
1197
|
+
// undefined when they don't say
|
|
1198
|
+
function msSchemaTypes(root,s,depth=0) {
|
|
1199
|
+
if(depth>16||!jsIsObj(s))return undefined;
|
|
1200
|
+
if('type' in s)return new Set([].concat(s.type).map(String));
|
|
1201
|
+
if('$ref' in s){const t=msRefTarget(root,s.$ref);return t===undefined?undefined:msSchemaTypes(root,t,depth+1);}
|
|
1202
|
+
if(Array.isArray(s.enum)||'const' in s)return new Set(('const' in s?[s.const]:s.enum).map(msValueType));
|
|
1203
|
+
for(const k of ['oneOf','anyOf','allOf']) {
|
|
1204
|
+
if(!Array.isArray(s[k]))continue;
|
|
1205
|
+
const ts=new Set();
|
|
1206
|
+
for(const b of s[k]) {
|
|
1207
|
+
const bt=msSchemaTypes(root,b,depth+1);
|
|
1208
|
+
if(!bt){if(k!=='allOf')return undefined;continue;}
|
|
1209
|
+
for(const t of bt)ts.add(t);
|
|
1210
|
+
}
|
|
1211
|
+
if(ts.size)return ts;
|
|
1212
|
+
}
|
|
1213
|
+
const has=(...ks)=>ks.some(k=>k in s);
|
|
1214
|
+
if(has('properties','required','additionalProperties','patternProperties','minProperties','maxProperties','propertyNames','dependentRequired','dependentSchemas','unevaluatedProperties'))return new Set(['object']);
|
|
1215
|
+
if(has('items','prefixItems','minItems','maxItems','uniqueItems','contains','unevaluatedItems'))return new Set(['array']);
|
|
1216
|
+
if(has('minLength','maxLength','pattern','format','contentEncoding','contentMediaType'))return new Set(['string']);
|
|
1217
|
+
if(has('minimum','maximum','exclusiveMinimum','exclusiveMaximum','multipleOf'))return new Set(['number']);
|
|
1218
|
+
return undefined;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function msTypeFor(root,s) {
|
|
1222
|
+
if(Array.isArray(s.enum))return msEnumType(s.enum);
|
|
1223
|
+
if('const' in s)return msEnumType([s.const]);
|
|
1224
|
+
const ts=msSchemaTypes(root,s);
|
|
1225
|
+
if(!ts||!ts.size)return [...msALL_TYPES];
|
|
1226
|
+
if(ts.has('integer')&&ts.has('number'))ts.delete('integer');
|
|
1227
|
+
const names=msALL_TYPES.filter(t=>ts.has(t));
|
|
1228
|
+
return names.length===1?names[0]:names;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// the same object with type written first, as an agent would have
|
|
1232
|
+
const msWithType=(s,type)=>{const rest={...s};delete rest.type;for(const k of Object.keys(s))delete s[k];Object.assign(s,{type},rest);};
|
|
1233
|
+
|
|
1234
|
+
function moonshotSchema(root) {
|
|
1235
|
+
let changed=false;
|
|
1236
|
+
// a $ref beside a type, and a type beside an anyOf
|
|
1237
|
+
msEachSchema(root,s=>{
|
|
1238
|
+
if('$ref' in s&&'type' in s) {
|
|
1239
|
+
const t=msRefTarget(root,s.$ref);
|
|
1240
|
+
if(jsIsObj(t)&&!msHasRefInside(t)) {
|
|
1241
|
+
const own={...s};delete own.$ref;
|
|
1242
|
+
for(const k of Object.keys(s))delete s[k];
|
|
1243
|
+
Object.assign(s,structuredClone(t),own);
|
|
1244
|
+
} else delete s.type;
|
|
1245
|
+
changed=true;
|
|
1246
|
+
}
|
|
1247
|
+
if(Array.isArray(s.anyOf)&&'type' in s){delete s.type;changed=true;}
|
|
1248
|
+
});
|
|
1249
|
+
// an enum or const with no type
|
|
1250
|
+
msEachSchema(root,s=>{
|
|
1251
|
+
if('type' in s||!(Array.isArray(s.enum)||'const' in s))return;
|
|
1252
|
+
const t=msEnumType(Array.isArray(s.enum)?s.enum:[s.const]);
|
|
1253
|
+
if(t!==undefined){msWithType(s,t);changed=true;}
|
|
1254
|
+
});
|
|
1255
|
+
// a $ref's target with no type
|
|
1256
|
+
msEachSchema(root,s=>{
|
|
1257
|
+
if(typeof s.$ref!=='string')return;
|
|
1258
|
+
const t=msRefTarget(root,s.$ref);
|
|
1259
|
+
if(!jsIsObj(t)||'type' in t||'anyOf' in t||'$ref' in t||!Object.keys(t).length)return;
|
|
1260
|
+
const typ=msTypeFor(root,t);
|
|
1261
|
+
if(typ!==undefined){msWithType(t,typ);changed=true;}
|
|
1262
|
+
});
|
|
1263
|
+
return changed;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
const moonshotModel = (...names) => names.some((n) => /kimi|moonshot/i.test(String(n ?? "")))
|
|
1268
|
+
|
|
1269
|
+
// nativeTools are the tools as Trae names them: parameters a JSON string.
|
|
1270
|
+
// moonshot is whether a Kimi model takes them (moonshotSchema)
|
|
1271
|
+
const nativeTools = (tools, moonshot = false) =>
|
|
1131
1272
|
tools.map((t) => {
|
|
1132
1273
|
const f = t.function ?? t
|
|
1133
|
-
|
|
1274
|
+
let p = f.parameters
|
|
1275
|
+
if (moonshot && jsIsObj(p)) {
|
|
1276
|
+
p = structuredClone(p)
|
|
1277
|
+
moonshotSchema(p)
|
|
1278
|
+
}
|
|
1279
|
+
return { type: "function", function: { name: f.name, description: f.description ?? "", parameters: typeof p === "string" ? p : JSON.stringify(p ?? {}) } }
|
|
1134
1280
|
})
|
|
1135
1281
|
|
|
1136
1282
|
// SESSION carries the conversation magpie (or OpenCode) names a request by,
|
|
@@ -1225,7 +1371,7 @@ function chatBody(req, fn, modelName, most = 0, max = null, session = "", sees =
|
|
|
1225
1371
|
}
|
|
1226
1372
|
if (typeof req.temperature === "number") body.temperature = req.temperature
|
|
1227
1373
|
if (Array.isArray(req.tools) && req.tools.length && req.tool_choice !== "none") {
|
|
1228
|
-
body.tools = nativeTools(req.tools)
|
|
1374
|
+
body.tools = nativeTools(req.tools, moonshotModel(config, modelName, req.model))
|
|
1229
1375
|
const c = req.tool_choice
|
|
1230
1376
|
if (c && typeof c === "object") body.tool_choice = c.function?.name || c.name || "auto"
|
|
1231
1377
|
else if (typeof c === "string") body.tool_choice = c
|
|
@@ -2593,13 +2739,16 @@ const MAX = "-max"
|
|
|
2593
2739
|
// (light, high, extra_high) and the one it defaults to. The request takes the
|
|
2594
2740
|
// same names, as reasoning_effort, so a level an agent picks is sent as Trae
|
|
2595
2741
|
// spells it. OpenCode knows the three as low/high/xhigh, so the map goes both
|
|
2596
|
-
// ways.
|
|
2742
|
+
// ways. Trae's client names two more, medium and max (its bundle's
|
|
2743
|
+
// reasoning_effort.{low,medium,high,extra_high,max}), handed out per model:
|
|
2744
|
+
// its app offers DeepSeek V4.1 Flash max (#434), so they are spelled here too,
|
|
2745
|
+
// and still offered only for a model whose entry names them.
|
|
2597
2746
|
//
|
|
2598
2747
|
// An entry without the config takes no level: Trae serves it at its own
|
|
2599
2748
|
// default and nothing is sent. The plugin used to send none for every model,
|
|
2600
2749
|
// which read as "Trae takes no level" — it takes them, per model, and an
|
|
2601
2750
|
// entry that names none is the exception rather than the rule.
|
|
2602
|
-
const LEVELS = { light: "low", high: "high", extra_high: "xhigh" }
|
|
2751
|
+
const LEVELS = { light: "low", medium: "medium", high: "high", extra_high: "xhigh", max: "max" }
|
|
2603
2752
|
|
|
2604
2753
|
// levelsOf is an entry's levels in OpenCode's names, in Trae's order: [] when
|
|
2605
2754
|
// the entry names none, null when it names ones this map can't spell (so a
|
|
@@ -3393,4 +3542,4 @@ const makePlugin = (site) => async ({ client }) => {
|
|
|
3393
3542
|
export const TraeCNAuthPlugin = makePlugin(SITES["trae-cn"])
|
|
3394
3543
|
export const TraeGlobalAuthPlugin = makePlugin(SITES["trae-global"])
|
|
3395
3544
|
|
|
3396
|
-
export const _internal = { ownClient, clientStoragePaths, clientDevice, clientDecode, thisMachine, traeHost, accountOf, HOSTS: SITES["trae-cn"].hosts, MODELS: SITES["trae-cn"].models, TextTools, NativeCalls, looseJSON, glmCall, nameCall, toolNamed, unwrapped, traeMessages, chatBody, credits, dollarUsageOf, whenOf, newDevice, ugDevice, proofOf, SITES, SESSION, conversationID, LEVELS, levelsOf, variantsOf, effortOf }
|
|
3545
|
+
export const _internal = { ownClient, clientStoragePaths, clientDevice, clientDecode, thisMachine, traeHost, accountOf, HOSTS: SITES["trae-cn"].hosts, MODELS: SITES["trae-cn"].models, TextTools, NativeCalls, looseJSON, glmCall, nameCall, toolNamed, unwrapped, traeMessages, chatBody, nativeTools, moonshotSchema, credits, dollarUsageOf, whenOf, newDevice, ugDevice, proofOf, SITES, SESSION, conversationID, LEVELS, levelsOf, variantsOf, effortOf }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-trae-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.24",
|
|
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",
|