@magpie-community/opencode-trae-auth 0.2.23 → 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 +7 -0
- package/index.mjs +151 -5
- 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.
|
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
|
|
@@ -3396,4 +3542,4 @@ const makePlugin = (site) => async ({ client }) => {
|
|
|
3396
3542
|
export const TraeCNAuthPlugin = makePlugin(SITES["trae-cn"])
|
|
3397
3543
|
export const TraeGlobalAuthPlugin = makePlugin(SITES["trae-global"])
|
|
3398
3544
|
|
|
3399
|
-
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",
|