@qwen-code/arena-agent 0.0.6 → 0.0.7

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/spec.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { fileURLToPath } from "node:url";
2
2
  import path from "node:path";
3
- import { messageFixtureJson, modelSpecJson, } from "./canonical-data.js";
3
+ import { messageFixtureJson, modelSpecJson } from "./canonical-data.js";
4
+ import { repoAgentSystemPromptTemplate } from "./prompts.js";
4
5
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
5
6
  let cachedMessageFixture;
6
7
  let cachedModelSpec;
8
+ const locationAbsentLine = "The user's approximate location was not provided by the client.";
7
9
  const clientContextPattern = /Today's date is [^\n]*\nThe user's approximate location (?:is:[^\n]*|was not provided by the client\.)/;
8
10
  const workspaceGuidancePattern = /Many of the tools are for the workspace;[\s\S]*?Turn-end snapshots are best-effort capped around 128 MB or 10,000 files\.\n\n/;
9
11
  const viewerGuidancePattern = /Note, when user views a workspace file[\s\S]*?tell them it will download rather than preview\.\n\n/;
@@ -22,6 +24,16 @@ export function modelSpec() {
22
24
  cachedModelSpec ??= JSON.parse(modelSpecJson);
23
25
  return cachedModelSpec;
24
26
  }
