@qwen-code/arena-agent 0.0.10-dev.5 → 0.0.10-dev.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.
Files changed (101) hide show
  1. package/.env.example +19 -10
  2. package/CHANGELOG.md +160 -0
  3. package/README.md +50 -37
  4. package/dist/agent.d.ts +2 -2
  5. package/dist/agent.js +62 -96
  6. package/dist/agent.js.map +1 -1
  7. package/dist/anthropic.js +2 -0
  8. package/dist/anthropic.js.map +1 -1
  9. package/dist/ask-user.js +2 -0
  10. package/dist/ask-user.js.map +1 -1
  11. package/dist/attachments.js +4 -3
  12. package/dist/attachments.js.map +1 -1
  13. package/dist/cli.js +9 -8
  14. package/dist/cli.js.map +1 -1
  15. package/dist/code-tools.js +122 -30
  16. package/dist/code-tools.js.map +1 -1
  17. package/dist/compaction.d.ts +4 -2
  18. package/dist/compaction.js +37 -5
  19. package/dist/compaction.js.map +1 -1
  20. package/dist/config.d.ts +0 -2
  21. package/dist/config.js +2 -3
  22. package/dist/config.js.map +1 -1
  23. package/dist/file-edit.d.ts +2 -0
  24. package/dist/file-edit.js +9 -4
  25. package/dist/file-edit.js.map +1 -1
  26. package/dist/history.d.ts +7 -6
  27. package/dist/history.js +41 -46
  28. package/dist/history.js.map +1 -1
  29. package/dist/mode.js +3 -1
  30. package/dist/mode.js.map +1 -1
  31. package/dist/model-router-media.d.ts +1 -0
  32. package/dist/model-router-media.js +71 -1
  33. package/dist/model-router-media.js.map +1 -1
  34. package/dist/openai-responses.js +2 -0
  35. package/dist/openai-responses.js.map +1 -1
  36. package/dist/provider.d.ts +0 -1
  37. package/dist/provider.js +12 -41
  38. package/dist/provider.js.map +1 -1
  39. package/dist/replay-responses.d.ts +17 -0
  40. package/dist/replay-responses.js +100 -0
  41. package/dist/replay-responses.js.map +1 -0
  42. package/dist/routify-web-fetch.js +18 -4
  43. package/dist/routify-web-fetch.js.map +1 -1
  44. package/dist/server.js +7 -7
  45. package/dist/server.js.map +1 -1
  46. package/dist/spec.js +2 -0
  47. package/dist/spec.js.map +1 -1
  48. package/dist/speech-audio.d.ts +9 -0
  49. package/dist/speech-audio.js +130 -0
  50. package/dist/speech-audio.js.map +1 -0
  51. package/dist/speech-model.d.ts +3 -0
  52. package/dist/speech-model.js +8 -0
  53. package/dist/speech-model.js.map +1 -0
  54. package/dist/tools.js +81 -11
  55. package/dist/tools.js.map +1 -1
  56. package/dist/tui.js +6 -9
  57. package/dist/tui.js.map +1 -1
  58. package/dist/types.d.ts +16 -2
  59. package/dist/voice-registry.d.ts +2 -0
  60. package/dist/voice-registry.js +10 -2
  61. package/dist/voice-registry.js.map +1 -1
  62. package/dist/web-fetch-output.d.ts +9 -0
  63. package/dist/web-fetch-output.js +38 -0
  64. package/dist/web-fetch-output.js.map +1 -0
  65. package/docs/BINARY_DISTRIBUTION.md +25 -16
  66. package/docs/MODES_AND_RELEASES.md +28 -5
  67. package/docs/SDK.md +4 -0
  68. package/package.json +9 -3
  69. package/scripts/README.md +62 -0
  70. package/scripts/check-changelog.mjs +66 -0
  71. package/vendor/audio/darwin-arm64/FFmpeg.LICENSE +502 -0
  72. package/vendor/audio/darwin-arm64/LAME.LICENSE +481 -0
  73. package/vendor/audio/darwin-arm64/Opus.LICENSE +44 -0
  74. package/vendor/audio/darwin-arm64/ffmpeg.gz +0 -0
  75. package/vendor/audio/darwin-arm64/manifest.json +39 -0
  76. package/vendor/audio/darwin-x64/FFmpeg.LICENSE +502 -0
  77. package/vendor/audio/darwin-x64/LAME.LICENSE +481 -0
  78. package/vendor/audio/darwin-x64/Opus.LICENSE +44 -0
  79. package/vendor/audio/darwin-x64/ffmpeg.gz +0 -0
  80. package/vendor/audio/darwin-x64/manifest.json +39 -0
  81. package/vendor/audio/linux-arm64/FFmpeg.LICENSE +502 -0
  82. package/vendor/audio/linux-arm64/LAME.LICENSE +481 -0
  83. package/vendor/audio/linux-arm64/Opus.LICENSE +44 -0
  84. package/vendor/audio/linux-arm64/ffmpeg.gz +0 -0
  85. package/vendor/audio/linux-arm64/manifest.json +39 -0
  86. package/vendor/audio/linux-x64/FFmpeg.LICENSE +502 -0
  87. package/vendor/audio/linux-x64/LAME.LICENSE +481 -0
  88. package/vendor/audio/linux-x64/Opus.LICENSE +44 -0
  89. package/vendor/audio/linux-x64/ffmpeg.gz +0 -0
  90. package/vendor/audio/linux-x64/manifest.json +39 -0
  91. package/vendor/audio/sources/build-audio-encoder.sh +76 -0
  92. package/vendor/audio/sources/ffmpeg-7.0.2.tar.xz +0 -0
  93. package/vendor/audio/sources/lame-3.100.tar.gz +0 -0
  94. package/vendor/audio/sources/opus-1.5.2.tar.gz +0 -0
  95. package/vendor/audio/sources/pkgconf-2.3.0.tar.xz +0 -0
  96. package/vendor/audio/sources/sources.lock.json +30 -0
  97. package/vendor/audio/win32-x64/FFmpeg.LICENSE +502 -0
  98. package/vendor/audio/win32-x64/LAME.LICENSE +481 -0
  99. package/vendor/audio/win32-x64/Opus.LICENSE +44 -0
  100. package/vendor/audio/win32-x64/ffmpeg.gz +0 -0
  101. package/vendor/audio/win32-x64/manifest.json +39 -0
