qwen-agent-server 0.11.1

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/dist/vision.js ADDED
@@ -0,0 +1,293 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // vision.ts — direct-HTTP multimodal dispatch path for `qwen_oneshot_vision`.
4
+ //
5
+ // @qwen-code/sdk's ContentBlock union is {TextBlock | ThinkingBlock |
6
+ // ToolUseBlock | ToolResultBlock} — there is no ImageBlock. The whole
7
+ // SDK pipeline (qwen_spawn → query() → CLI subprocess) is text-only.
8
+ // To pass images we bypass the SDK entirely and POST directly to the
9
+ // backend's OpenAI-compat /v1/chat/completions endpoint, matching the
10
+ // shape llama-server expects when its --mmproj projector is loaded.
11
+ //
12
+ // Prerequisite: the chosen backend must be running with --mmproj.
13
+ // Without it, llama-server returns:
14
+ // "image input is not supported - hint: if this is unexpected, you
15
+ // may need to provide the mmproj"
16
+ // at HTTP 500. The supervisor surfaces this as `error.code="backend_no_mmproj"`
17
+ // so callers can route around or fail cleanly.
18
+ import { promises as fs, realpathSync } from "node:fs";
19
+ import os from "node:os";
20
+ import path from "node:path";
21
+ import { createLogger } from "./log.js";
22
+ import { dispatchOpenAIPost } from "./openai-compat.js";
23
+ const log = createLogger("qwen-vision");
24
+ // ─────────────────────────────────────────────────────────────────
25
+ // Input validation (bead qwen-coprocessor-stack-mtt)
26
+ //
27
+ // Pre-mtt: normalizeImage did fs.readFile(input.path) on caller-
28
+ // supplied paths with zero sandboxing, and forwarded {url} inputs
29
+ // to the backend with zero scheme validation. Single-operator local
30
+ // use is self-harm only, but the moment the plugin runs in a shared
31
+ // or non-trusting MCP-client setting, both surfaces become arbitrary
32
+ // file read / SSRF assist. This module enforces an allowlist on both.
33
+ /** Schemes acceptable for {url} inputs. http(s) and data: are what
34
+ * llama-server's mmproj path is actually known to accept; file:,
35
+ * javascript:, etc. should never reach the backend. */
36
+ const ALLOWED_URL_SCHEMES = new Set(["http:", "https:", "data:"]);
37
+ /** Default path roots for {path} inputs. Operators can extend this set
38
+ * via the QWEN_VISION_IMAGE_PATHS env var (colon-separated absolute
39
+ * paths). The defaults cover the two common drop-zones — the user's
40
+ * home directory (screenshots, downloads, image collections) and
41
+ * os.tmpdir() (clipboard / pasted-image pipelines). */
42
+ function resolveAllowedRoots() {
43
+ const roots = new Set();
44
+ const tmp = fs_realpathSync(os.tmpdir());
45
+ const home = fs_realpathSync(os.homedir());
46
+ if (tmp)
47
+ roots.add(tmp);
48
+ if (home)
49
+ roots.add(home);
50
+ const extra = process.env["QWEN_VISION_IMAGE_PATHS"];
51
+ if (extra && extra.trim() !== "") {
52
+ for (const p of extra.split(":")) {
53
+ const trimmed = p.trim();
54
+ if (trimmed === "")
55
+ continue;
56
+ const real = fs_realpathSync(trimmed);
57
+ if (real)
58
+ roots.add(real);
59
+ }
60
+ }
61
+ return [...roots];
62
+ }
63
+ /** Synchronously realpath a directory, returning undefined on any
64
+ * error (path doesn't exist, permission denied, etc.). Used at root-
65
+ * resolution time so we never include a non-canonicalized prefix in
66
+ * the allowlist (which would let symlink trickery escape the sandbox). */
67
+ function fs_realpathSync(p) {
68
+ try {
69
+ return realpathSync(p);
70
+ }
71
+ catch {
72
+ return undefined;
73
+ }
74
+ }
75
+ /** True if `child` (already realpath'd) is inside `parent` (already
76
+ * realpath'd). Uses path.relative so we get cross-platform separator
77
+ * handling without manual slicing. */
78
+ function isInside(child, parent) {
79
+ const rel = path.relative(parent, child);
80
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
81
+ }
82
+ const DEFAULT_TIMEOUT_MS = 300_000;
83
+ const DEFAULT_MAX_TOKENS = 2048;
84
+ const DEFAULT_TEMPERATURE = 0.3;
85
+ // MIME inference from common extensions. Restricted to formats
86
+ // llama-server's mmproj reliably accepts; everything else gets
87
+ // passed as application/octet-stream and may fail at the backend.
88
+ const EXT_TO_MIME = {
89
+ ".png": "image/png",
90
+ ".jpg": "image/jpeg",
91
+ ".jpeg": "image/jpeg",
92
+ ".webp": "image/webp",
93
+ ".gif": "image/gif",
94
+ ".bmp": "image/bmp",
95
+ };
96
+ function inferMimeFromPath(p) {
97
+ const ext = path.extname(p).toLowerCase();
98
+ return EXT_TO_MIME[ext] ?? "application/octet-stream";
99
+ }
100
+ /**
101
+ * Normalize one image input to an OpenAI-compat content block.
102
+ * Returns either {image_url} ready to drop into the chat-completions
103
+ * messages array, or throws on a filesystem read failure.
104
+ */
105
+ export async function normalizeImage(input) {
106
+ if ("url" in input) {
107
+ // Reject anything outside the http(s)/data: allowlist. Past `file:`,
108
+ // `javascript:`, custom schemes etc. would be forwarded to llama-
109
+ // server, which might not handle them cleanly — and `file:` in
110
+ // particular invites local-file disclosure if the backend ever
111
+ // implements remote-fetch.
112
+ let scheme;
113
+ try {
114
+ scheme = new URL(input.url).protocol;
115
+ }
116
+ catch {
117
+ throw new Error(`invalid image URL (not parseable): ${input.url.slice(0, 80)}`);
118
+ }
119
+ if (!ALLOWED_URL_SCHEMES.has(scheme)) {
120
+ throw new Error(`image URL scheme "${scheme}" not allowed (permitted: http, https, data)`);
121
+ }
122
+ return { type: "image_url", image_url: { url: input.url } };
123
+ }
124
+ if ("base64" in input) {
125
+ return {
126
+ type: "image_url",
127
+ image_url: { url: `data:${input.mime};base64,${input.base64}` },
128
+ };
129
+ }
130
+ // {path}: resolve via realpath (defeats symlink escape) then verify
131
+ // the canonical path lives under at least one allowlisted root.
132
+ // Operators can extend the root set via QWEN_VISION_IMAGE_PATHS;
133
+ // anything outside is rejected before fs.readFile runs.
134
+ let realPath;
135
+ try {
136
+ realPath = await fs.realpath(input.path);
137
+ }
138
+ catch (err) {
139
+ throw new Error(`cannot resolve image path "${input.path}": ${err.message}`);
140
+ }
141
+ const roots = resolveAllowedRoots();
142
+ if (!roots.some((r) => isInside(realPath, r))) {
143
+ throw new Error(`image path "${input.path}" resolves to "${realPath}" which is outside the allowed roots (${roots.join(", ") || "<none configured>"}); set QWEN_VISION_IMAGE_PATHS to extend`);
144
+ }
145
+ const buf = await fs.readFile(realPath);
146
+ const mime = input.mime ?? inferMimeFromPath(realPath);
147
+ const b64 = buf.toString("base64");
148
+ return { type: "image_url", image_url: { url: `data:${mime};base64,${b64}` } };
149
+ }
150
+ /**
151
+ * POST to a backend's /v1/chat/completions with multimodal content.
152
+ * Returns a VisionOneshotResult shape; never throws — all failures are
153
+ * encoded in result.error.
154
+ */
155
+ export async function dispatchVisionOneshot(backend, task, images, opts = {}, prior_messages = []) {
156
+ const start = Date.now();
157
+ const timeout_ms = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS;
158
+ const max_tokens = opts.max_tokens ?? DEFAULT_MAX_TOKENS;
159
+ const temperature = opts.temperature ?? DEFAULT_TEMPERATURE;
160
+ const no_think = opts.no_think ?? true;
161
+ let imageBlocks;
162
+ try {
163
+ imageBlocks = await Promise.all(images.map(normalizeImage));
164
+ }
165
+ catch (err) {
166
+ return {
167
+ ok: false,
168
+ elapsed_ms: Date.now() - start,
169
+ backend_id: backend.id,
170
+ error: {
171
+ code: "image_read_failed",
172
+ message: err instanceof Error ? err.message : String(err),
173
+ },
174
+ };
175
+ }
176
+ const userText = no_think ? `/no_think ${task}` : task;
177
+ const messages = [];
178
+ if (opts.system) {
179
+ messages.push({ role: "system", content: opts.system });
180
+ }
181
+ // Prior turns from a continuation thread, oldest-first. v1 carries
182
+ // only text; images from prior vision turns get a placeholder in the
183
+ // formatter.
184
+ for (const m of prior_messages) {
185
+ messages.push({ role: m.role, content: m.content });
186
+ }
187
+ messages.push({
188
+ role: "user",
189
+ content: [{ type: "text", text: userText }, ...imageBlocks],
190
+ });
191
+ const body = {
192
+ model: backend.model,
193
+ messages,
194
+ max_tokens,
195
+ temperature,
196
+ stream: false,
197
+ };
198
+ if (opts.json_schema) {
199
+ body.response_format = {
200
+ type: "json_schema",
201
+ json_schema: { name: "output", strict: true, schema: opts.json_schema },
202
+ };
203
+ }
204
+ if (opts.grammar !== undefined && opts.grammar !== "") {
205
+ body.grammar = opts.grammar;
206
+ }
207
+ const outcome = await dispatchOpenAIPost(backend, "/v1/chat/completions", body, {
208
+ timeout_ms,
209
+ });
210
+ if (!outcome.ok) {
211
+ // llama-server returns the "image input is not supported" hint with
212
+ // HTTP 500; classify that case specifically so callers can route.
213
+ const noMmproj = outcome.status === 500 &&
214
+ typeof outcome.body_text === "string" &&
215
+ /image input is not supported/i.test(outcome.body_text);
216
+ if (outcome.status !== undefined) {
217
+ log.warn({
218
+ backend_id: backend.id,
219
+ status: outcome.status,
220
+ no_mmproj: noMmproj,
221
+ body_excerpt: outcome.body_text?.slice(0, 200),
222
+ }, "vision dispatch HTTP failure");
223
+ }
224
+ return {
225
+ ok: false,
226
+ elapsed_ms: outcome.elapsed_ms,
227
+ backend_id: backend.id,
228
+ error: noMmproj
229
+ ? {
230
+ code: "backend_no_mmproj",
231
+ message: outcome.error.message,
232
+ }
233
+ : outcome.error,
234
+ };
235
+ }
236
+ let body_parsed;
237
+ try {
238
+ body_parsed = JSON.parse(outcome.body_text);
239
+ }
240
+ catch (err) {
241
+ return {
242
+ ok: false,
243
+ elapsed_ms: outcome.elapsed_ms,
244
+ backend_id: backend.id,
245
+ error: {
246
+ code: "backend_error",
247
+ message: `non-JSON response from backend: ${err.message}`,
248
+ },
249
+ };
250
+ }
251
+ const choice = body_parsed.choices?.[0];
252
+ const content = choice?.message?.content;
253
+ if (typeof content !== "string") {
254
+ return {
255
+ ok: false,
256
+ elapsed_ms: outcome.elapsed_ms,
257
+ backend_id: backend.id,
258
+ ...(body_parsed.usage !== undefined ? { usage: body_parsed.usage } : {}),
259
+ error: {
260
+ code: "no_choices",
261
+ message: "backend returned no choices or empty content",
262
+ },
263
+ };
264
+ }
265
+ let parsed;
266
+ if (opts.json_schema) {
267
+ try {
268
+ parsed = JSON.parse(content);
269
+ }
270
+ catch {
271
+ return {
272
+ ok: false,
273
+ result: content,
274
+ elapsed_ms: outcome.elapsed_ms,
275
+ backend_id: backend.id,
276
+ ...(body_parsed.usage !== undefined ? { usage: body_parsed.usage } : {}),
277
+ error: {
278
+ code: "validation_failed",
279
+ message: "response did not parse as JSON despite json_schema",
280
+ },
281
+ };
282
+ }
283
+ }
284
+ return {
285
+ ok: true,
286
+ result: content,
287
+ ...(parsed !== undefined ? { parsed } : {}),
288
+ ...(body_parsed.usage !== undefined ? { usage: body_parsed.usage } : {}),
289
+ elapsed_ms: outcome.elapsed_ms,
290
+ backend_id: backend.id,
291
+ };
292
+ }
293
+ //# sourceMappingURL=vision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision.js","sourceRoot":"","sources":["../src/vision.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,8EAA8E;AAC9E,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,qEAAqE;AACrE,qEAAqE;AACrE,sEAAsE;AACtE,oEAAoE;AACpE,EAAE;AACF,kEAAkE;AAClE,oCAAoC;AACpC,qEAAqE;AACrE,oCAAoC;AACpC,gFAAgF;AAChF,+CAA+C;AAE/C,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;AAExC,oEAAoE;AACpE,qDAAqD;AACrD,EAAE;AACF,iEAAiE;AACjE,kEAAkE;AAClE,oEAAoE;AACpE,oEAAoE;AACpE,qEAAqE;AACrE,sEAAsE;AAEtE;;wDAEwD;AACxD,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAElE;;;;wDAIwD;AACxD,SAAS,mBAAmB;IAC1B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3C,IAAI,GAAG;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,IAAI;QAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE1B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACrD,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACzB,IAAI,OAAO,KAAK,EAAE;gBAAE,SAAS;YAC7B,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,IAAI;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC;AAED;;;2EAG2E;AAC3E,SAAS,eAAe,CAAC,CAAS;IAChC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;uCAEuC;AACvC,SAAS,QAAQ,CAAC,KAAa,EAAE,MAAc;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AACxE,CAAC;AA4FD,MAAM,kBAAkB,GAAG,OAAO,CAAC;AACnC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,+DAA+D;AAC/D,+DAA+D;AAC/D,kEAAkE;AAClE,MAAM,WAAW,GAA2B;IAC1C,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF,SAAS,iBAAiB,CAAC,CAAS;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1C,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAuB;IAEvB,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACnB,qEAAqE;QACrE,kEAAkE;QAClE,+DAA+D;QAC/D,+DAA+D;QAC/D,2BAA2B;QAC3B,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,sCAAsC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,8CAA8C,CAC1E,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;QACtB,OAAO;YACL,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC,IAAI,WAAW,KAAK,CAAC,MAAM,EAAE,EAAE;SAChE,CAAC;IACJ,CAAC;IACD,oEAAoE;IACpE,gEAAgE;IAChE,iEAAiE;IACjE,wDAAwD;IACxD,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,8BAA8B,KAAK,CAAC,IAAI,MAAO,GAAa,CAAC,OAAO,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,eAAe,KAAK,CAAC,IAAI,kBAAkB,QAAQ,yCAAyC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,mBAAmB,0CAA0C,CAC9K,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,IAAI,WAAW,GAAG,EAAE,EAAE,EAAE,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAgB,EAChB,IAAY,EACZ,MAA0B,EAC1B,OAA0B,EAAE,EAC5B,iBAAiF,EAAE;IAEnF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,kBAAkB,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,kBAAkB,CAAC;IACzD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,mBAAmB,CAAC;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;IAEvC,IAAI,WAAW,CAAC;IAChB,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;YAC9B,UAAU,EAAE,OAAO,CAAC,EAAE;YACtB,KAAK,EAAE;gBACL,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D;SACF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,MAAM,QAAQ,GAAmC,EAAE,CAAC;IACpD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,mEAAmE;IACnE,qEAAqE;IACrE,aAAa;IACb,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,WAAW,CAAC;KAC5D,CAAC,CAAC;IAEH,MAAM,IAAI,GAA4B;QACpC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ;QACR,UAAU;QACV,WAAW;QACX,MAAM,EAAE,KAAK;KACd,CAAC;IACF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,IAAI,CAAC,eAAe,GAAG;YACrB,IAAI,EAAE,aAAa;YACnB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE;SACxE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC;QACtD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9B,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,sBAAsB,EAAE,IAAI,EAAE;QAC9E,UAAU;KACX,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,oEAAoE;QACpE,kEAAkE;QAClE,MAAM,QAAQ,GACZ,OAAO,CAAC,MAAM,KAAK,GAAG;YACtB,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;YACrC,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACjC,GAAG,CAAC,IAAI,CACN;gBACE,UAAU,EAAE,OAAO,CAAC,EAAE;gBACtB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,SAAS,EAAE,QAAQ;gBACnB,YAAY,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;aAC/C,EACD,8BAA8B,CAC/B,CAAC;QACJ,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,UAAU,EAAE,OAAO,CAAC,EAAE;YACtB,KAAK,EAAE,QAAQ;gBACb,CAAC,CAAC;oBACE,IAAI,EAAE,mBAAmB;oBACzB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;iBAC/B;gBACH,CAAC,CAAC,OAAO,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,IAAI,WAMH,CAAC;IACF,IAAI,CAAC;QACH,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,UAAU,EAAE,OAAO,CAAC,EAAE;YACtB,KAAK,EAAE;gBACL,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,mCAAoC,GAAa,CAAC,OAAO,EAAE;aACrE;SACF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;IACzC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,UAAU,EAAE,OAAO,CAAC,EAAE;YACtB,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxE,KAAK,EAAE;gBACL,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,8CAA8C;aACxD;SACF,CAAC;IACJ,CAAC;IAED,IAAI,MAA2B,CAAC;IAChC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,OAAO;gBACf,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,UAAU,EAAE,OAAO,CAAC,EAAE;gBACtB,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxE,KAAK,EAAE;oBACL,IAAI,EAAE,mBAAmB;oBACzB,OAAO,EAAE,oDAAoD;iBAC9D;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,OAAO;QACf,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,UAAU,EAAE,OAAO,CAAC,EAAE;KACvB,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "qwen-agent-server",
3
+ "version": "0.11.1",
4
+ "description": "Stateful MCP supervisor exposing Qwen Code as a multi-backend agent — see docs/rdr/RDR-001",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Hal Hildebrand",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Hellblazer/qwen-coprocessor-stack.git",
11
+ "directory": "mcp-bridges/qwen-agent-server"
12
+ },
13
+ "main": "dist/server.js",
14
+ "bin": {
15
+ "qwen-agent-server": "dist/server.js"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "prepublishOnly": "tsc",
23
+ "test": "vitest run --exclude 'tests/integration/**'",
24
+ "test:integration": "vitest run --dir tests/integration",
25
+ "start": "node dist/server.js",
26
+ "bench": "npm run build && tsx ../../scripts/bench/qwen_vs_claude.ts"
27
+ },
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "1.29.0",
30
+ "@qwen-code/sdk": "0.1.7",
31
+ "pino": "10.3.1"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "25.6.0",
35
+ "tsx": "4.20.6",
36
+ "typescript": "6.0.3",
37
+ "vitest": "4.1.5"
38
+ },
39
+ "engines": {
40
+ "node": ">=20.0.0"
41
+ }
42
+ }