@qwen-code/arena-agent 0.0.10-dev.9 → 0.0.10-test.0

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 (144) hide show
  1. package/.env.example +61 -10
  2. package/CHANGELOG.md +140 -11
  3. package/README.md +309 -46
  4. package/dist/agent.d.ts +20 -6
  5. package/dist/agent.js +303 -73
  6. package/dist/agent.js.map +1 -1
  7. package/dist/anthropic.d.ts +1 -1
  8. package/dist/anthropic.js +46 -57
  9. package/dist/anthropic.js.map +1 -1
  10. package/dist/arena-history.d.ts +12 -0
  11. package/dist/arena-history.js +119 -0
  12. package/dist/arena-history.js.map +1 -0
  13. package/dist/ask-user.js +44 -2
  14. package/dist/ask-user.js.map +1 -1
  15. package/dist/attachment-limits.d.ts +4 -0
  16. package/dist/attachment-limits.js +10 -0
  17. package/dist/attachment-limits.js.map +1 -0
  18. package/dist/attachments.d.ts +4 -2
  19. package/dist/attachments.js +9 -11
  20. package/dist/attachments.js.map +1 -1
  21. package/dist/brave-search.js +11 -7
  22. package/dist/brave-search.js.map +1 -1
  23. package/dist/canonical-data.d.ts +9 -0
  24. package/dist/canonical-data.js +12 -3
  25. package/dist/canonical-data.js.map +1 -1
  26. package/dist/cli.js +140 -40
  27. package/dist/cli.js.map +1 -1
  28. package/dist/code-tool-schema.d.ts +3 -0
  29. package/dist/code-tool-schema.js +92 -0
  30. package/dist/code-tool-schema.js.map +1 -0
  31. package/dist/code-tools.js +63 -26
  32. package/dist/code-tools.js.map +1 -1
  33. package/dist/compaction-threshold.d.ts +3 -0
  34. package/dist/compaction-threshold.js +6 -0
  35. package/dist/compaction-threshold.js.map +1 -0
  36. package/dist/compaction.d.ts +24 -7
  37. package/dist/compaction.js +242 -66
  38. package/dist/compaction.js.map +1 -1
  39. package/dist/config.d.ts +17 -0
  40. package/dist/config.js +30 -5
  41. package/dist/config.js.map +1 -1
  42. package/dist/connector-errors.d.ts +16 -0
  43. package/dist/connector-errors.js +69 -0
  44. package/dist/connector-errors.js.map +1 -0
  45. package/dist/connector-files.d.ts +31 -0
  46. package/dist/connector-files.js +249 -0
  47. package/dist/connector-files.js.map +1 -0
  48. package/dist/connector-input.d.ts +10 -0
  49. package/dist/connector-input.js +79 -0
  50. package/dist/connector-input.js.map +1 -0
  51. package/dist/connector-mcp.d.ts +60 -0
  52. package/dist/connector-mcp.js +474 -0
  53. package/dist/connector-mcp.js.map +1 -0
  54. package/dist/connectors.d.ts +13 -0
  55. package/dist/connectors.js +69 -0
  56. package/dist/connectors.js.map +1 -0
  57. package/dist/fetch-page.js +2 -2
  58. package/dist/fetch-page.js.map +1 -1
  59. package/dist/history.d.ts +15 -5
  60. package/dist/history.js +59 -14
  61. package/dist/history.js.map +1 -1
  62. package/dist/image-choice.d.ts +23 -0
  63. package/dist/image-choice.js +190 -0
  64. package/dist/image-choice.js.map +1 -0
  65. package/dist/image-input-fallback.js +5 -0
  66. package/dist/image-input-fallback.js.map +1 -1
  67. package/dist/image-model.d.ts +6 -0
  68. package/dist/image-model.js +32 -0
  69. package/dist/image-model.js.map +1 -0
  70. package/dist/midturn-checkpoint-hook.d.ts +14 -1
  71. package/dist/midturn-checkpoint-hook.js.map +1 -1
  72. package/dist/mode.js +1 -1
  73. package/dist/mode.js.map +1 -1
  74. package/dist/model-capabilities.js +18 -8
  75. package/dist/model-capabilities.js.map +1 -1
  76. package/dist/model-router-media.js +55 -3
  77. package/dist/model-router-media.js.map +1 -1
  78. package/dist/model-stream.d.ts +16 -0
  79. package/dist/model-stream.js +129 -0
  80. package/dist/model-stream.js.map +1 -0
  81. package/dist/openai-responses.d.ts +2 -1
  82. package/dist/openai-responses.js +85 -88
  83. package/dist/openai-responses.js.map +1 -1
  84. package/dist/process-manager.js +9 -9
  85. package/dist/process-manager.js.map +1 -1
  86. package/dist/prompts.d.ts +0 -2
  87. package/dist/prompts.js +10 -60
  88. package/dist/prompts.js.map +1 -1
  89. package/dist/protocol.d.ts +5 -1
  90. package/dist/protocol.js +23 -2
  91. package/dist/protocol.js.map +1 -1
  92. package/dist/provider.d.ts +18 -0
  93. package/dist/provider.js +120 -51
  94. package/dist/provider.js.map +1 -1
  95. package/dist/repo.d.ts +1 -1
  96. package/dist/repo.js +26 -4
  97. package/dist/repo.js.map +1 -1
  98. package/dist/routify-web-fetch.js +2 -2
  99. package/dist/routify-web-fetch.js.map +1 -1
  100. package/dist/server.js +55 -27
  101. package/dist/server.js.map +1 -1
  102. package/dist/session-memory-prompts.d.ts +3 -1
  103. package/dist/session-memory-prompts.js +4 -2
  104. package/dist/session-memory-prompts.js.map +1 -1
  105. package/dist/side-query.js +5 -0
  106. package/dist/side-query.js.map +1 -1
  107. package/dist/spec.d.ts +5 -4
  108. package/dist/spec.js +30 -11
  109. package/dist/spec.js.map +1 -1
  110. package/dist/startup-config.d.ts +39 -0
  111. package/dist/startup-config.js +347 -0
  112. package/dist/startup-config.js.map +1 -0
  113. package/dist/startup-main-config.d.ts +63 -0
  114. package/dist/startup-main-config.js +94 -0
  115. package/dist/startup-main-config.js.map +1 -0
  116. package/dist/startup-tool-config.d.ts +8 -0
  117. package/dist/startup-tool-config.js +116 -0
  118. package/dist/startup-tool-config.js.map +1 -0
  119. package/dist/summary-compaction.d.ts +29 -0
  120. package/dist/{legacy-compaction.js → summary-compaction.js} +79 -14
  121. package/dist/summary-compaction.js.map +1 -0
  122. package/dist/tools.d.ts +5 -1
  123. package/dist/tools.js +164 -54
  124. package/dist/tools.js.map +1 -1
  125. package/dist/tui.js +26 -6
  126. package/dist/tui.js.map +1 -1
  127. package/dist/types.d.ts +21 -1
  128. package/dist/web/app.js +30 -5
  129. package/dist/web/app.js.map +1 -1
  130. package/dist/workspace.js +61 -14
  131. package/dist/workspace.js.map +1 -1
  132. package/docs/BINARY_DISTRIBUTION.md +44 -8
  133. package/docs/MODES_AND_RELEASES.md +79 -6
  134. package/docs/SDK.md +1 -0
  135. package/modes/code-arena/capture-provenance.json +9 -6
  136. package/modes/code-arena/full-stack/dependencies.lock.json +8670 -0
  137. package/modes/code-arena/full-stack/system-prompt.txt +1 -1
  138. package/modes/code-arena/html/system-prompt.txt +1 -1
  139. package/modes/code-arena/react/dependencies.lock.json +2707 -0
  140. package/package.json +7 -2
  141. package/scripts/README.md +23 -4
  142. package/web/styles.css +22 -0
  143. package/dist/legacy-compaction.d.ts +0 -16
  144. package/dist/legacy-compaction.js.map +0 -1
package/README.md CHANGED
@@ -1,16 +1,21 @@
1
1
  # Arena CodeAgent Harness