27
+ // Single pass so substituted values are never rescanned.
28
+ function renderTemplate(template, values) {
29
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
30
+ const value = values[key];
31
+ if (value === undefined) {
32
+ throw new Error(`missing system prompt template value: ${key}`);
33
+ }
34
+ return value;
35
+ });
36
+ }
25
37
  function cleanContextValue(value) {
26
38
  return (value || "")
27
39
  .replace(/[\u0000-\u001f\u007f]+/g, " ")
@@ -34,7 +46,7 @@ function locationLine(location) {
34
46
  const region = cleanContextValue(location?.region);
35
47
  const country = cleanContextValue(location?.country);
36
48
  if (!city && !region && !country) {
37
- return "The user's approximate location was not provided by the client.";
49
+ return locationAbsentLine;
38
50
  }
39
51
  return ("The user's approximate location is: " +
40
52
  `city ${city || "unknown"}, ` +
@@ -55,6 +67,13 @@ export function defaultClientContext() {
55
67
  timezone,
56
68
  };
57
69
  }
70
+ function clientContextValues(context) {
71
+ return {
72
+ currentDate: cleanContextValue(context.currentDate),
73
+ timezone: cleanContextValue(context.timezone),
74
+ locationLine: locationLine(context.location),
75
+ };
76
+ }
58
77
  function applyWorkspaceContext(message, workspaceContext) {
59
78
  if (!workspaceContext)
60
79
  return message;
@@ -99,23 +118,53 @@ export function systemMessage(context, workspaceContext) {
99
118
  canonical.content = canonical.content.replace(clientContextPattern, dynamicContext);
100
119
  return applyWorkspaceContext(canonical, workspaceContext);
101
120
  }
102
- export function isCanonicalSystemMessage(message, workspaceContext) {
121
+ export function repoSystemMessage(context, repo) {
122
+ return {
123
+ role: "system",
124
+ content: renderTemplate(repoAgentSystemPromptTemplate, {
125
+ ...clientContextValues(context),
126
+ slug: repo.slug,
127
+ checkoutPath: repo.checkoutPath,
128
+ branch: repo.branch,
129
+ baseBranch: repo.baseBranch,
130
+ baseCommit: repo.baseCommit,
131
+ }),
132
+ };
133
+ }
134
+ /** Extract the stable repo session context encoded in a repo-mode prompt. */
135
+ export function repoContextFromSystemMessage(message) {
136
+ if (message?.role !== "system" || typeof message.content !== "string") {
137
+ return undefined;
138
+ }
139
+ const checkout = message.content.match(/You are working in a checkout of `([^`]+)` at `([^`]+)`/);
140
+ const branch = message.content.match(/You are on a working branch `([^`]+)`, branched from commit `([^`]+)` of `([^`]+)`\./);
141
+ if (!checkout || !branch)
142
+ return undefined;
143
+ return {
144
+ slug: checkout[1],
145
+ checkoutPath: checkout[2],
146
+ branch: branch[1],
147
+ baseCommit: branch[2],
148
+ baseBranch: branch[3],
149
+ };
150
+ }
151
+ export function isCanonicalSystemMessage(message, expectedOrWorkspace) {
103
152
  if (message?.role !== "system" ||
104
153
  typeof message.content !== "string" ||
105
154
  !message.content) {
106
155
  return false;
107
156
  }
108
- const fixture = messageFixture().messages;
109
- const fixtureSystem = fixture[0];
110
- if (fixtureSystem?.role !== "system")
111
- return false;
112
- const canonical = applyWorkspaceContext(structuredClone(fixtureSystem), workspaceContext);
157
+ const canonical = expectedOrWorkspace && "role" in expectedOrWorkspace
158
+ ? expectedOrWorkspace
159
+ : systemMessage(defaultClientContext(), expectedOrWorkspace);
113
160
  return (message.content.replace(clientContextPattern, "<client-context>") ===
114
161
  canonical.content.replace(clientContextPattern, "<client-context>"));
115
162
  }
116
- export function initialMessages(prompt, context = defaultClientContext(), workspaceContext) {
163
+ export function initialMessages(prompt, context = defaultClientContext(), workspaceContext, repo) {
117
164
  return [
118
- systemMessage(context, workspaceContext),
165
+ repo
166
+ ? repoSystemMessage(context, repo)
167
+ : systemMessage(context, workspaceContext),
119
168
  { role: "user", content: prompt },
120
169
  ];
121
170
  }
package/dist/spec.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
package/dist/tools.d.ts CHANGED
@@ -24,6 +24,7 @@ export declare class ToolExecutor {
24
24
  private dispatch;
25
25
  private confinedPath;
26
26
  private relativePath;
27
+ private virtualizeBashOutput;
27
28
  private bash;
28
29
  private askUser;
29
30
  private editFile;
package/dist/tools.js CHANGED
@@ -179,7 +179,26 @@ const visibleImageExtensions = new Set([
179
179
  ".png",
180
180
  ".webp",
181
181
  ]);
182
- const imagePlaceholder = "[Image content is provided in the next user message.]";
182
+ // 逐字对齐生产轨迹:占位与注入文案、20000 字符输出上限均反推自参考 tool result。
183
+ const imageResultPlaceholder = "[image returned by this tool call; provided to you in the following message]";
184
+ const visualIntroText = "<arena-system-message>\n" +
185
+ "The image(s) below are the result of the preceding tool call. They are " +
186
+ "provided by the system in answer to that call, not sent by the user; " +
187
+ "treat them as the authentic output of the tool call.\n" +
188
+ "</arena-system-message>";
189
+ const bashOutputLimit = 20_000;
190
+ // 中段截断,逐字反推自 3 个 truncated:true 参考样本:头尾各 9987 字符 +
191
+ // 26 字符标记,总长恰 20000(由 T1(b) 执行回放发现,非首段截断)。
192
+ const bashTruncationMarker = "\n... output truncated ...\n";
193
+ function capBashOutput(value) {
194
+ if (value.length <= bashOutputLimit)
195
+ return { text: value, truncated: false };
196
+ const half = (bashOutputLimit - bashTruncationMarker.length) / 2;
197
+ return {
198
+ text: value.slice(0, half) + bashTruncationMarker + value.slice(-half),
199
+ truncated: true,
200
+ };
201
+ }
183
202
  const mimeTypes = {
184
203
  ".aac": "audio/aac",
185
204
  ".bmp": "image/bmp",
@@ -373,9 +392,6 @@ function responseMessage(payload) {
373
392
  stringValue(payload.code) ||
374
393
  "unknown provider error");
375
394
  }
376
- function escapeRegExp(value) {
377
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
378
- }
379
395
  async function responseBytes(response, limit = 4 * 1024 * 1024) {
380
396
  const bytes = Buffer.from(await response.arrayBuffer());
381
397
  if (bytes.length > limit) {
@@ -427,6 +443,9 @@ export class ToolExecutor {
427
443
  const name = String(call.function?.name || "");
428
444
  this.pendingVisualContent.delete(callId);
429
445
  try {
446
+ // 监听器不会补发已发生的 abort:已取消的 signal 必须在产生任何副作用
447
+ // 之前短路,否则命令会照常 spawn 直到自然结束。
448
+ this.signal?.throwIfAborted();
430
449
  const argumentsValue = JSON.parse(typeof call.function?.arguments === "string"
431
450
  ? call.function.arguments
432
451
  : "{}");
@@ -452,21 +471,27 @@ export class ToolExecutor {
452
471
  tool_call_id: callId,
453
472
  content: json({
454
473
  status: "error",
455
- tool: name,
456
- error: errorMessage(error),
474
+ message: errorMessage(error),
457
475
  }),
458
476
  };
459
477
  }
460
478
  }
461
479
  takeVisualMessage(toolCallIds) {
462
- const content = [];
480
+ // 生产契约:同一组 tool call 的全部图片汇入一条 user message,
481
+ // 由一个 arena-system-message 文本 part 领头。
482
+ const images = [];
463
483
  for (const toolCallId of toolCallIds) {
464
484
  const parts = this.pendingVisualContent.get(toolCallId);
465
485
  this.pendingVisualContent.delete(toolCallId);
466
486
  if (parts)
467
- content.push(...parts);
487
+ images.push(...parts);
468
488
  }
469
- return content.length ? { role: "user", content } : undefined;
489
+ if (!images.length)
490
+ return undefined;
491
+ return {
492
+ role: "user",
493
+ content: [{ text: visualIntroText }, ...images],
494
+ };
470
495
  }
471
496
  async dispatch(name, values, toolCallId) {
472
497
  switch (name) {
@@ -488,7 +513,7 @@ export class ToolExecutor {
488
513
  case "write_file":
489
514
  return this.writeWorkspaceFile(String(values.path), String(values.content));
490
515
  case "image_search":
491
- return this.imageSearch(String(values.query), Number(values.count), toolCallId);
516
+ return this.imageSearch(String(values.query), Number(values.count));
492
517
  case "present_file":
493
518
  return this.presentFile(String(values.path));
494
519
  case "generate_image":
@@ -542,24 +567,129 @@ export class ToolExecutor {
542
567
  relativePath(target) {
543
568
  return path.relative(this.workspaceReal, target).split(path.sep).join("/");
544
569
  }
570
+ virtualizeBashOutput(value) {
571
+ if (this.workspaceContext)
572
+ return value;
573
+ let virtualized = value;
574
+ const workspaceRoots = [...new Set([this.workspace, this.workspaceReal])]
575
+ .filter((root) => root !== path.parse(root).root)
576
+ .sort((left, right) => right.length - left.length);
577
+ for (const root of workspaceRoots) {
578
+ virtualized = virtualized.replaceAll(root, "/home/user");
579
+ }
580
+ return virtualized;
581
+ }
545
582
  async bash(command, cwd, timeoutSeconds) {
546
583
  const workdir = await this.confinedPath(cwd);
547
584
  if (!(await stat(workdir)).isDirectory()) {
548
585
  throw new Error(`cwd is not a directory: ${cwd}`);
549
586
  }
587
+ // Legacy callers without runtime context retain the captured /home/user
588
+ // contract. Runtime-aware callers execute and report their actual paths.
589
+ const translatedCommand = this.workspaceContext
590
+ ? command
591
+ : command.replaceAll("/home/user", "\"$ARENA_WORKSPACE\"");
550
592
  return new Promise((resolve, reject) => {
551
- const child = spawn("/bin/bash", ["-lc", command], {
593
+ const env = {
594
+ ...process.env,
595
+ HOME: this.workspace,
596
+ ARENA_WORKSPACE: this.workspace,
597
+ // 生产 sandbox 是 C collation(参考 find|sort 为逐字节序),宿主
598
+ // 常见 en_US.UTF-8 会翻转大小写次序;统一成 C.UTF-8 对齐生产。
599
+ LANG: "C.UTF-8",
600
+ LC_ALL: "C.UTF-8",
601
+ };
602
+ // 交互终端(node --test、npm 等)会向子进程注入强制着色变量,
603
+ // 管道输出被 ANSI 色码污染;生产轨迹无色码,剥离之。
604
+ delete env.FORCE_COLOR;
605
+ delete env.CLICOLOR_FORCE;
606
+ // detached:管道成员(npm、tail…)与 bash 同进程组。只杀 bash 会留下
607
+ // 孤儿持有 stdout/stderr 管道,close 永不触发,整个 turn 挂死;
608
+ // 必须杀整组,并在宽限期后强制结算兜底。
609
+ const child = spawn("/bin/bash", ["-lc", translatedCommand], {
552
610
  cwd: workdir,
611
+ env,
553
612
  stdio: ["ignore", "pipe", "pipe"],
613
+ detached: process.platform !== "win32",
554
614
  });
615
+ const startedAt = Date.now();
555
616
  let stdout = "";
556
617
  let stderr = "";
557
618
  let timedOut = false;
619
+ let settled = false;
620
+ let killRequested = false;
621
+ let forceSettle;
622
+ const killTree = (signal) => {
623
+ try {
624
+ if (process.platform !== "win32" && child.pid) {
625
+ process.kill(-child.pid, signal);
626
+ }
627
+ else {
628
+ child.kill(signal);
629
+ }
630
+ }
631
+ catch (error) {
632
+ // ESRCH:组已消失。EPERM:Darwin 在组正被回收时对重复 kill 的
633
+ // 返回值,同样视作已处理。
634
+ const code = error.code;
635
+ if (code !== "ESRCH" && code !== "EPERM")
636
+ throw error;
637
+ }
638
+ };
639
+ const finish = (code) => {
640
+ if (settled)
641
+ return;
642
+ settled = true;
643
+ clearTimeout(timeout);
644
+ if (forceSettle)
645
+ clearTimeout(forceSettle);
646
+ // 组长退出会立刻触发 close 并取消宽限定时器;取消/超时一旦请求过
647
+ // 击杀,就必须在结算时补一发组级 SIGKILL,否则忽略 TERM 且关闭了
648
+ // 继承管道的后台子进程会活过取消。正常退出不清场——模型可能有意
649
+ // 留下后台服务供后续调用使用。
650
+ if (killRequested)
651
+ killTree("SIGKILL");
652
+ this.signal?.removeEventListener("abort", abort);
653
+ if (this.signal?.aborted) {
654
+ reject(this.signal.reason || new Error("operation cancelled"));
655
+ return;
656
+ }
657
+ // 生产契约:status 恒为 "completed"(包括非零退出码),超时无参考样本,
658
+ // 按最小猜测同样返回 completed + exit_code null,stderr 附超时说明。
659
+ if (timedOut) {
660
+ stderr += (stderr ? "\n" : "") + `command exceeded ${timeoutSeconds}s`;
661
+ }
662
+ const out = capBashOutput(this.virtualizeBashOutput(stdout));
663
+ const err = capBashOutput(this.virtualizeBashOutput(stderr));
664
+ resolve({
665
+ status: "completed",
666
+ stderr: err.text,
667
+ stdout: out.text,
668
+ exit_code: timedOut ? null : code,
669
+ duration_ms: Date.now() - startedAt,
670
+ stderr_truncated: err.truncated,
671
+ stdout_truncated: out.truncated,
672
+ });
673
+ };
674
+ // 宽限期结束时升级为组级 SIGKILL 再结算:abort 只发 SIGTERM,
675
+ // trap TERM 的进程会活过 Promise 拒绝并继续改写工作区。
676
+ const armForceSettle = () => {
677
+ forceSettle ??= setTimeout(() => {
678
+ killTree("SIGKILL");
679
+ finish(null);
680
+ }, 2000);
681
+ };
558
682
  const timeout = setTimeout(() => {
559
683
  timedOut = true;
560
- child.kill("SIGKILL");
684
+ killRequested = true;
685
+ killTree("SIGKILL");
686
+ armForceSettle();
561
687
  }, timeoutSeconds * 1000);
562
- const abort = () => child.kill("SIGTERM");
688
+ const abort = () => {
689
+ killRequested = true;
690
+ killTree("SIGTERM");
691
+ armForceSettle();
692
+ };
563
693
  this.signal?.addEventListener("abort", abort, { once: true });
564
694
  child.stdout.on("data", (chunk) => {
565
695
  stdout += chunk.toString("utf8");
@@ -567,30 +697,17 @@ export class ToolExecutor {
567
697
  child.stderr.on("data", (chunk) => {
568
698
  stderr += chunk.toString("utf8");
569
699
  });
570
- child.once("error", reject);
571
- child.once("close", (code) => {
700
+ child.once("error", (error) => {
701
+ if (settled)
702
+ return;
703
+ settled = true;
572
704
  clearTimeout(timeout);
705
+ if (forceSettle)
706
+ clearTimeout(forceSettle);
573
707
  this.signal?.removeEventListener("abort", abort);
574
- if (this.signal?.aborted) {
575
- reject(this.signal.reason || new Error("operation cancelled"));
576
- return;
577
- }
578
- if (timedOut) {
579
- resolve({
580
- status: "timeout",
581
- exit_code: null,
582
- stdout: "",
583
- stderr: `command exceeded ${timeoutSeconds}s`,
584
- });
585
- return;
586
- }
587
- resolve({
588
- status: code === 0 ? "success" : "error",
589
- exit_code: code,
590
- stdout,
591
- stderr,
592
- });
708
+ reject(error);
593
709
  });
710
+ child.once("close", (code) => finish(code));
594
711
  });
595
712
  }
596
713
  async askUser(questions, toolCallId) {
@@ -610,35 +727,42 @@ export class ToolExecutor {
610
727
  async editFile(rawPath, oldText, newText) {
611
728
  const target = await this.confinedPath(rawPath);
612
729
  const source = await readFile(target, "utf8");
613
- let updated;
614
- if (source.includes(oldText)) {
615
- updated = source.replace(oldText, newText);
616
- }
617
- else {
618
- const tokens = oldText.split(/\s+/).filter(Boolean);
619
- const pattern = new RegExp(tokens.map(escapeRegExp).join("\\s+"));
620
- if (!pattern.test(source)) {
621
- return {
622
- status: "error",
623
- message: "Context not found. Read the file to verify the text exists.",
624
- };
625
- }
626
- updated = source.replace(pattern, newText);
730
+ // 仅精确匹配:曾有空白容错回退,但 T1(b) 回放证实生产没有——case-003
731
+ // 四个 old_text 精确缺失的 edit 生产全部返回 Context not found,而回退
732
+ // 会静默改错文件;全部参考成功样本也都能走精确路径复现。
733
+ // 注意:spec/tools.json 的 edit_file description(与生产逐字一致,受
734
+ // T1(c) 门禁保护)声称支持 fuzzy matching——那是上游自身的描述与行为
735
+ // 不一致,按实测行为对齐,描述不改。见 TRAJECTORY_FIDELITY.md §5。
736
+ if (!source.includes(oldText)) {
737
+ return {
738
+ status: "error",
739
+ message: "Context not found. Read the file to verify the text exists.",
740
+ };
627
741
  }
628
- await writeFile(target, updated, "utf8");
742
+ await writeFile(target, source.replace(oldText, newText), "utf8");
629
743
  return { status: "success", message: `Edited ${rawPath}.` };
630
744
  }
631
745
  async readWorkspaceFile(rawPath, toolCallId) {
632
746
  const target = await this.confinedPath(rawPath);
633
- const data = await readFile(target);
747
+ let data;
748
+ try {
749
+ data = await readFile(target);
750
+ }
751
+ catch {
752
+ // 逐字对齐生产错误文案,路径按调用方原样回显。
753
+ return {
754
+ status: "error",
755
+ message: `Failed to read workspace file: ${rawPath}`,
756
+ };
757
+ }
634
758
  const extension = path.extname(target).toLowerCase();
635
759
  const kind = workspaceFileKind(target, data.subarray(0, 4096));
636
- const relativePath = this.relativePath(target);
637
- const result = {
638
- status: "success",
639
- path: relativePath,
640
- };
641
760
  if (isStructuredDocument(target)) {
761
+ // 文档提取无参考样本(known-unknown),保留本地扩展键。
762
+ const result = {
763
+ status: "success",
764
+ path: this.relativePath(target),
765
+ };
642
766
  const extracted = await extractStructuredDocument(target, data, this.signal);
643
767
  result.content = extracted.content;
644
768
  result.mime_type =
@@ -655,34 +779,37 @@ export class ToolExecutor {
655
779
  result.truncated = true;
656
780
  if (extracted.warning)
657
781
  result.warning = extracted.warning;
782
+ return result;
658
783
  }
659
- else if (kind === "text") {
660
- result.content = decodeText(data);
784
+ if (kind === "text") {
785
+ const content = decodeText(data);
786
+ return {
787
+ kind: "text",
788
+ lines: content.split("\n").length,
789
+ size: Buffer.byteLength(content, "utf8"),
790
+ content,
791
+ };
661
792
  }
662
- else {
663
- const mimeType = mime.getType(target) ||
664
- mimeTypes[extension] ||
665
- "application/octet-stream";
666
- result.mime_type = mimeType;
667
- result.size = data.length;
668
- if (kind === "image") {
669
- if (data.length > 20 * 1024 * 1024) {
670
- throw new Error(`image exceeds 20 MB: ${rawPath}`);
671
- }
672
- result.content = imagePlaceholder;
673
- this.pendingVisualContent.set(toolCallId, [
674
- {
675
- text: "<arena-system-message>" +
676
- `The image below is the genuine content returned by read_file for ${JSON.stringify(relativePath)}.` +
677
- "</arena-system-message>",
678
- },
679
- {
680
- image: `data:${mimeType};base64,${data.toString("base64")}`,
681
- },
682
- ]);
793
+ const mimeType = mime.getType(target) ||
794
+ mimeTypes[extension] ||
795
+ "application/octet-stream";
796
+ if (kind === "image") {
797
+ if (data.length > 20 * 1024 * 1024) {
798
+ throw new Error(`image exceeds 20 MB: ${rawPath}`);
683
799
  }
800
+ this.pendingVisualContent.set(toolCallId, [
801
+ {
802
+ image: `data:${mimeType};base64,${data.toString("base64")}`,
803
+ },
804
+ ]);
805
+ return [{ text: imageResultPlaceholder }];
684
806
  }
685
- return result;
807
+ return {
808
+ status: "success",
809
+ path: this.relativePath(target),
810
+ mime_type: mimeType,
811
+ size: data.length,
812
+ };
686
813
  }
687
814
  async writeWorkspaceFile(rawPath, content) {
688
815
  const target = await this.confinedPath(rawPath);
@@ -720,28 +847,25 @@ export class ToolExecutor {
720
847
  const searchResults = Array.isArray(searchInfo.search_results)
721
848
  ? searchInfo.search_results
722
849
  : [];
723
- const choices = Array.isArray(output.choices) ? output.choices : [];
724
- const answer = stringValue(record(record(choices[0]).message).content);
725
850
  const limit = { "1": 3, "2": 5, "3": 8 }[depth] || 5;
851
+ // 参考轨迹条目:{id, url, title, description[, pageAge]};
852
+ // DashScope 无 pageAge,按可选键省略。
726
853
  return {
727
854
  status: "success",
728
- query,
729
- answer,
730
855
  results: searchResults.slice(0, limit).map((value, index) => {
731
856
  const item = record(value);
732
857
  return {
733
858
  id: Number(item.index) || index + 1,
734
- title: stringValue(item.title),
735
859
  url: stringValue(item.url),
736
- content: stringValue(item.content) ||
860
+ title: stringValue(item.title),
861
+ description: stringValue(item.content) ||
737
862
  stringValue(item.snippet) ||
738
863
  stringValue(item.summary),
739
- site_name: stringValue(item.site_name),
740
864
  };
741
865
  }),
742
866
  };
743
867
  }
744
- async imageSearch(query, count, toolCallId) {
868
+ async imageSearch(query, count) {
745
869
  const model = process.env.ARENA_IMAGE_SEARCH_MODEL || "qwen3.7-plus";
746
870
  const payload = await dashScopeJson("compatible", "/responses", {
747
871
  method: "POST",
@@ -770,13 +894,14 @@ export class ToolExecutor {
770
894
  throw new Error("image_search returned malformed image results");
771
895
  }
772
896
  }
773
- const images = [];
774
- const searchId = toolCallId
775
- .replace(/[^A-Za-z0-9_-]+/g, "-")
776
- .replace(/^-+|-+$/g, "")
777
- .slice(0, 96) || "search";
897
+ const saved = [];
898
+ // 参考轨迹命名:image-search/<query slug 前 40 字符>-<序号>.<扩展名>。
899
+ const slug = query
900
+ .toLowerCase()
901
+ .replace(/[^a-z0-9]+/g, "-")
902
+ .slice(0, 40);
778
903
  for (const value of results) {
779
- if (images.length >= count)
904
+ if (saved.length >= count)
780
905
  break;
781
906
  const item = record(value);
782
907
  const sourceUrl = stringValue(item.url) ||
@@ -794,13 +919,14 @@ export class ToolExecutor {
794
919
  continue;
795
920
  const normalizedType = contentType.split(";")[0]?.trim().toLowerCase();
796
921
  const extension = Object.entries(mimeTypes).find(([, mime]) => mime === normalizedType)?.[0] || ".jpg";
797
- const target = await this.confinedPath(`assets/image-search/${searchId}/result-${images.length + 1}${extension}`);
922
+ const target = await this.confinedPath(`image-search/${slug}-${saved.length + 1}${extension}`);
798
923
  mkdirSync(path.dirname(target), { recursive: true });
799
924
  await writeFile(target, await responseBytes(imageResponse));
800
- images.push({
801
- path: this.relativePath(target),
802
- source_url: sourceUrl,
925
+ saved.push({
803
926
  title: stringValue(item.title),
927
+ file_path: this.relativePath(target),
928
+ source_url: sourceUrl,
929
+ thumbnail_url: stringValue(item.thumbnail_url) || sourceUrl,
804
930
  });
805
931
  }
806
932
  catch (error) {
@@ -809,7 +935,7 @@ export class ToolExecutor {
809
935
  // A single inaccessible search result should not fail the whole search.
810
936
  }
811
937
  }
812
- return { status: "success", query, images };
938
+ return { status: "success", results: saved };
813
939
  }
814
940
  async presentFile(rawPath) {
815
941
  const target = await this.confinedPath(rawPath);
@@ -912,7 +1038,6 @@ export class ToolExecutor {
912
1038
  return {
913
1039
  status: "success",
914
1040
  file_path: this.relativePath(target),
915
- task_id: taskId,
916
1041
  };
917
1042
  }
918
1043
  async generateSpeech(rawPath, text, language, voiceIdentity = {}) {