@@ -7,6 +7,7 @@ export interface VoiceAuditionCandidate {
7
7
  export interface VoiceBattle {
8
8
  battleId: string;
9
9
  candidates: VoiceAuditionCandidate[];
10
+ model?: string;
10
11
  language: string;
11
12
  }
12
13
  export interface RegisteredVoiceSelection {
@@ -16,6 +17,7 @@ export interface RegisteredVoiceSelection {
16
17
  interface RegisteredVoice {
17
18
  language: string;
18
19
  providerVoice: string;
20
+ model: string;
19
21
  }
20
22
  export declare class VoiceRegistry {
21
23
  private battleSequence;
@@ -1,10 +1,14 @@
1
1
  import { generateSpeechWithModelRouter } from "./model-router-media.js";
2
+ import { isGeminiSpeechModel, speechModel } from "./speech-model.js";
2
3
  const registries = new Map();
3
4
  function primaryLanguage(language) {
4
5
  return language.toLowerCase().split(/[-_]/)[0] || "";
5
6
  }
6
7
  function voiceCatalog(language, gender) {
7
8
  const masculine = gender === "masculine";
9
+ if (isGeminiSpeechModel(speechModel())) {
10
+ return masculine ? ["Puck", "Charon"] : ["Kore", "Aoede"];
11
+ }
8
12
  if (primaryLanguage(language) === "en") {
9
13
  return masculine
10
14
  ? ["en_male_tim_uranus_bigtts", "zh_male_m191_uranus_bigtts"]
@@ -31,9 +35,11 @@ export class VoiceRegistry {
31
35
  async audition(text, language, voiceIdentity = {}, signal) {
32
36
  const battleId = this.nextBattleId();
33
37
  const voices = selectedVoices(language, voiceIdentity);
38
+ const model = speechModel();
34
39
  const audio = await Promise.all(voices.map((voice) => generateSpeechWithModelRouter({
35
40
  format: "mp3",
36
- model: process.env.ARENA_SPEECH_MODEL || "doubao-tts",
41
+ model,
42
+ language,
37
43
  signal,
38
44
  text,
39
45
  voice,
@@ -41,6 +47,7 @@ export class VoiceRegistry {
41
47
  return {
42
48
  battleId,
43
49
  language,
50
+ model,
44
51
  candidates: voices.map((providerVoice, index) => ({
45
52
  id: `candidate-${index + 1}`,
46
53
  label: `Voice ${index + 1}`,
@@ -57,6 +64,7 @@ export class VoiceRegistry {
57
64
  this.voices.set(voiceId, {
58
65
  language: battle.language,
59
66
  providerVoice: candidate.providerVoice,
67
+ model: battle.model || speechModel(),
60
68
  });
61
69
  return voiceId;
62
70
  }
@@ -64,7 +72,7 @@ export class VoiceRegistry {
64
72
  const battleId = this.nextBattleId();
65
73
  const voiceId = `voice-${battleId}`;
66
74
  const providerVoice = selectedVoices(language, voiceIdentity)[0];
67
- this.voices.set(voiceId, { language, providerVoice });
75
+ this.voices.set(voiceId, { language, providerVoice, model: speechModel() });
68
76
  return { battleId, voiceId };
69
77
  }
70
78
  resolve(voiceId) {
@@ -1 +1 @@
1
- {"version":3,"file":"voice-registry.js","sourceRoot":"","sources":["../src/voice-registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"voice-registry.js","sourceRoot":"","sources":["../src/voice-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare function truncateCodeArenaText(value: string, maxBytes?: number): string;
2
+ export declare function boundedWebFetchResult(result: {
3
+ title?: unknown;
4
+ content?: unknown;
5
+ }): {
6
+ status: "success";
7
+ title: string;
8
+ content: string;
9
+ };
@@ -0,0 +1,38 @@
1
+ const defaultMaxBytes = 40_000;
2
+ const maxTitleBytes = 512;
3
+ function configuredMaxBytes() {
4
+ const value = process.env.CODE_HARNESS_WEB_FETCH_MAX_BYTES?.trim() || "";
5
+ const parsed = /^\d+$/.test(value) ? Number(value) : NaN;
6
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : defaultMaxBytes;
7
+ }
8
+ export function truncateCodeArenaText(value, maxBytes = defaultMaxBytes) {
9
+ const bytes = Buffer.from(value, "utf8");
10
+ if (bytes.length <= maxBytes)
11
+ return value;
12
+ // Captures retain ~40 KB with symmetric head/tail and this marker. The
13
+ // omitted-token count is an estimate (UTF-8 bytes / 4), not model tokenization.
14
+ const omittedTokens = Math.ceil((bytes.length - maxBytes) / 4);
15
+ let notice = `\n…${omittedTokens} tokens truncated…\n`;
16
+ if (Buffer.byteLength(notice) > maxBytes)
17
+ notice = maxBytes >= 3 ? "…" : ".";
18
+ const half = Math.floor((maxBytes - Buffer.byteLength(notice)) / 2);
19
+ let headEnd = half;
20
+ let tailStart = bytes.length - half;
21
+ // Move cut points inward to preserve complete UTF-8 characters and the bound.
22
+ while (headEnd > 0 && (bytes[headEnd] & 0xc0) === 0x80)
23
+ headEnd -= 1;
24
+ while (tailStart < bytes.length && (bytes[tailStart] & 0xc0) === 0x80)
25
+ tailStart += 1;
26
+ return bytes.subarray(0, headEnd).toString("utf8") + notice +
27
+ bytes.subarray(tailStart).toString("utf8");
28
+ }
29
+ // Apply at the Code Arena tool boundary so local and Routify readers share the
30
+ // same bound. Agent Arena fetch_page retains its full, separately paged cache.
31
+ export function boundedWebFetchResult(result) {
32
+ return {
33
+ status: "success",
34
+ title: truncateCodeArenaText(String(result.title || "No Title Found"), maxTitleBytes),
35
+ content: truncateCodeArenaText(String(result.content || ""), configuredMaxBytes()),
36
+ };
37
+ }
38
+ //# sourceMappingURL=web-fetch-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-fetch-output.js","sourceRoot":"","sources":["../src/web-fetch-output.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,SAAS,kBAAkB;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,gCAAgC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACzE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACzD,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAa,EAAE,QAAQ,GAAG,eAAe;IAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3C,uEAAuE;IACvE,gFAAgF;IAChF,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,IAAI,MAAM,GAAG,MAAM,aAAa,sBAAsB,CAAC;IACvD,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,QAAQ;QAAE,MAAM,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IACpC,8EAA8E;IAC9E,OAAO,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAE,GAAG,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC;IACtE,OAAO,SAAS,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAE,GAAG,IAAI,CAAC,KAAK,IAAI;QAAE,SAAS,IAAI,CAAC,CAAC;IACvF,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM;QACzD,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,MAAM,UAAU,qBAAqB,CAAC,MAGrC;IACC,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,gBAAgB,CAAC,EAAE,aAAa,CAAC;QACrF,OAAO,EAAE,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC;KACnF,CAAC;AACJ,CAAC"}
@@ -102,9 +102,9 @@ npm run package:binary
102
102
  ```
103
103
 
104
104
  默认命令构建当前 runner 的系统、CPU 和 libc。CI 还支持在 Linux x64 上按 Node
105
- SEA 跨平台流程生成三个关键目标:`linux-x64` 固定下载完全相同版本的 Node.js
105
+ SEA 跨平台流程生成四个发布目标:`linux-x64` 固定下载完全相同版本的 Node.js
106
106
  unofficial-builds glibc 2.17 Runtime,`linux-x64-musl` 下载 unofficial-builds
107
- musl Runtime,`darwin-x64` 下载官方 macOS x64 Runtime。三者都通过 Aone 可访问的
107
+ musl Runtime,`darwin-arm64` 和 `darwin-x64` 下载对应架构的官方 macOS Runtime。四者都通过 Aone 可访问的
108
108
  npmmirror 二进制镜像下载,并校验对应的
109
109
  `SHASUMS256.txt`,关闭 code cache/snapshot 后注入 SEA blob。
110
110
  macOS 产物再使用固定版本和 checksum 的 `rcodesign` 做 ad-hoc 签名。Aone 从仓库的
@@ -134,29 +134,33 @@ node scripts/package-binaries.mjs \
134
134
  ```
135
135
 
136
136
  CI 应构建并汇总 `build/npm/packages/<target>`。原生 Runtime 通常应在对应环境中
137
- 构建;禁用 SEA snapshot/code cache 后,`linux-x64`、`linux-x64-musl` 和
138
- `darwin-x64` 都可以在 Linux x64 Runner 上使用经过校验的目标 Node Runtime
137
+ 构建;禁用 SEA snapshot/code cache 后,`linux-x64`、`linux-x64-musl`、
138
+ `darwin-arm64` 和 `darwin-x64` 都可以在 Linux x64 Runner 上使用经过校验的目标 Node Runtime
139
139
  组装。
140
140
 
141
141
  本仓库使用 Aone CI,流水线定义在
142
142
  [`/.aoneci/arena-binaries.yaml`](../.aoneci/arena-binaries.yaml)。当前项目没有关联
143
143
  自托管 Runner 组,因此流水线优先使用 Aone 托管的 Linux amd64 资源构建
144
- `linux-x64`,并交叉构建 `linux-x64-musl` 和 `darwin-x64`。推送与根
144
+ `linux-x64`,并交叉构建 `linux-x64-musl`、`darwin-arm64` 和 `darwin-x64`。推送与根
145
145
  `package.json` 版本完全一致的 `v<semver>` Tag 时会自动运行,并在同一批次
146
- 发布这三个关键平台包、`@qwen-code/arena-agent` 和
146
+ 发布这四个平台包、`@qwen-code/arena-agent` 和
147
147
  `@qwen-code/arena-cli`;后两个包都包含四种模式和两个命令入口。普通分支 push
148
148
  不触发,手动运行只构建、不发布。发布前
149
149
  需要在“设置 → 持续集成 → 变量管理”中添加名为 `NPM_TOKEN` 的 Secret。
150
150
 
151
- 例如,预发布版自动使用 npm `dev` tag:
151
+ 每次发布前先按[发版流程](MODES_AND_RELEASES.md#发布节奏)更新
152
+ [`CHANGELOG.md`](../CHANGELOG.md)。以下以新的预发布版 `0.0.10-dev.7` 为例,
153
+ 发布脚本自动使用 npm `dev` tag:
152
154
 
153
155
  ```bash
154
- npm version 0.0.10-dev.1 --no-git-tag-version
155
- git add package.json package-lock.json
156
- git commit -m "chore: release 0.0.10-dev.1"
156
+ npm version 0.0.10-dev.7 --no-git-tag-version
157
+ # 将本次变更整理到 CHANGELOG.md 的 0.0.10-dev.7 条目,填写日期并更新比较链接
158
+ npm run check:changelog
159
+ git add package.json package-lock.json CHANGELOG.md
160
+ git commit -m "chore: release 0.0.10-dev.7"
157
161
  git push origin master
158
- git tag v0.0.10-dev.1
159
- git push origin v0.0.10-dev.1
162
+ git tag v0.0.10-dev.7
163
+ git push origin v0.0.10-dev.7
160
164
  ```
161
165
 
162
166
  正式版同理,例如 `package.json` 为 `0.0.10` 时推送 `v0.0.10`,发布脚本会自动
@@ -164,16 +168,21 @@ git push origin v0.0.10-dev.1
164
168
  发布到 npm。首次稳定版发布前请显式使用 `@dev` 或完整版本号;首个正式版本发布后
165
169
  才把无版本安装渠道作为稳定入口。
166
170
 
167
- Aone 官方不提供 Windows/macOS 托管资源;`darwin-x64` 已通过上述交叉构建覆盖。
171
+ Aone 官方不提供 Windows/macOS 托管资源;macOS 两个架构通过上述交叉构建覆盖。
168
172
  Linux arm64 的两个平台包暂不参与自动发布,以免非关键 ARM64 Job 阻塞 x64
169
- 发布。发布 `darwin-arm64` 和 Windows 包前,仍需要关联对应的 self-hosted Runner
170
- 组。
173
+ 发布。Windows 包仍需要关联对应的 self-hosted Runner 组。
171
174
 
172
175
  ## 发布
173
176
 
177
+ 发布前必须有与根 `package.json` 版本一致、带有效日期及实际变更条目的 changelog,
178
+ 并按 Agent Arena、Code Arena、通用分别记录变更。
179
+ `npm publish` 和多平台发布脚本均执行此检查,缺失时立即失败;多平台脚本在发布
180
+ 第一个平台包前检查,避免只发布部分包后才发现变更记录缺失。
181
+
174
182
  汇总全部 7 个平台包目录后,重新生成一次入口包并进行发布前检查:
175
183
 
176
184
  ```bash
185
+ npm run check:changelog
177
186
  npm run package:binary:cli
178
187
  npm run publish:binaries -- --dry-run --tag dev
179
188
  ```
@@ -201,7 +210,7 @@ SEA 产物内含目标平台的 Node.js 可执行文件,因此需要为每种
201
210
  - Linux arm64/x64 的 glibc 与 musl
202
211
  - Windows x64
203
212
 
204
- 除流水线已覆盖的三个关键 x64 目标外,其他目标应在对应操作系统和 CPU 架构的
213
+ 除流水线已覆盖的四个发布目标外,其他目标应在对应操作系统和 CPU 架构的
205
214
  构建机或 CI runner 上执行。
206
215
 
207
216
  “业务机不需要 Node.js 20+”不等于“二进制不受 Node.js 20 的系统要求影响”。
@@ -8,7 +8,7 @@
8
8
  agent loop、compaction、history、metrics、CLI/TUI/server 和 SDK。
9
9
  - 模式边界只包含 system prompt、工具 schema/实现以及可选 scaffold。
10
10
  - 四种模式使用同一个版本号一起演进,避免两份 agent loop 再次漂移。
11
- - JavaScript/SDK 包与 SEA 二进制包是两种分发形式,可以按需要分别发布;
11
+ - JavaScript/SDK 包与 SEA 二进制包是两种分发形式,共用发布版本;
12
12
  它们不是两套产品源码,也不要求 npm workspaces。
13
13
 
14
14
  ## 目录
@@ -89,10 +89,30 @@ arena --mode full-stack -p '...'
89
89
 
90
90
  ## 发布节奏
91
91
 
92
- 可以只发布 JavaScript/SDK 包,也可以在随后完成 7 个平台的 SEA 包和 launcher;
93
- 但不建议单独发布某一个模式。推荐顺序:
92
+ 每次发布(包括 dev 预发布)都必须先更新根目录的
93
+ [`CHANGELOG.md`](../CHANGELOG.md),并与版本号修改一起提交:
94
+
95
+ 1. 将 `Unreleased` 中本次要发布的变更整理为 `## [版本号] - YYYY-MM-DD`,
96
+ 版本号与 `package.json` 完全一致,并说明配置迁移。每个版本及 `Unreleased`
97
+ 内按 `### Agent Arena`、`### Code Arena`、`### 通用` 分组;两类 Arena 的专属
98
+ 变更分别记录,共用运行时、模型连接和 CLI 变化只写在通用部分。无变更的
99
+ 分类省略,分类内可按需用四级标题区分新增、变更、修复。
100
+ 2. 保留顶部 `## [Unreleased]` 供后续记录,更新新版本及 `Unreleased` 的比较链接。
101
+ 3. 完成下面的发布检查,再提交版本变更和 changelog、推送匹配的 `v<version>` 标签。
102
+
103
+ Changelog 只记录用户使用 CLI 时能感知的功能、行为、修复、配置、安装兼容性和
104
+ 迁移要求。内部技能、回放与审计工具、报告、测试、发布流程维护、源码归档及
105
+ 构建下载诊断不写入 changelog。
106
+
107
+ `npm run check:changelog` 校验最新版本条目、有效日期和非空变更内容。
108
+ 直接 `npm publish` 会通过 `prepublishOnly` 执行检查;多平台发布脚本(含 dry-run)
109
+ 也会在任何包发布前执行检查,Aone 标签发布走同一入口。
110
+
111
+ 四种模式一起发布。Agent、CLI 和所支持的平台包必须使用同一版本;只发布
112
+ JavaScript/SDK 包不算发版完成。检查及打包顺序:
94
113
 
95
114
  ```bash
115
+ npm run check:changelog
96
116
  npm run typecheck
97
117
  npm test
98
118
  npm pack --dry-run
@@ -105,8 +125,11 @@ npm run package:binary:cli
105
125
  npm run publish:binaries -- --dry-run
106
126
  ```
107
127
 
108
- 平台包必须先于 `@qwen-code/arena-cli` 发布。完整矩阵见
109
- [`BINARY_DISTRIBUTION.md`](BINARY_DISTRIBUTION.md)。
128
+ 平台包必须先于 `@qwen-code/arena-cli` 发布。推送标签后,核验 Agent、CLI 和所有
129
+ 受支持平台包的 exact-version npm 查询,至少覆盖 `darwin-arm64`、`darwin-x64`、
130
+ `linux-x64`、`linux-x64-musl`,并用该完整版本号的 `install.sh` 做端到端烟测。
131
+ 预发布还需确认相关包的 `dev` dist-tag 均指向最新预发布版本。
132
+ 完整矩阵及发布命令见 [`BINARY_DISTRIBUTION.md`](BINARY_DISTRIBUTION.md)。
110
133
 
111
134
  ## 原 Code Harness 的迁移边界
112
135
 
package/docs/SDK.md CHANGED
@@ -173,6 +173,10 @@ console.log(history.activePrompt);
173
173
  `getHistory()` 同时会设置 SSE 续传需要的 `lastEventId`。恢复后再调用
174
174
  `run()`、`stream()` 或 `connect()` 即可继续。
175
175
 
176
+ `history.messages` 与运行结果的 `messages` 只包含对话历史(含 compaction),
177
+ 不包含运行时生成的开头 system;中途的 system 消息仍会保留。服务端在每次运行时
178
+ 按当前配置生成开头 system,并只将它加入模型请求。
179
+
176
180
  当前服务端还没有用户账号维度的 `sessions.list()`/`sessions.open()`。
177
181
  跨设备登录应由服务端把 session 绑定到可信的 `userId`,并在用户登录后重新
178
182
  签发短期 session token,而不是在设备之间同步旧 token。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwen-code/arena-agent",
3
- "version": "0.0.10-dev.5",
3
+ "version": "0.0.10-dev.7",
4
4
  "description": "A four-mode TypeScript agent harness for Arena and Code Arena traces.",
5
5
  "type": "module",
6
6
  "types": "./dist/sdk.d.ts",
@@ -29,7 +29,10 @@
29
29
  "docs/BINARY_DISTRIBUTION.md",
30
30
  "modes",
31
31
  "web",
32
+ "vendor/audio",
32
33
  "README.md",
34
+ "CHANGELOG.md",
35
+ "scripts/check-changelog.mjs",
33
36
  ".env.example"
34
37
  ],
35
38
  "engines": {
@@ -46,11 +49,14 @@
46
49
  "package:binary:platform": "npm run build:binary && node scripts/package-binaries.mjs --skip-cli",
47
50
  "package:binary:cli": "node scripts/package-binaries.mjs --cli-only",
48
51
  "publish:binaries": "node scripts/publish-binaries.mjs",
52
+ "check:changelog": "node scripts/check-changelog.mjs",
53
+ "prepublishOnly": "npm run check:changelog",
49
54
  "dev": "tsx src/cli.ts serve",
50
55
  "tui": "tsx src/cli.ts tui",
51
- "prepack": "npm run build",
56
+ "prepare:audio": "node scripts/prepare-audio.mjs",
57
+ "prepack": "npm run build && node scripts/prepare-audio.mjs --all",
52
58
  "start": "node dist/cli.js serve",
53
- "pretest": "npm run build",
59
+ "pretest": "npm run build && npm run prepare:audio",
54
60
  "test": "tsx --test test/*.test.ts",
55
61
  "test:tool-replay": "ARENA_TOOL_REPLAY_TESTS=1 tsx --test test/tool-replay.test.ts",
56
62
  "scaffolds:validate-inputs": "tsx scripts/validate-scaffold-inputs.ts",
@@ -0,0 +1,62 @@
1
+ # 轨迹复刻脚本
2
+
3
+ 本目录保存 DataWorks SQL Arena 轨迹数据的复刻与环境准备脚本。数据集位于
4
+ `tasks/`,运行产物位于 `tasks/reproductions/`。
5
+
6
+ ## 通用脚本
7
+
8
+ `reproduce_case.mjs` 运行单个 case,`reproduce_all.mjs`
9
+ 批量运行数据集中标记为可复刻的 case。
10
+
11
+ 每个 case 可以通过可选的 `prepare.json` 声明运行前环境准备。没有配置时
12
+ prepare phase 是 no-op;存在配置时,单任务和批量入口都会在调用模型前自动
13
+ 执行并校验。当前支持固定 Git commit、从原始 history 恢复附件和文件断言。
14
+
15
+ ```bash
16
+ # 单个 case;默认数据集可省略 --dataset
17
+ node scripts/reproduce_case.mjs --case 010
18
+
19
+ # 指定其他数据集
20
+ node scripts/reproduce_case.mjs \
21
+ --dataset /path/to/dataset \
22
+ --case 010
23
+
24
+ # 以 5 个并发运行全部可复刻 case
25
+ node scripts/reproduce_all.mjs \
26
+ --dataset /path/to/dataset \
27
+ --concurrency 5
28
+ ```
29
+
30
+ 单独验证某个 prepare 配置,不调用模型:
31
+
32
+ ```bash
33
+ node scripts/prepare_workspace.mjs \
34
+ --case-dir /path/to/dataset/cases/case-xxx \
35
+ --workspace /path/to/empty/workspace
36
+ ```
37
+
38
+ 脚本读取项目根目录 `.env` 中的 `ARENA_API_MODEL`、`OPENAI_BASE_URL` 和
39
+ `OPENAI_API_KEY`。固定生成参数由数据集自身的 `reproduction.config.json`
40
+ 提供。
41
+
42
+ 批量入口具有以下行为:
43
+
44
+ - 每个失败 case 自动重试一次。
45
+ - 第一次失败后删除该次失败目录。
46
+ - 成功或最终失败后,只保留该 case 最新的最终运行目录。
47
+ - prepare 失败时不会调用模型,错误会写入该次 `result.json`。
48
+ - 单次运行默认 60 分钟超时,可通过 `--timeout-minutes` 或
49
+ `ARENA_REPRO_TIMEOUT_MINUTES` 调整。
50
+ - `--case` 的退出码只反映本次指定 case,不受历史失败记录影响。
51
+ - `--clean` 仅在数据集清单和可复刻性审计验证通过后清理
52
+ `reproductions/`。
53
+
54
+ 完整清理后重新运行:
55
+
56
+ ```bash
57
+ node scripts/reproduce_all.mjs \
58
+ --dataset /path/to/dataset \
59
+ --concurrency 5 \
60
+ --timeout-minutes 60 \
61
+ --clean
62
+ ```
@@ -0,0 +1,66 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
+
7
+ export function checkChangelog(directory = root) {
8
+ const { version } = JSON.parse(
9
+ readFileSync(path.join(directory, "package.json"), "utf8"),
10
+ );
11
+ const fail = (message) => {
12
+ throw new Error(`CHANGELOG.md: ${message}. Update the changelog before releasing ${version}.`);
13
+ };
14
+ let changelog;
15
+ try {
16
+ changelog = readFileSync(path.join(directory, "CHANGELOG.md"), "utf8");
17
+ } catch (error) {
18
+ if (error.code === "ENOENT") fail("file is missing");
19
+ throw error;
20
+ }
21
+ // Examples and comments are not release notes.
22
+ const content = changelog
23
+ .replace(/<!--[^]*?-->/g, "")
24
+ .replace(/^(`{3,}|~{3,})[^\n]*\n[^]*?^\1[^\S\r\n]*$/gm, "");
25
+ const sections = [...content.matchAll(/^##[ \t]+(.+?)[ \t]*\r?$/gm)];
26
+ if (sections[0]?.[1] !== "[Unreleased]") {
27
+ fail("the first section must be ## [Unreleased]");
28
+ }
29
+ if (sections.filter((section) => section[1] === "[Unreleased]").length !== 1) {
30
+ fail("Unreleased must appear exactly once");
31
+ }
32
+ const currentSections = sections.filter((section) =>
33
+ section[1].startsWith(`[${version}]`),
34
+ );
35
+ if (currentSections.length !== 1 || currentSections[0] !== sections[1]) {
36
+ fail(`the newest release section must be a single ## [${version}] - YYYY-MM-DD`);
37
+ }
38
+ const current = currentSections[0];
39
+ const date = current[1].slice(`[${version}] - `.length);
40
+ if (
41
+ current[1] !== `[${version}] - ${date}` ||
42
+ !/^\d{4}-\d{2}-\d{2}$/.test(date) ||
43
+ !Number.isFinite(Date.parse(`${date}T00:00:00Z`)) ||
44
+ new Date(`${date}T00:00:00Z`).toISOString().slice(0, 10) !== date
45
+ ) {
46
+ fail(`release ${version} needs a valid date in YYYY-MM-DD format`);
47
+ }
48
+ const body = content.slice(current.index + current[0].length, sections[2]?.index);
49
+ const entries = [...body.matchAll(/^[-*][ \t]+(.+?)\r?$/gm)];
50
+ if (!entries.some((entry) =>
51
+ /[\p{L}\p{N}]/u.test(entry[1]) &&
52
+ !/^(?:TODO|TBD|待补充|待填写|暂无)(?:[\s::.!!。…-]|$)/i.test(entry[1]),
53
+ )) {
54
+ fail(`release ${version} needs at least one non-placeholder change entry`);
55
+ }
56
+ return version;
57
+ }
58
+
59
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
60
+ try {
61
+ console.log(`Changelog ready for ${checkChangelog()}`);
62
+ } catch (error) {
63
+ console.error(error.message);
64
+ process.exitCode = 1;
65
+ }
66
+ }