2
2
 
3
+ [在线文档](https://arena-harness.io.alibaba-inc.com)集中提供模式、工具、系统提示词、环境变量和更新日志,集团账号登录后即可访问。源码与预览方式见 [website/README.md](website/README.md)。
4
+
3
5
  版本变化见 [Changelog](CHANGELOG.md),发布前请按[发版流程](docs/MODES_AND_RELEASES.md#发布节奏)
4
6
  更新变更记录并运行 `npm run check:changelog`。
5
7
 
6
- 这是一个基于 `message.json` 与 `tools.json` 所定义消息和工具契约的 TypeScript
8
+ 这是一个基于各模式正式提示词与工具契约的 TypeScript
7
9
  CodeAgent harness。运行时只依赖 Node.js,不再依赖 Python:
8
10
 
9
11
  - `arena -p ...` / `arena --file prompt.txt`:headless 单轮入口,支持文本、完整 JSON 和 NDJSON 流。
10
12
  - `arena tui`:终端多轮对话,实时输出并自动保存、恢复历史。
11
13
  - `arena serve`:HTTP daemon、Web UI 与双通道 SSE 实时接口。
12
14
  - Agent loop:流式 assistant → tool call → tool result → assistant。
13
- - 15 个工具按照 `tools.json` 中的 Draft-07 schema 提供参数校验。
15
+ - Agent Arena 的 15 个工具按照 `spec/tools.json` 中的 Draft-07 schema 提供参数校验;Code Arena 使用各模式的工具契约。
16
+
17
+ 查看代码或修改配置前,可先看[源码入口与历史资料](docs/MODES_AND_RELEASES.md#源码入口与历史资料),
18
+ 其中区分了当前提示词、生成文件、测试样本和历史审计脚本。
14
19
 
15
20
  本文提及的第三方产品、模型与服务名称仅用于说明兼容性和配置方式,不表示相关
16
21
  权利人对本项目的认可或背书;相关商标归各自权利人所有。使用者应自行确认并遵守
@@ -108,10 +113,60 @@ code-harness --file prompt.txt --mode react
108
113
  `CODE_HARNESS_MODE`、`ARENA_MODE` 及旧模式别名。详细目录和发包决策见
109
114
  [`docs/MODES_AND_RELEASES.md`](docs/MODES_AND_RELEASES.md)。
110
115
 
116
+ ### 启动配置日志
117
+
118
+ `arena -p`、`--file` 和 `--continue` 默认在 stderr 输出一条 `[arena:config]`
119
+ JSON 启动日志,并将同一条记录追加到会话目录下的 `startup-config.jsonl`,与
120
+ `history.json` 放在一起。日志中的 `run.configLogFile` 给出完整路径;AP 可直接
121
+ 收集 stderr,或从 `ARENA_HOME` 指定的状态目录读取文件。
122
+
123
+ `config` 按环境变量名记录解析后的主模型、工具、搜索、图片与语音、缓存、代理、
124
+ 预览和快照配置,包括默认值;没有对应环境变量的计算结果保留字段名,例如
125
+ `compactionThresholdTokens`。同一设置使用主变量名,例如 `OPENAI_MODEL` 表示最终选中的
126
+ 模型,即使输入来自 `ARENA_MODEL` 或 `--model`。未配置或当前模式不适用的项记为 `null`。
127
+ 工具配置表示启动时的默认设置,单次工具调用仍可覆盖超时、选择声音等参数。
128
+
129
+ `inputs.environment` 和 `inputs.workspaceEnv` 分别保留启动环境和工作区 `.env` 的
130
+ 相关原始键值,包括被覆盖的值;`inputs.arguments` 记录解析后的 CLI 选项。进程环境
131
+ 优先于 `.env`,CLI 参数在加载配置后覆盖,续跑记录恢复后的模式和模型。未知的
132
+ `ARENA_*` 等变量只出现在输入中,不会被当成生效配置。这里只记录 harness 相关设置,
133
+ 不会转储整个进程环境。快照配置只采用启动环境;server/TUI 专用配置在 headless 中不生效。
134
+
135
+ `connection` 记录实际模型协议和连接地址,`connection.sources` 标明最终使用的地址和
136
+ 凭据来源,例如 `OPENAI_API_KEY` 或 `ANTHROPIC_API_KEY`;密钥值仍会脱敏。
137
+ `run.cwd` 记录实际工作目录,`run.history` 记录历史是否加载、消息数、来源,以及
138
+ 模型和模式是否继承历史。不带 `--continue` 时,同一 session 也可能加载已有历史。
139
+ `sessionMemoryAvailable` 只表示存在可供检查的快照,`restoreSessionMemory` 表示允许恢复,
140
+ 不表示已通过历史匹配检查。`inputs.workspaceEnvFile` 记录 `.env` 的路径、读取状态和错误码。
141
+ `modelInput.inputAllowed` 汇总文字、图片、视频和音频的输入开关,图片等开关也保留在
142
+ `config.ARENA_MODEL_SUPPORTS_*_INPUT`。`modelInput.sources` 说明每种模态的判断来源:
143
+ 对应环境变量名表示显式覆盖,`model_registry` 表示命中内置模型规则,
144
+ `unknown_model_default` 表示未知模型默认允许尝试;这种默认值不表示已验证远端支持。
145
+ `registryKnown`、`registryRule` 和 `normalizedModel` 可用于核对模型名是否被正确识别。
146
+ 这些字段描述主模型的输入能力,与图片、语音生成工具使用的模型配置分开。
147
+ `modelInput.transport.userContent` 另行说明传输处理:Chat Completions 为 `passthrough`,
148
+ 当前 Responses/Anthropic 编码器为 `text_and_images_only`;模态开关不会扩展编码器支持范围。
149
+ 设置 `ARENA_EXTRA_BODY.model` 时,`modelInput.requestModelOverride` 会单独显示该覆盖值,
150
+ 模态规则仍按 `modelInput.model` 判断。`imageFallback` 记录图片转文字 fallback 是否启用、
151
+ 原因和启用时的实际连接;它只适用于 Agent Arena 的 `read_file` 图片输入。
152
+
153
+ 密钥只显示 `[set]` / `[unset]`,URL 移除账号、密码、查询参数与片段,自定义提示词
154
+ 和额外请求体中的自由文本仅记录字符数与 SHA-256。
155
+
156
+ 启动记录的 `event` 为 `startup_config`。实际 HTTP 请求准备好后,会在相同位置补写
157
+ `model_request_config`,记录协议映射和 `ARENA_EXTRA_BODY` 覆盖后的模型、参数与工具名称,
158
+ 使用接口字段名,例如 `max_output_tokens`。被协议省略的参数不会出现在这条记录中。
159
+ 同一进程内相同请求配置只记录一次;模型、参数或工具定义变化时再记录。
160
+ 因此可以直接核对温度、thinking 设置和工具是否真正进入请求,无需从启动配置推导。
161
+ 这条记录表示请求已构造,不表示服务端已接受;完整调用过程仍可查看 `llm-calls.jsonl`。
162
+ `ARENA_MODEL_CALL_LOGGING=false` 不影响配置日志;设置 `ARENA_CONFIG_LOGGING=false`
163
+ 会同时关闭 stderr 和文件中的启动、请求配置日志。stdout 输出格式保持不变。
164
+
111
165
  ### 中途快照与单步执行
112
166
 
113
167
  Headless CLI 可通过 JavaScript Hook 决定何时继续或创建 Harbor 快照。工具校验在
114
- 整批工具执行前触发;输出重复在主模型生成正文或 reasoning 时触发。
168
+ 整批工具执行前触发;输出重复在主模型生成正文或 reasoning 时触发;Agent Arena
169
+ 的压缩事件在达到阈值且存在可压缩历史时、执行压缩前触发。
115
170
 
116
171
  | 类型 | 含义 |
117
172
  | --- | --- |
@@ -120,12 +175,13 @@ Headless CLI 可通过 JavaScript Hook 决定何时继续或创建 Harbor 快照
120
175
  | `invalid_json` | 工具参数不是合法 JSON |
121
176
  | `invalid_arguments` | 参数不符合工具 schema |
122
177
  | `repetitive_output` | 模型持续重复同一字符或片段 |
178
+ | `before_compaction` | Agent Arena 即将执行摘要式或 rolling session memory 压缩 |
123
179
 
124
180
  | Hook 返回值 | 后续行为 |
125
181
  | --- | --- |
126
182
  | `continue` 或 `false` | 正常继续 |
127
- | `snapshot_continue` 或 `true` | 快照后继续当前生成与工具执行 |
128
- | `snapshot_exit` | 停止当前生成,保存快照后退出,本批工具不执行 |
183
+ | `snapshot_continue` 或 `true` | 快照后继续当前生成、工具执行或压缩 |
184
+ | `snapshot_exit` | 保存快照后暂停退出;触发处的生成、工具批次或压缩不再继续 |
129
185
 
130
186
  例如,将以下内容保存为 `policy.mjs`:
131
187
 
@@ -145,14 +201,34 @@ export HARBOR_REMOTE_COMMAND_BASE_URL="http://<harbor-host>:<port>"
145
201
  export HARBOR_REMOTE_COMMAND_API_KEY="<api-key>"
146
202
  ```
147
203
 
204
+ 要在第 `k` 次压缩执行前停止,可将 `policy.mjs` 写成:
205
+
206
+ ```js
207
+ const k = 3; // 正整数;1 表示第一次压缩前停止
208
+ export default event =>
209
+ event.type === "before_compaction" && event.compaction.count >= k
210
+ ? "snapshot_exit"
211
+ : "continue";
212
+ ```
213
+
214
+ 两种压缩模式共用 `event.compaction.count`:历史中已完成且未撤销的压缩次数 + 1,
215
+ 因此第 `k` 次暂停时只完成了 `k - 1` 次压缩。续跑或切换 `ARENA_COMPACTION_MODE`
216
+ 会从历史恢复计数;后台记忆更新、失败的压缩和已撤销的记录不增加次数。
217
+ 事件还提供 `compaction.mode`(`summary` / `rolling`)、`source`(`outer` / `inner`)、
218
+ `estimatedTokensBefore` 和 `thresholdTokens`,以及空的 `toolCalls` 数组。
219
+ 暂停不会发起本次摘要或强制记忆更新,也不会应用已缓存的记忆;在途后台更新会收束后再快照。
220
+ 结果为 `status: "paused"`、`stop_reason: "checkpoint"`,CLI 退出码为 0。
221
+ 续跑前需提高 `k` 或调整 Hook,否则会在同一次压缩前再次暂停。
222
+
148
223
  配置 Hook 后自动检测输出重复,命中时由 Hook 决定继续、快照或退出;未配置 Hook
149
224
  时按默认流程执行。检测连续重复至少 512 个 Unicode 字符、至少 4 次的片段,
150
225
  片段最长 1024 字符,忽略纯空白和标点填充。
151
226
  正文与 reasoning 分开检测,工具参数和工具结果不参与;每次生成最多触发一次重复事件。
152
227
  事件的 `repetition.channel` 和 `repetition.sample` 提供通道与最多 256 字符的诊断片段。
153
228
 
154
- 快照保存当前回复生成前的 `history.json`。恢复快照对应环境后,使用原 session ID
155
- 续跑同一个 user turn,不追加用户消息;被中止的那一步会重新生成:
229
+ 快照保存触发点之前的 `history.json`;压缩事件保留完整的压缩前历史。
230
+ 恢复快照对应环境后,使用原 session ID 续跑同一个 user turn,不追加用户消息;
231
+ 被中止的生成或压缩会重新执行:
156
232
 
157
233
  ```bash
158
234
  arena --continue --session-id demo
@@ -187,8 +263,11 @@ arena serve --system-prompt '只使用中文回复。'
187
263
  history.json、增量日志和返回的 messages 只保存对话历史(含 compaction),不保存运行时生成的开头 system;中途出现的 system 消息会保留。
188
264
  新会话和续跑均在构建模型请求时前置运行时 system;官方以 user 开头的历史可直接续跑。
189
265
  旧版带开头 system 的本地历史仍可读取,该旧前缀不作为本次指令,后续写入会移除它。
190
- 每次运行(包括 resume)都会重新探测当前工作区和 Git 状态来填充仓库提示词,
191
- 不从旧 system 或会话 metadata 恢复仓库信息;因此不同轮次的 system 可以变化。
266
+ Agent Arena 在尚未选定仓库时,沿用“工作区根不是 Git 仓库,且仅有一个直接子仓库”的自动识别规则。
267
+ 成功识别后,会话记住该子仓库的相对路径;新消息、`--continue`、TUI 和服务端恢复均沿用该目标,
268
+ 新增同级仓库不会使会话退出 Repo 模式。普通会话后续出现首个可识别仓库时仍可自动进入 Repo 模式。
269
+ 每次运行仍从当前 Git 状态刷新分支(包括 `arena/...`)、远端和基准提交,不复用旧 system 中的信息。
270
+ 已绑定仓库缺失或 Git 状态无法解析时明确报错,不会静默换仓库或模式。旧历史没有绑定信息时仍按原规则识别。
192
271
 
193
272
  如需保留当前 mode 生成的 system prompt,只在末尾追加任务或部署指令,可传入
194
273
  `--append-system-prompt <text>`:
@@ -271,6 +350,11 @@ Anthropic Messages payload 并使用 `x-api-key`;OpenAI 路由根据上述规
271
350
  回合会把这些 output items 连同 function output 一起完整回放到下一次 `input`,
272
351
  从而在不依赖 provider 端存储的情况下保留隐藏 reasoning。请求显式包含
273
352
  `reasoning.encrypted_content`;GPT-5.6 还会使用 `reasoning.context=all_turns`。
353
+ 设置 `ARENA_RESPONSES_REPLAY_REASONING_STATE=false` 可在发送 Responses 请求时,
354
+ 移除 `input` 中所有 `reasoning` item 的 `encrypted_content` 和 `id`;默认值为
355
+ `true`。其他字段(如 `summary`)、工具调用和消息 ID 均保留,本地历史仍保存完整
356
+ output items。该开关只控制下一轮回传,不阻止服务端返回加密内容,也不影响 Chat
357
+ Completions 或 Anthropic Messages;关闭后模型无法通过这两个字段恢复历史隐藏推理。
274
358
  设置 `ARENA_OPENAI_API=chat` 或 `responses` 可覆盖 GPT 的自动选择。
275
359
  该开关不作用于 Gemini 或 Anthropic 路由。如未设置统一入口,
276
360
  Anthropic 可回退到 `ANTHROPIC_BASE_URL` /
@@ -286,7 +370,7 @@ claude-code-proxy),收到 401 后会自动切换鉴权方式并记住。流
286
370
  Anthropic 直连在原生协议上完整支持 thinking。旧版 Claude 和非 Claude
287
371
  兼容网关把 `ARENA_ENABLE_THINKING` / `ARENA_THINKING_BUDGET` 映射为
288
372
  `thinking.type=enabled` / `thinking.budget_tokens`;Claude Opus/Sonnet 4.6+
289
- 以及 Claude 5、Fable、Mythos 使用 `thinking.type=adaptive`,不再发送固定
373
+ 以及 Haiku 5.5+、Fable、Mythos 使用 `thinking.type=adaptive`,不再发送固定
290
374
  budget。Anthropic 要求启用 thinking 时不调整 temperature,此时不发送该
291
375
  参数。带签名的 thinking/redacted_thinking block 会原样保存在对应 assistant
292
376
  消息的 `reasoning_details` 字段——
@@ -308,7 +392,7 @@ OpenAI-compatible 服务发送同一份历史时,`reasoning_details` 与 `usag
308
392
  OPENAI_MODEL=qwen3.7-max
309
393
  OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
310
394
  OPENAI_API_KEY=sk-...
311
- ARENA_MAX_TURNS=500
395
+ ARENA_MAX_TURNS=250
312
396
  ARENA_MAX_TOKENS=131072
313
397
  ARENA_TEMPERATURE=1
314
398
  ARENA_TOOL_CHOICE=auto
@@ -316,7 +400,7 @@ ARENA_ENABLE_THINKING=true
316
400
  ARENA_THINKING_BUDGET=81920
317
401
  ARENA_PRESERVE_THINKING=true
318
402
  ARENA_CONTEXT_WINDOW=1000000
319
- ARENA_CONTEXT_COMPACTION_RATIO=0.7
403
+ ARENA_CONTEXT_COMPACTION_RATIO=0.8
320
404
  ARENA_COMPACTION_SUMMARY_MAX_TOKENS=131072
321
405
  ```
322
406
 
@@ -334,32 +418,137 @@ ARENA_DISALLOWED_TOOLS=["bash","write_file"]
334
418
 
335
419
  被禁用的工具不会发送给模型;即使模型仍构造了该工具调用,执行层也会
336
420
  在产生副作用前拒绝。
421
+
422
+ Agent Arena 可通过 Merge MCP 调用 Notion、Google Calendar、Google Drive、Docs、
423
+ Sheets、Slides、Gmail、Dropbox、Supabase 和 Vercel,应用范围由
424
+ `ARENA_CONNECTORS` JSON 数组控制。先在 Merge 创建 Tool Pack 和 Registered User,
425
+ 让该用户通过 Merge Link 连接目标应用,再配置同一环境的 Access Key:
426
+
427
+ ```sh
428
+ # 写入本地 .env;三个 Merge 值从自己的 Merge 环境取得。
429
+ ARENA_CONNECTORS='["notion","google_drive","google_docs","google_sheets"]'
430
+ ARENA_MERGE_ACCESS_KEY=...
431
+ ARENA_MERGE_TOOL_PACK_ID=...
432
+ ARENA_MERGE_REGISTERED_USER_ID=...
433
+ ```
434
+
435
+ 首次可运行 `arena -p '加载 Google Drive 工具并最多列出一个文件,不要创建或修改文件'`。
436
+ Tool Pack 需启用所选应用的完整捕获目录,见
437
+ [目录源文件](spec/connectors/catalog.json)。缺少工具时会明确报错;上游新增的工具不会自动暴露给模型。
438
+
439
+ | 应用 slug | 工具数 | 模型参数定义 |
440
+ | --- | ---: | --- |
441
+ | `notion` | 22 | 历史完整捕获 |
442
+ | `google_drive` | 49 | 历史完整捕获 |
443
+ | `google_docs` | 46 | 历史完整捕获 |
444
+ | `google_sheets` | 56 | 历史完整捕获 |
445
+ | `google_slides` | 50 | 历史完整捕获 |
446
+ | `gmail` | 29 | 历史完整捕获 |
447
+ | `dropbox` | 33 | 历史完整捕获 |
448
+ | `supabase` | 42 | 历史完整捕获 |
449
+ | `vercel` | 53 | 历史完整捕获 |
450
+ | `linear` | 41 | 历史完整捕获 |
451
+ | `google_calendar` | 42 | 旧捕获目录与 Merge 实时 schema |
452
+
453
+ 默认不启用应用。非空 `ARENA_CONNECTORS` 在基础工具集合中注册
454
+ `list_connector_tools`,并在普通/Repo 系统提示词末尾加入捕获的 `Connected apps`
455
+ 段落,列出数组中的应用。接受上表中的 slug;重复项去重,
456
+ 保留输入顺序,未知名称或非法 JSON 会在启动时报错。Code Arena 忽略此配置。
457
+
458
+ 可选 `ARENA_CONNECTED_CONNECTORS` JSON 数组由宿主提供已连接应用的快照,用来区分
459
+ “已连接但未开启”和“未连接”。它不执行 OAuth,也不自动查询账号状态。未设置时按
460
+ `ARENA_CONNECTORS` 提供会话应用;显式 `[]` 可启用“尚无连接应用”的提示和发现入口。
461
+ 两项均未设置时,基础提示词和工具集合保持原样。
462
+
463
+ 模型调用 `list_connector_tools` 后,harness 才连接 Merge、校验后端目录并注册业务工具。
464
+ 每条新用户消息都需要重新发现;暂停续跑恢复目录及重复调用保护,不重放已执行的操作。
465
+ 数组表示本次会话允许哪些应用,加载目录成功也不保证账号具有全部读写权限。
466
+ 缺少配置、授权失败或目录不完整时返回明确错误;业务调用按历史日志包装结果。
467
+ `ARENA_DISALLOWED_TOOLS` 禁用发现工具时也会省略应用提示词;显式 `--system-prompt`
468
+ 仍然完整覆盖系统提示词。
469
+
470
+ 默认连接 `https://ah-api.merge.dev`,可用 `ARENA_MERGE_API_BASE` 配置区域地址;
471
+ `ARENA_CONNECTOR_TIMEOUT_MS` 默认 60000。超时或取消不会自动重试写操作,但远端可能
472
+ 已经完成写入,需先查询确认。当前一个进程使用一个 Registered User,Web 会话共用该账号;
473
+ 尚未提供各 Web 用户独立登录和账号绑定界面。
474
+
475
+ Drive、Dropbox 上传及 Gmail 草稿附件使用 `input.file_path` 读取工作区文件;下载/导出
476
+ 通过 Merge 文件接口落盘并返回文件路径。单文件上限为 100 MiB。同一回复中已完成或正在
477
+ 执行的相同写调用、已失败的相同业务调用会被拦截;上传限流也会阻止该回复继续上传文件。
478
+
479
+ 提示词、发现入口和十家应用的 421 个完整模型工具定义(含 Linear 的 41 个工具)保存在 `spec/connectors/`,
480
+ 由构建脚本嵌入;后端要求的 nullable 字段和文件引用只在发送给 Merge 时转换。
481
+ Google Calendar 本批没有完整历史 schema,继续使用实时定义。
482
+ 配置步骤及历史与真实后端的验证边界见 [Connector 接入分析](docs/research/AGENT_ARENA_CONNECTOR_BACKEND.md)。
483
+
337
484
  `ARENA_EXTRA_BODY` 会在配置加载时解析为 JSON 对象,既作为最后一层请求
338
485
  参数覆盖,也会原样写入 `history.json` 的
339
486
  `metadata.parameters.extra_body`。
340
487
 
341
488
  `ROUTIFY_API_KEY` 供 `web_search`、`image_search`、`generate_image` 和
342
- `generate_speech` 使用;开启
489
+ `generate_speech` 使用。前三者分别支持 `ROUTIFY_WEB_SEARCH_API_KEY`、
490
+ `ROUTIFY_IMAGE_SEARCH_API_KEY`、`ROUTIFY_GENERATE_IMAGE_API_KEY` 独立覆盖;
491
+ 未设置或留空(含纯空白)时回退到 `ROUTIFY_API_KEY`,旧配置无需修改。开启
343
492
  `CODE_HARNESS_ROUTIFY_WEB_FETCH_ENABLED` 后,Arena 的 `fetch_page` 和 Code Arena 的
344
493
  `web_fetch` 也复用该密钥,通过 Routify 先调用 Firecrawl,失败时回退到
345
494
  Jina Reader。
346
495
  该密钥不会作为主模型连接的密钥。
347
496
 
497
+ Agent Arena 通过 `ARENA_COMPACTION_MODE` 选择压缩方式,默认 `rolling`;设置为
498
+ `summary` 可切换回原来的摘要式压缩,适用于 headless CLI、TUI 和服务端:
499
+
500
+ ```bash
501
+ ARENA_COMPACTION_MODE=summary arena -p "继续任务" --session-id demo
502
+ ```
503
+
504
+ `summary` 达到阈值时同步生成完整摘要,优先保留当前用户回合,不启动或恢复
505
+ 后台记忆缓存。当前实现位于 `src/summary-compaction.ts`,与 Code Arena 共用
506
+ 摘要系统提示词、请求提示词和 continuation preamble。
507
+ 摘要按 `Goal`、`Instructions`、`Discoveries`、`Accomplished` 等章节生成,使用
508
+ 主模型、temperature=0、关闭 thinking,默认输出上限为 8192。
509
+ `ARENA_COMPACTION_SUMMARY_MAX_TOKENS` 可覆盖上限;`rolling` 的默认上限仍为 131072。
510
+ 两种模式默认阈值均为 `min(250000, 0.8 × context window)`;窗口和比例可独立配置。
511
+ 两种模式及 Code Arena 都优先使用最近一次主请求的服务端 usage,仅估算此后新增的内容。
512
+ 大段文本取字符估算与带 10% 余量的 `o200k_base` 分词估算的较大值,减少 SVG 等
513
+ 密集文本的低估;本地分词不保证与所选模型完全一致。长用户回合没有更早回合可归档,
514
+ 或当前回合本身达到阈值时,摘要模式也可归档回合内较早的步骤,保留最近完整步骤的
515
+ 工具调用、全部结果及图片;Code Arena 的摘要压缩也支持此行为。
516
+ Code Arena 始终使用摘要模式,阈值仍为窗口与比例的乘积,默认比例 70%。
517
+
518
+ Agent Arena 两种模式和 Code Arena 都保存已应用的 `compaction` 历史记录,续跑时恢复
519
+ 压缩后的上下文。原始历史保留服务端 usage,但已压缩上下文中的旧 usage 不再作为计数基线。
520
+ Agent Arena 历史 metadata 记录本次压缩方式;下一次运行仍以当前环境变量为准,
521
+ 未设置时回到 `rolling`,切换模式也会恢复已应用的压缩记录。
522
+
523
+ 以下后台记忆机制适用于 `rolling` 模式。
524
+
348
525
  Agent Arena 在每次模型调用前检查新增对话预算:自上次成功记忆更新以来,
349
- 可归档内容的本地估算达到 `ARENA_MEMORY_UPDATE_TOKENS`(默认 24000)时,
526
+ 可归档内容的 token 增量达到 `ARENA_MEMORY_UPDATE_TOKENS`(默认 24000)时,
350
527
  启动一个后台记忆更新任务。24000 是根据历史日志选择的暂定策略,不是已确认的官方常数。
351
528
  已有记忆、系统提示词和工具定义不计入新增预算;最新完整的模型/工具步骤保留原文。
529
+ 新增预算优先用该段首个有效 assistant 的 input usage 作为基线,减去公共输入前缀:
530
+ 末个有效 assistant 的 total usage 减去该基线,再补估段首用户消息及末尾工具结果。
531
+ total 缺失时尝试 input/output 相加;只有 input 时,连同该 assistant 一起补估。
532
+ 缺失 usage 的中间步骤由后续 input 覆盖,没有可用基线、input usage 倒退或用量自相矛盾时回退本地估算。
533
+ 差分依赖同一模型和稳定的输入前缀;切换模型或工具定义后若 usage 未被清除,
534
+ 仅靠数值无法识别所有口径变化。
535
+ 回填已归档消息仍会触发现有的历史前缀校验失效,清除覆盖位置并重新计算新增范围。
536
+ 本地估算忽略工具调用 ID(包括 Responses 的函数调用 ID)和 usage 元数据,
537
+ 保留参数、结果正文及图片计数;实际历史与请求中的 ID 和签名保持完整。
352
538
  更新完成只刷新缓存,主模型继续使用原上下文。
353
539
 
354
- 整体上下文达到固定的 250,000 tokens 时才回注最新记忆;所有 Agent Arena 模型
355
- 使用同一个硬压缩点。计数优先采用最近一次主模型请求返回的 `total_tokens`,并只对
540
+ 整体上下文达到压缩阈值时才回注最新记忆。Agent Arena 使用
541
+ `min(250000, ARENA_CONTEXT_WINDOW × ARENA_CONTEXT_COMPACTION_RATIO)`,
542
+ 默认窗口为 1,000,000、比例为 0.8;两项可独立配置,无效值各自回退默认值。
543
+ 例如 200,000 窗口在 160,000 tokens 触发,128,000 窗口在 102,400 tokens 触发;
544
+ 大窗口的阈值最高为 250,000。SDK 显式设置 `compactionThresholdTokens` 可覆盖该阈值。
545
+ 计数优先采用最近一次主模型请求返回的 `total_tokens`,并只对
356
546
  该 assistant 之后追加的 tool 和 user 消息做本地增量估算;缺少 `total_tokens` 时,
357
547
  先尝试由 input/output usage 相加得到,若 output usage 也缺失则回退到 input usage
358
548
  加 assistant 及后续消息的本地估算。没有可靠 usage 锚点时回退到全量本地估算。
359
- 有正在进行的更新则等待完成,必要时提前发起更新。
360
- `ARENA_CONTEXT_WINDOW` 和 `ARENA_CONTEXT_COMPACTION_RATIO` 暂时保留;前者仍用于
361
- 记录压缩比例,后者不参与 Agent Arena 的压缩判定。Code Arena 仍使用两者相乘的阈值,
362
- 压缩比例默认是 70%。
549
+ 达到阈值时若没有可用记忆,则继续使用原上下文,不因阈值额外发起或等待更新。
550
+ 后续检查发现后台记忆已就绪时,再替换已覆盖的历史前缀;生成期间新增的消息保留原样。
551
+ Code Arena 仍使用窗口与比例相乘的阈值,压缩比例默认是 70%。
363
552
  每个会话只运行一个更新任务,失败保留原上下文,源内容推进后允许重试。
364
553
  Agent Arena 使用捕获的 session-memory 协议:只输出变化章节并合并,保留未输出章节,
365
554
  固定维护当前状态、任务、用户约束、工作区、已执行操作、外部来源、失败经验和关键结果。
@@ -367,23 +556,47 @@ Agent Arena 使用捕获的 session-memory 协议:只输出变化章节并合
367
556
  1500 tokens 时,下一次更新提示模型精简,不直接截断内容。
368
557
 
369
558
  主模型上下文通过新的 leading `<arena-system-message>` 回注记忆,最近完整的
370
- 模型/工具步骤保持原样。后台记忆只存在内存中,退出后丢弃。实际触发压缩时,
559
+ 模型/工具步骤保持原样。后台记忆保存在 `history.json.session_memory` 中,包含最新
560
+ 成功快照、覆盖边界和前缀校验值,以及最近更新的状态;运行中的变化先写入增量日志,
561
+ 结束时合并进 `history.json`。实际触发压缩时,
371
562
  在 `history.json.messages` 中插入官方 dump 形式的 `role: "compaction"` 记录:
372
563
  正文为 `content: [{type: "text", text: "摘要正文"}]`,`compaction` 包含 `ratio`、
373
564
  `tokens_at_compaction` 和 `source`。普通消息格式不变,原始历史不删除。
374
- 记录插在已覆盖前缀与保留步骤之间,其位置就是恢复边界,不另存覆盖索引或
375
- `session_memory`。请求与 resume 使用系统提示词、最后一条未撤销压缩记录的摘要、
565
+ 压缩记录插在已覆盖前缀与保留步骤之间,其位置就是已应用摘要的恢复边界。
566
+ 请求与 resume 使用系统提示词、最后一条未撤销压缩记录的摘要、
376
567
  以及其后的消息;`compaction` 角色只用于历史,不发送给模型。后台更新仍不会提前
377
568
  改变上下文。`undone: true` 的压缩记录不生效。
378
569
  这些字段及 `outer`/`inner` 值来自下载的官方 dump;本地将回合开始/工具循环内的
379
570
  压缩分别标为 `outer`/`inner`。token 数和比例使用本地估算,不代表已复刻官方计数。
571
+
572
+ Agent Arena 默认恢复后台记忆缓存,包括同 session 的新一轮 `-p`、`--continue`
573
+ 和 `--single-step`。设置 `ARENA_RESTORE_SESSION_MEMORY=false` 或
574
+ `--no-restore-session-memory` 可显式关闭(适用于 CLI、TUI 和服务端,命令行标志优先);
575
+ `--restore-session-memory` 可覆盖环境变量的关闭设置。
576
+ 恢复前会校验记忆覆盖的历史前缀;不匹配的缓存会被丢弃。
577
+ 关闭恢复不会删除仍有效的已存快照。恢复缓存本身不触发压缩,也不影响历史里
578
+ 已应用的 `compaction` 记录。
579
+
580
+ `memory.update` 事件的 `status` 为 `pending`、`success`、`error` 或 `cancelled`;
581
+ JSON 最终结果的 `memory_update` 和历史里的 `session_memory.update` 也保留最近状态。
582
+ 失败时 `code` 区分 `MEMORY_REQUEST_FAILED`(请求失败)、`MEMORY_EMPTY_RESPONSE`、
583
+ `MEMORY_INCOMPLETE_RESPONSE`(截断或异常结束)、`MEMORY_INVALID_PATCH`、
584
+ `MEMORY_STALE_HISTORY`(前缀失配);取消为 `MEMORY_CANCELLED`,恢复上次进程遗留的
585
+ `pending` 状态时记为 `MEMORY_INTERRUPTED`。新更新失败会保留本次运行已有的成功快照。
586
+ 没有更新记录表示尚未尝试更新。
587
+
588
+ 单步暂停会等待在途更新完成,再保存历史;主任务完成、失败、取消、返回待输入状态,
589
+ 以及外部快照前,会取消未完成的记忆请求并等待退出,再保存最终状态。已成功生成的
590
+ 记忆会保留,未完成的输出不会作为有效记忆使用。
380
591
  Agent Arena 记忆更新使用主模型、temperature=1、thinking
381
592
  开启、thinking budget=81920,输出上限由 `ARENA_COMPACTION_SUMMARY_MAX_TOKENS`
382
- 控制(默认 131072)。Code Arena 保留原有完整摘要实现,默认上限仍为 8192。
593
+ 控制(rolling 默认 131072,summary 默认 8192)。Code Arena 默认上限仍为 8192。
383
594
 
384
595
  `ARENA_CONTEXT_WINDOW` 应设置为所用模型的真实上下文窗口;默认 `1000000` 是
385
596
  当前默认模型的暂定值。Agent Arena 的 usage 基线来自 provider;最近 assistant
386
- 之后的新增上下文以及缺少 usage 时的完整上下文仍使用本地保守估算。如果摘要请求失败,Harness 会保留原历史并继续正常模型请求,避免
597
+ 之后的新增上下文以及缺少 usage 时的完整上下文仍使用本地保守估算。压缩后,保留消息
598
+ 中的旧 usage 仅用于原始历史的用量统计;下一次主请求完成后重新建立计数基线。
599
+ 如果摘要请求失败,Harness 会保留原历史并继续正常模型请求,避免
387
600
  摘要服务的瞬时故障直接中断任务。
388
601
 
389
602
  Responses 与其他协议一样,会在每个工具 step 前检查是否需要压缩。由于所有
@@ -397,8 +610,10 @@ reasoning/function-call 状态都由本地 history 携带,压缩不再需要
397
610
  assistant 消息的 `usage` 字段(`history.json` 与事件流中均可见),供训练
398
611
  和成本统计使用。再次发送历史上下文时,`usage` 会在 wire 边界剥离,不会发回
399
612
  服务端。`model.complete` 事件同样携带该轮 `usage`;上下文压缩的摘要调用
400
- 不会在历史中留下消息,其用量通过 `compaction` 事件上报,只在事件流中可
401
- 见。不支持 `stream_options` 的服务可通过 `ARENA_EXTRA_BODY` 覆盖该参数。
613
+ 不会在历史中留下普通消息,其用量通过 `compaction` 事件上报。Agent Arena 还通过
614
+ `memory.update` 上报记忆更新用量,并将最近一次更新返回的 usage 保存在
615
+ `session_memory.update.usage` 中。不支持 `stream_options` 的服务可通过
616
+ `ARENA_EXTRA_BODY` 覆盖该参数。
402
617
 
403
618
  模型请求遇到网络错误、HTTP 408/409/429 或 5xx 时会自动重试,默认最多重试
404
619
  10 次。没有 `Retry-After` 时使用带随机抖动的指数退避,单次等待默认最多
@@ -421,9 +636,10 @@ load`、`Provisioned Throughput` 容量错误会重试。容量错误最多重
421
636
  模型输入,并沿用 full-jitter 指数退避。上下文超限、参数、鉴权、媒体和安全错误
422
637
  等永久错误不会因正文中出现 `error` 一词而重试。
423
638
 
424
- Agent turn 默认没有模型 step 数上限;设置 `ARENA_MAX_TURNS` 后,达到指定上限
425
- 会返回 error,已经完成的 assistant/tool step 仍会保留。整轮没有内建墙钟时长
426
- 上限,会持续到模型正常结束、请求用户输入、发生不可恢复错误、达到显式配置的
639
+ Agent Arena 每轮默认最多 250 个模型 step,`ARENA_MAX_TURNS` 可覆盖上限。
640
+ 达到上限返回 `paused` / `step_limit`,保存已完成的 assistant/tool step;用户发送
641
+ 下一条消息即可继续,并获得新一轮步数预算。Code Arena 保留各模式原有上限和错误行为。
642
+ 整轮没有内建墙钟时长上限,会持续到模型正常结束、请求用户输入、发生不可恢复错误、达到
427
643
  step 上限,或用户主动取消。工具自身仍可定义单次调用的超时。headless 与 daemon
428
644
  中的每个完整 assistant/tool step 都会同步追加到 `history.json.journal.jsonl`;turn
429
645
  正常结束时再原子物化兼容的 `history.json` 并删除 journal。进程被外部终止时,加载器
@@ -547,7 +763,8 @@ arena -p 'redraw this photo with the reference face' \
547
763
  网络图片使用统一的会话部件格式:图片部件
548
764
  `{"image": ...}` 在前,随后是 prompt 文本 `{"text": ...}`,最后追加一条
549
765
  `<arena-system-message>`,列出保存到 `/home/user/uploads/` 的文件名。单条
550
- 消息最多 10 个附件,单个附件上限 20 MB、总量上限 50 MB,与 daemon 一致。
766
+ 消息最多 10 个附件;Agent Arena 普通附件上限 25 MB、PDF 上限 10 MB,Code Arena
767
+ 单文件上限仍为 20 MB。总量上限为 50 MB,与 daemon 一致(按 1 MB = 1024² bytes 计)。
551
768
 
552
769
  多模态 user 消息(附件、图片 `read_file` 回灌、中断通知)在历史与事件流中
553
770
  一律使用 `{"image": ...}` / `{"text": ...}` 原生部件并保持字段格式一致;仅在
@@ -557,8 +774,15 @@ URL(模型服务端会自行下载;恢复历史上下文时要求该 URL 仍
557
774
  需要缩放或转码,则记录处理后的 Base64 data-URI。本地图片在 attach 时不生成
558
775
  `image` 部件;后续 `read_file` 返回图片时仍可使用 Base64 data-URI。
559
776
 
560
- `stream-json` 每行一个 JSON 对象。`start`、`message`、`result` 事件中的
561
- `messages` 与输入样例保持相同契约:
777
+ `stream-json` 每行一个 JSON 对象。四种模式的 headless(Agent Arena、React、HTML、Full Stack)
778
+ 的 `message` 事件只带新增的 `message`;`start` 和最终 `result` 保留完整 `messages`,
779
+ 避免评测端缓存大量重复历史。需要每个 `message` 事件都带完整 `messages` 的旧消费者,
780
+ 可设置 `ARENA_STREAM_HISTORY=full` 恢复旧输出,默认值为 `incremental`。
781
+ 旧变量 `ARENA_CODE_STREAM_HISTORY` 作为所有模式的兼容别名保留,`ARENA_STREAM_HISTORY` 优先。
782
+ 增量模式使用 JSON Unicode 转义,解析后的文字和图片内容保持原样。
783
+ SDK 和 daemon 的事件保持原有格式。
784
+
785
+ 事件中的消息与输入样例保持相同契约:
562
786
 
563
787
  - system/user:`role` + `content`;支持图片输入的模型在读取图片后会持久化
564
788
  一条 multimodal user message
@@ -585,6 +809,10 @@ export ARENA_ASK_USER_TIMEOUT_MS=60000
585
809
  arena -p 'build a home page'
586
810
  ```
587
811
 
812
+ `ARENA_ASK_USER_TIMEOUT_MS` 以毫秒计,限制从发起请求到读完响应正文的总时长。
813
+ 较慢的 User Agent 可设为 `3600000`(1 小时),等待不会被客户端额外的
814
+ 300 秒响应头或响应体超时提前中断。
815
+
588
816
  远程请求使用 `application/json`,body 只含 `sessionId`、`toolCallId`、
589
817
  `questions` 和字符串形式的完整 ATIF `trajectory`;当前不发送 token 或鉴权
590
818
  header。合法的 `answers`/`skipped` 会直接写成 tool result。服务端 `error`、
@@ -799,7 +1027,8 @@ message。未提供位置时,prompt 会明确标记位置不可用,不会使
799
1027
  附件采用两阶段提交。客户端先将每个文件作为原始请求体上传到 `uploads`
800
1028
  接口,并通过 URI 编码的 `x-file-name` header 传递文件名;再把返回的
801
1029
  `attachment.path` 作为 `{ "type": "file", "path": "uploads/..." }`
802
- part 放入实时消息。每个文件最大 20 MB,每条消息最多 10 个文件、合计
1030
+ part 放入实时消息。Agent Arena 普通文件最大 25 MB、PDF 最大 10 MB;Code Arena
1031
+ 每个文件最大 20 MB。每条消息最多 10 个文件、合计
803
1032
  50 MB。服务端会把文件写入该 session 工作区的 `uploads/` 目录,
804
1033
  并在 user message 最后追加 `arena-system-message` 附件清单。
805
1034
  上传的图片由模型按需调用 `read_file` 查看。附件引用只能指向本 session 的
@@ -875,7 +1104,10 @@ curl -N -X POST \
875
1104
 
876
1105
  构建时会将两份规范生成为 `dist` 内的 JS 数据模块;npm 包不包含原始
877
1106
  `spec/` 目录,运行时也不依赖仓库文件。执行工具前仍使用 AJV 校验原始
878
- Draft-07 schema。repo mode 的动态提示词模板保存在 `src/prompts.ts`:
1107
+ Draft-07 schema。普通模式的审计模板也从同一份生成数据取得;`src/prompts.ts`
1108
+ 中直接维护的正文用于 Repo 模式。
1109
+
1110
+ Agent Arena 提供以下工具:
879
1111
 
880
1112
  `bash`、`ask_user`、`add_voice`、`edit_file`、`read_file`、`fetch_page`、
881
1113
  `web_search`、`write_file`、`image_search`、`present_file`、`stop_process`、
@@ -884,8 +1116,10 @@ Draft-07 schema。repo mode 的动态提示词模板保存在 `src/prompts.ts`
884
1116
  新版生产导出的前 50 条脱敏请求单独保存在
885
1117
  [`tasks/v2`](tasks/v2/README.md),没有与原有 replay 数据混放。该目录记录了
886
1118
  2 GB 源文件的 SHA-256、全量解析统计、tool schema 变体和可重复执行的拆分命令;
887
- `npm run refresh:arena-v2-contract` 用它刷新 canonical prompt/tool fixtures 并重新
888
- 生成 `src/canonical-data.ts`。
1119
+ 每条历史样本在 `request.messages` 中保留当时的系统提示词,不另存一份提示词 TXT。
1120
+ `npm run refresh:arena-v2-contract` 是从这批旧数据重新导入契约的历史维护命令,
1121
+ 会用首条样本覆盖 `spec/message.json` 并重新生成派生数据;它不用于日常构建或
1122
+ 获取最新官方提示词。
889
1123
 
890
1124
  `bash` 只用于一次性命令:超时结果为 `status=timeout`,被信号终止为
891
1125
  `status=killed`,并会清理命令遗留的后台进程。需要跨 tool call 存活的 server、
@@ -900,7 +1134,8 @@ watcher 或任务应使用 `start_process`;随后可用 `get_process_output`
900
1134
  保持普通捕获的结果 shape 不变。
901
1135
 
902
1136
  `web_search` 使用 Routify 网关提供的 Brave LLM Context,从工作区 `.env`
903
- 读取 `ROUTIFY_API_KEY`,默认通过内置的 ModelRouter 兼容接口调用
1137
+ 优先读取 `ROUTIFY_WEB_SEARCH_API_KEY`,未设置或留空时读取 `ROUTIFY_API_KEY`,
1138
+ 默认通过内置的 ModelRouter 兼容接口调用
904
1139
  `brave.llm-context` 路由。实现按 depth 请求最多 3/5/8 条结果,并将
905
1140
  `grounding.generic` 的内容片段及 `sources` 的来源信息转换成 Arena 的既有工具
906
1141
  结果。请求固定考虑最多 20 个搜索候选,再通过 `maximum_number_of_urls` 将
@@ -911,8 +1146,9 @@ snippets,仅在没有 grounding 内容时回退到来源摘要。三个 Code A
911
1146
  token 预算 512/1280/1536。
912
1147
  可用 `ROUTIFY_WEB_SEARCH_URL` 和 `ARENA_WEB_SEARCH_MODEL` 覆盖默认值。
913
1148
 
914
- `image_search` 使用 Routify 网关提供的 Brave Images Search,同样读取
915
- `ROUTIFY_API_KEY`,通过 `X-Subscription-Token` 发送 GET 请求。默认使用内置的
1149
+ `image_search` 使用 Routify 网关提供的 Brave Images Search,优先读取
1150
+ `ROUTIFY_IMAGE_SEARCH_API_KEY`,未设置或留空时读取 `ROUTIFY_API_KEY`,
1151
+ 通过 `X-Subscription-Token` 发送 GET 请求。默认使用内置的
916
1152
  ModelRouter 兼容接口,可用 `ROUTIFY_IMAGE_SEARCH_URL` 覆盖。实现优先下载 Brave
917
1153
  返回的原图地址,原图不可用
918
1154
  时回退到 Brave 代理缩略图,并保持既有工具结果格式和工作区文件命名不变。
@@ -923,10 +1159,25 @@ Arena 的 `fetch_page` 和 Code Arena 的 `web_fetch` 默认仍使用本地抓
923
1159
  错误。`fetch_page` 仍按 8000 字符分块并缓存同一 URL 的后续 chunk。认证复用
924
1160
  `ROUTIFY_API_KEY`。
925
1161
 
926
- `generate_image`、`generate_speech` 使用 ModelRouter/Routify,并从工作区
927
- `.env` 读取同一个 `ROUTIFY_API_KEY`。图片使用 Google 兼容协议调用;可用
928
- `ROUTIFY_VERTEX_BASE_URL` 覆盖默认根地址。语音默认也通过该 Google 协议
929
- 调用 Gemini TTS,不需要新增服务密钥。
1162
+ `generate_image`、`generate_speech` 使用 ModelRouter/Routify。图片生成优先读取
1163
+ `ROUTIFY_GENERATE_IMAGE_API_KEY`,未设置或留空时回退到 `ROUTIFY_API_KEY`;
1164
+ 语音仍使用 `ROUTIFY_API_KEY`。GPT Image 图片生成与编辑使用 OpenAI 图片
1165
+ 协议,可用 `ROUTIFY_OPENAI_BASE_URL` 覆盖 ModelRouter 根地址。显式选择 Gemini
1166
+ 图片模型及默认 Gemini TTS 时使用 Google 协议,可用 `ROUTIFY_VERTEX_BASE_URL`
1167
+ 覆盖对应根地址。
1168
+
1169
+ 例如,搜索直连 MR、GPT Image 生图走 proxy 时,可以在 `.env` 中配置:
1170
+
1171
+ ```dotenv
1172
+ ROUTIFY_API_KEY=your-real-mr-key
1173
+ ROUTIFY_GENERATE_IMAGE_API_KEY=your-proxy-key
1174
+ ROUTIFY_OPENAI_BASE_URL=https://your-proxy.example/v1
1175
+ ARENA_IMAGE_MODEL=gpt-image-2
1176
+ ```
1177
+
1178
+ Gemini 生图使用同一个生图专用 key,但 proxy 地址需配置到
1179
+ `ROUTIFY_VERTEX_BASE_URL`(该地址也供 Gemini TTS 使用)。新密钥配置对 Agent Arena
1180
+ 和提供对应工具的 Code Arena 模式均生效,不改变请求协议、模型默认值或工具契约。
930
1181
 
931
1182
  `fetch_page` 使用 Defuddle 将 HTML 正文转换为 Markdown,并使用
932
1183
  `pdf-parse` 提取 PDF 的前 30 页。响应按 Markdown 边界稳定分块,同一轮内
@@ -941,8 +1192,20 @@ XLSX 仍可提取文字和表格内容,旧版 DOC、PPT、XLS 会先通过 Lib
941
1192
  内容继续发送给模型。所有工具结果另有 1,000,000 字符的最终兜底。当前不做
942
1193
  OCR,扫描版 PDF 只会返回无可提取文字的提示。
943
1194
 
944
- 图片和语音的默认 ModelRouter 路由分别为 `gemini-3.1-flash-image` 和
945
- `gemini-3.1-flash-tts-preview`,可用 `ARENA_IMAGE_MODEL`、`ARENA_SPEECH_MODEL` 覆盖。图片生成结果和
1195
+ Agent Arena 单图生成默认在 `gemini-3.1-flash-image` 和 `gpt-image-2` 中每次等概率随机选择;
1196
+ 设置 `ARENA_IMAGE_MODEL` 可固定模型。语音默认使用 `gemini-3.1-flash-tts-preview`,
1197
+ 可用 `ARENA_SPEECH_MODEL` 覆盖。
1198
+ `generate_image` 的 `offer_options=true` 默认并行调用 Gemini 和 GPT Image 2 生成候选图,等用户选择后
1199
+ 把选中的图保存到 `file_path`,另一张保存到工作区根目录的 `Unselected files`。
1200
+ 选择“两张都不要”或跳过时保留已有目标文件。省略该参数或传 `false` 仍直接生成一张图。
1201
+ 第一模型可用 `ARENA_IMAGE_MODEL` 指定,第二模型可用 `ARENA_IMAGE_ALTERNATIVE_MODEL` 指定,
1202
+ 默认 `gpt-image-2`;第一模型已经使用它时,第二模型默认改为 `gemini-3.1-flash-image`,两者不能相同。
1203
+ Web UI 直接展示两张图;TUI 给出候选图片路径供查看。没有交互 handler 的 headless 模式
1204
+ 仍生成两张候选图,默认保存第一张并继续执行;也可配置支持图片预览的远程用户 handler 供选择。未完成的候选图保存在
1205
+ `ARENA_HOME/image-options`,同一会话重试相同请求时可复用;选择完成后清理临时图片。
1206
+ Code Arena 单图生成默认在 `gpt-image-2.5-sunburst` 和 `gpt-image-2.5-flare` 中
1207
+ 每次等概率随机选择,暂不使用 Grok 和 `muse-image`。配置时优先使用 `CODE_HARNESS_IMAGE_MODEL` 固定模型,
1208
+ 其次使用显式设置的 `ARENA_IMAGE_MODEL`。Code Arena 不提供 `offer_options`。图片生成结果和
946
1209
  搜索到的图片会立即下载到工作区;每次图片搜索的结果按生产命名保存为
947
1210
  `image-search/<query slug>-<序号>.<扩展名>`,并作为可预览文件显示在 Web UI。
948
1211
  语音生成先由 `add_voice` 生成两个试听候选并让用户选择,选择结果注册为稳定的
package/dist/agent.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type ArenaConfig } from "./config.js";
2
- import { type SessionMemoryCache } from "./compaction.js";
2
+ import { type StoredSessionMemory, type SessionMemoryCache } from "./compaction.js";
3
+ import { type ModelRequestObserver } from "./provider.js";
3
4
  import { EventSink } from "./protocol.js";
4
5
  import { type HarnessMode } from "./mode.js";
5
6
  import { ToolExecutor } from "./tools.js";
@@ -15,18 +16,22 @@ interface AgentOptions {
15
16
  signal?: AbortSignal;
16
17
  clientContext?: ClientContext;
17
18
  workspaceContext?: WorkspaceContext;
18
- onCheckpoint?: (messages: readonly Message[], tools: readonly ToolDefinition[]) => Promise<void>;
19
- /** Inspect output repetition and complete tool-call batches before recording them. */
19
+ /** Restore the session's selected direct workspace child. */
20
+ repoDirectory?: string;
21
+ onCheckpoint?: (messages: readonly Message[], tools: readonly ToolDefinition[], sessionMemory?: StoredSessionMemory | null) => Promise<void>;
22
+ /** Inspect repetition, tool-call batches and pending Agent Arena compactions. */
20
23
  midturnCheckpointHook?: MidturnCheckpointHook;
21
24
  /** Materialize the current history and create the external snapshot. */
22
25
  onMidturnCheckpoint?: (messages: readonly Message[], tools: readonly ToolDefinition[], event: MidturnCheckpointHookEvent, action: Exclude<CheckpointAction, "continue">) => Promise<void>;
23
26
  /** Finish durable history before emitting a successful pause result. */
24
27
  onPause?: (messages: readonly Message[], tools: readonly ToolDefinition[]) => Promise<void>;
25
- /** Share ephemeral background updates across turns of one live session. */
28
+ /** Share background updates across invocations. Restoration is a run option. */
26
29
  sessionMemory?: SessionMemoryCache;
27
- /** Internal replay/test override; production Agent Arena uses the fixed 250k threshold. */
30
+ storedSessionMemory?: StoredSessionMemory;
31
+ /** Explicit Agent Arena override of the resolved compaction threshold. */
28
32
  compactionThresholdTokens?: number;
29
33
  onModelFailure?: (messages: readonly Message[], error: unknown) => void;
34
+ onModelRequest?: ModelRequestObserver;
30
35
  /** Reuse immutable loaded history messages instead of cloning the full prefix. */
31
36
  reuseHistoryMessages?: boolean;
32
37
  }
@@ -35,13 +40,20 @@ interface AgentRunOptions {
35
40
  userMessage?: UserTurnMessage;
36
41
  resumeFromHistory?: boolean;
37
42
  singleStep?: boolean;
43
+ /** Overrides config; defaults to true. Restores the cache without forcing compaction. */
44
+ restoreSessionMemory?: boolean;
38
45
  }
39
46
  export declare function isContinuableHistory(messages: readonly Message[]): boolean;
40
- export declare function resolveRepoContext(workspace: string, _history?: Message[], mode?: HarnessMode): RepoContext | undefined;
47
+ export declare function resolveRepoContext(workspace: string, _history?: Message[], mode?: HarnessMode, repoDirectory?: string): RepoContext | undefined;
41
48
  export declare class Agent {
42
49
  private readonly config;
43
50
  private readonly sink;
44
51
  private readonly options;
52
+ private checkpointQueue;
53
+ private readonly restoredToolCalls;
54
+ private checkpointError?;
55
+ private persistedMemory?;
56
+ private repoDirectory?;
45
57
  private readonly memoryCache;
46
58
  private readonly client;
47
59
  private readonly tools;
@@ -50,7 +62,9 @@ export declare class Agent {
50
62
  readonly sessionId: string;
51
63
  constructor(config: ArenaConfig, sink: EventSink, options?: AgentOptions);
52
64
  run(prompt: string, history?: Message[], runOptions?: AgentRunOptions): Promise<AgentResult>;
65
+ private runTurn;
53
66
  private checkpoint;
67
+ private finish;
54
68
  private makeCheckpoint;
55
69
  private pause;
56
70
  private maybeCompact;