@qwen-code/arena-agent 0.0.9-dev.8 → 0.0.10-dev.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 (87) hide show
  1. package/.env.example +54 -19
  2. package/README.md +218 -45
  3. package/dist/agent.d.ts +4 -2
  4. package/dist/agent.js +167 -21
  5. package/dist/agent.js.map +1 -1
  6. package/dist/anthropic.d.ts +1 -0
  7. package/dist/anthropic.js +90 -23
  8. package/dist/anthropic.js.map +1 -1
  9. package/dist/attachments.d.ts +1 -1
  10. package/dist/attachments.js +16 -4
  11. package/dist/attachments.js.map +1 -1
  12. package/dist/brave-search.d.ts +10 -0
  13. package/dist/brave-search.js +238 -0
  14. package/dist/brave-search.js.map +1 -0
  15. package/dist/canonical-data.js +2 -2
  16. package/dist/canonical-data.js.map +1 -1
  17. package/dist/cli.js +64 -18
  18. package/dist/cli.js.map +1 -1
  19. package/dist/code-tools.d.ts +0 -5
  20. package/dist/code-tools.js +29 -353
  21. package/dist/code-tools.js.map +1 -1
  22. package/dist/compaction.js +8 -2
  23. package/dist/compaction.js.map +1 -1
  24. package/dist/config.d.ts +30 -0
  25. package/dist/config.js +57 -3
  26. package/dist/config.js.map +1 -1
  27. package/dist/fetch-page.d.ts +1 -0
  28. package/dist/fetch-page.js +1 -1
  29. package/dist/fetch-page.js.map +1 -1
  30. package/dist/history.d.ts +3 -2
  31. package/dist/history.js +7 -2
  32. package/dist/history.js.map +1 -1
  33. package/dist/image-input-fallback.d.ts +9 -0
  34. package/dist/image-input-fallback.js +47 -0
  35. package/dist/image-input-fallback.js.map +1 -0
  36. package/dist/image.d.ts +16 -0
  37. package/dist/image.js +380 -0
  38. package/dist/image.js.map +1 -0
  39. package/dist/mock.d.ts +1 -0
  40. package/dist/mock.js +38 -0
  41. package/dist/mock.js.map +1 -1
  42. package/dist/model-capabilities.js +12 -2
  43. package/dist/model-capabilities.js.map +1 -1
  44. package/dist/model-diagnostics.js +16 -5
  45. package/dist/model-diagnostics.js.map +1 -1
  46. package/dist/model-router-media.d.ts +26 -0
  47. package/dist/model-router-media.js +274 -0
  48. package/dist/model-router-media.js.map +1 -0
  49. package/dist/openai-responses.d.ts +28 -0
  50. package/dist/openai-responses.js +473 -0
  51. package/dist/openai-responses.js.map +1 -0
  52. package/dist/process-manager.d.ts +38 -0
  53. package/dist/process-manager.js +486 -0
  54. package/dist/process-manager.js.map +1 -0
  55. package/dist/prompts.js +3 -1
  56. package/dist/prompts.js.map +1 -1
  57. package/dist/provider.d.ts +7 -0
  58. package/dist/provider.js +291 -27
  59. package/dist/provider.js.map +1 -1
  60. package/dist/routify-web-fetch.d.ts +17 -0
  61. package/dist/routify-web-fetch.js +188 -0
  62. package/dist/routify-web-fetch.js.map +1 -0
  63. package/dist/sdk.d.ts +9 -2
  64. package/dist/sdk.js +5 -2
  65. package/dist/sdk.js.map +1 -1
  66. package/dist/server.d.ts +3 -0
  67. package/dist/server.js +112 -21
  68. package/dist/server.js.map +1 -1
  69. package/dist/spec.js +15 -1
  70. package/dist/spec.js.map +1 -1
  71. package/dist/tools.d.ts +2 -1
  72. package/dist/tools.js +250 -340
  73. package/dist/tools.js.map +1 -1
  74. package/dist/tui.d.ts +3 -0
  75. package/dist/tui.js +18 -11
  76. package/dist/tui.js.map +1 -1
  77. package/dist/types.d.ts +17 -6
  78. package/dist/voice-registry.d.ts +30 -0
  79. package/dist/voice-registry.js +82 -0
  80. package/dist/voice-registry.js.map +1 -0
  81. package/dist/web/app.js +67 -6
  82. package/dist/web/app.js.map +1 -1
  83. package/docs/BINARY_DISTRIBUTION.md +25 -18
  84. package/docs/MODES_AND_RELEASES.md +8 -4
  85. package/docs/SDK.md +28 -4
  86. package/package.json +21 -3
  87. package/web/styles.css +28 -7
package/.env.example CHANGED
@@ -7,6 +7,14 @@ OPENAI_MODEL=openai/qwen-latest-series-invite-beta-v92
7
7
  OPENAI_BASE_URL=
8
8
  OPENAI_API_KEY=
9
9
 
10
+ # Auto mode uses Responses for GPT model names and Chat Completions otherwise.
11
+ # Native Responses preserves opaque OpenAI reasoning across tool steps and
12
+ # separate CLI turns by replaying encrypted output items with store=false.
13
+ # Set chat to force Chat Completions.
14
+ # ARENA_OPENAI_API=auto
15
+ # ARENA_OPENAI_API=responses
16
+ # ARENA_OPENAI_API=chat
17
+
10
18
  # Unified runtime mode. Canonical values are shown; full_stack/fullstack and
11
19
  # single_html/single-html remain accepted compatibility aliases.
12
20
  ARENA_HARNESS_MODE=arena
@@ -50,6 +58,10 @@ ARENA_HARNESS_MODE=arena
50
58
  # ARENA_THINKING_BUDGET=81920
51
59
  # ARENA_ENABLE_THINKING=true
52
60
  # ARENA_PRESERVE_THINKING=true
61
+ # Remove Qwen from the identity-model list in Arena system prompts. Canonical
62
+ # prompt fixtures and Code Arena prompts are unchanged.
63
+ # ARENA_EXCLUDE_QWEN_FROM_SYSTEM_PROMPT=false
64
+ # CLI equivalent: --exclude-qwen-from-system-prompt
53
65
  # Context is summarized before a model call when the estimated input reaches
54
66
  # this fraction of the configured model context window.
55
67
  # ARENA_CONTEXT_WINDOW=1000000
@@ -58,12 +70,23 @@ ARENA_HARNESS_MODE=arena
58
70
  # ARENA_EXTRA_BODY={"enable_thinking":true}
59
71
  # Transient model failures use full-jitter exponential backoff. The retry delay
60
72
  # is capped by ARENA_MODEL_RETRY_MAX_MS and honors Retry-After when present.
61
- # ARENA_MODEL_MAX_RETRIES=5
73
+ # ARENA_MODEL_MAX_RETRIES=10
62
74
  # ARENA_MODEL_RETRY_BASE_MS=1000
63
- # ARENA_MODEL_RETRY_MAX_MS=30000
75
+ # ARENA_MODEL_RETRY_MAX_MS=120000
64
76
  # A stream that stays open but delivers no bytes for this long is aborted and
65
77
  # retried like a network fault (0 disables). Some gateways stall silently.
66
78
  # ARENA_MODEL_STREAM_IDLE_TIMEOUT_MS=300000
79
+ # Explicit main-model media capability overrides. When image support resolves to
80
+ # false, the optional fallback model converts Arena read_file images to text.
81
+ # ARENA_MODEL_SUPPORTS_IMAGE_INPUT=false
82
+ # ARENA_IMAGE_INPUT_FALLBACK_MODEL=qwen3.7-plus
83
+ # Inline image budget before downscaling: max pixels per edge and max 28x28
84
+ # patches. Images within both limits are sent unchanged; larger ones are
85
+ # downscaled to fit and re-encoded as JPEG. Defaults match the captured
86
+ # baseline; raise both for long screenshots (a 1920x5344 full-page capture
87
+ # needs about 8192 edge and 13179 patches to pass through unchanged).
88
+ # ARENA_IMAGE_MAX_EDGE=1568
89
+ # ARENA_IMAGE_MAX_PATCHES=1568
67
90
 
68
91
  # Optional headless ask_user delegation. When enabled, `arena -p` sends the
69
92
  # current questions plus a serialized ATIF trajectory to this JSON endpoint.
@@ -72,10 +95,13 @@ ARENA_HARNESS_MODE=arena
72
95
  # ARENA_ASK_USER_HANDLER_URL=http://127.0.0.1:8080/v1/ask-user
73
96
  # ARENA_ASK_USER_TIMEOUT_MS=60000
74
97
 
75
- # Dynamic session metrics. High-confidence Bash cases use deterministic rules;
76
- # ambiguous Bash attribution/termination plus user praise, complaint, correction,
77
- # repair-attempt, and repair-verdict classification use this tool-free structured
78
- # side query. Disable it to retain those cases as unclassified/ambiguous.
98
+ # Dynamic session metrics. Disabled by default: with the switch off the daemon
99
+ # performs no metric analysis, persists no metrics, and emits no
100
+ # data-session-metrics events. Enable it to compute a snapshot after every
101
+ # user-facing turn; high-confidence Bash cases use deterministic rules, while
102
+ # ambiguous Bash attribution/termination plus user praise, complaint,
103
+ # correction, repair-attempt, and repair-verdict classification use this
104
+ # tool-free structured side query.
79
105
  # ARENA_METRICS_SIDE_QUERY_ENABLED=true
80
106
  # ARENA_METRICS_SIDE_QUERY_MODEL=openai/qwen-latest-series-invite-beta-v92
81
107
  # ARENA_METRICS_SIDE_QUERY_MAX_TOKENS=800
@@ -93,29 +119,38 @@ ARENA_HARNESS_MODE=arena
93
119
  # defaults to ~/.arena so runs never write into the task workspace.
94
120
  # ARENA_HOME=~/.arena
95
121
 
96
- # Tool backends.
97
- DASHSCOPE_API_KEY=
98
- # Optional: a workspace-specific host, with or without /api/v1.
99
- # DASHSCOPE_API_BASE=https://your-workspace.cn-beijing.maas.aliyuncs.com
100
- # ARENA_WEB_SEARCH_MODEL=qwen-plus
101
- # ARENA_IMAGE_SEARCH_MODEL=qwen3.7-plus
102
- # ARENA_IMAGE_MODEL=qwen-image-2.0-pro
103
- # ARENA_IMAGE_SIZE=2048*2048
104
- # ARENA_SPEECH_MODEL=qwen-audio-3.0-tts-flash
105
- # DASHSCOPE_SPEECH_VOICE=longanhuan_v3.6
122
+ # Search, page reading, image generation, and speech through ModelRouter/Routify.
123
+ ROUTIFY_API_KEY=
124
+ # ROUTIFY_WEB_SEARCH_URL=https://routify-pub.alibaba-inc.com/protocol/compatible-mode/res/v1/llm/context
125
+ # ROUTIFY_IMAGE_SEARCH_URL=https://routify-pub.alibaba-inc.com/protocol/brave/res/v1/images/search
126
+ # ROUTIFY_VERTEX_BASE_URL=https://routify-pub.alibaba-inc.com/protocol/vertex/v1beta
127
+ # ARENA_WEB_SEARCH_MODEL=brave.llm-context
128
+ # CODE_HARNESS_WEB_SEARCH_MODEL=brave.llm-context
129
+ # Optional fetch_page/web_fetch path: Firecrawl first, then Jina Reader.
130
+ # CODE_HARNESS_ROUTIFY_WEB_FETCH_ENABLED=false
131
+ # ARENA_IMAGE_MODEL=gemini-2.5-flash-image
132
+ # ARENA_IMAGE_SIZE=1024*1024
133
+ # CODE_HARNESS_IMAGE_MODEL=gemini-2.5-flash-image
134
+ # CODE_HARNESS_IMAGE_SIZE=1024*1024
135
+ # ARENA_SPEECH_MODEL=doubao-tts
136
+ # ARENA_SPEECH_VOICE=zh_female_vv_uranus_bigtts
137
+
138
+ # start_process automatically checks E2B preview hosts when E2B_SANDBOX_ID is
139
+ # present. For another preview proxy, use {port} and optionally {sandboxId}:
140
+ # ARENA_PREVIEW_HOST_TEMPLATE={port}-{sandboxId}.e2b.app
141
+ # ROUTIFY_MEDIA_MAX_RETRIES=5
106
142
 
107
143
  # Code Arena stock photo/video search used by fetch_media.
108
144
  # PEXELS_API_KEY=
109
145
  # CODE_HARNESS_MEDIA_CACHE_DIR=/tmp/code-harness-media-cache
110
146
  # CODE_HARNESS_MEDIA_CACHE_TTL_MS=86400000
111
147
 
112
- # Full Stack build_and_start runtime. It builds and health-checks the Next.js
113
- # preview and can start PostgreSQL 16 in Docker for an unavailable local URL.
148
+ # Full Stack build_and_start runtime. PostgreSQL and psql are environment
149
+ # prerequisites, matching the captured sandbox baseline.
114
150
  # CODE_HARNESS_PREVIEW_PORT=3000
115
151
  # CODE_HARNESS_PREVIEW_URL=http://127.0.0.1:3000
116
152
  # CODE_HARNESS_FULLSTACK_BUILD_TIMEOUT_MS=300000
117
153
  # CODE_HARNESS_BASH_TIMEOUT_SECONDS=300
118
- # CODE_HARNESS_POSTGRES_IMAGE=postgres:16-alpine
119
154
 
120
155
  # Optional LibreOffice executable for read_file support of legacy .doc/.ppt/.xls.
121
156
  # ARENA_SOFFICE_PATH=/usr/local/bin/soffice
package/README.md CHANGED
@@ -1,13 +1,17 @@
1
1
  # Arena CodeAgent Harness
2
2
 
3
- 这是一个根据 `message.json` 与 `tools.json` 逆向实现的 TypeScript CodeAgent
4
- harness。运行时只依赖 Node.js,不再依赖 Python:
3
+ 这是一个基于 `message.json` 与 `tools.json` 所定义消息和工具契约的 TypeScript
4
+ CodeAgent harness。运行时只依赖 Node.js,不再依赖 Python:
5
5
 
6
6
  - `arena -p ...`:headless 单轮入口,支持文本、完整 JSON 和 NDJSON 流。
7
7
  - `arena tui`:终端多轮对话,实时输出并自动保存、恢复历史。
8
8
  - `arena serve`:HTTP daemon、Web UI 与双通道 SSE 实时接口。
9
9
  - Agent loop:流式 assistant → tool call → tool result → assistant。
10
- - 11 个工具逐字使用 `tools.json` 的 Draft-07 schema。
10
+ - 15 个工具按照 `tools.json` 中的 Draft-07 schema 提供参数校验。
11
+
12
+ 本文提及的第三方产品、模型与服务名称仅用于说明兼容性和配置方式,不表示相关
13
+ 权利人对本项目的认可或背书;相关商标归各自权利人所有。使用者应自行确认并遵守
14
+ 所接入模型、数据源和在线服务的许可、服务条款及适用法律。
11
15
 
12
16
  ## 源码与 SDK 安装
13
17
 
@@ -49,6 +53,9 @@ npm ci
49
53
  npm run package:binary
50
54
  ```
51
55
 
56
+ 其中 `linux-x64` 产物使用 glibc 2.17 兼容的内嵌 Node.js Runtime,目标机器无需
57
+ 安装 Node.js;Alpine/musl 系统请使用独立的 `linux-x64-musl` 产物。
58
+
52
59
  命令会生成 `build/sea/arena`(Windows 为 `arena.exe`),并打出当前平台包和
53
60
  `@qwen-code/arena-cli` 的 npm tarball。二进制必须针对目标操作系统、CPU 架构和
54
61
  Linux libc 分别构建;不能把 macOS 上生成的文件直接用于 Linux。
@@ -83,11 +90,61 @@ code-harness -p '做一个单文件网页' --mode html
83
90
  `CODE_HARNESS_MODE`、`ARENA_MODE` 及旧模式别名。详细目录和发包决策见
84
91
  [`docs/MODES_AND_RELEASES.md`](docs/MODES_AND_RELEASES.md)。
85
92
 
93
+ 如需完全替换模型收到的 system prompt,可在任一运行入口传入
94
+ `--system-prompt <text>`:
95
+
96
+ ```bash
97
+ arena -p '分析这个目录' --system-prompt '你是一名精简的代码审查助手。'
98
+ arena tui --system-prompt '始终先给结论,再解释原因。'
99
+ arena serve --system-prompt '只使用中文回复。'
100
+ ```
101
+
102
+ 该参数只在运行时覆盖第一条 system message,不会修改 canonical prompt fixture;
103
+ 续跑已有 session 时,当前传入的值会替换历史中的 system message。未传参数时,
104
+ 日期、workspace 和 mode 对应的现有 prompt 生成逻辑保持不变。
105
+
106
+ 如需保留当前 mode 生成的 system prompt,只在末尾追加任务或部署指令,可传入
107
+ `--append-system-prompt <text>`:
108
+
109
+ ```bash
110
+ arena -p '分析这个目录' --append-system-prompt '回答保持精简。'
111
+ arena tui --append-system-prompt '始终先给结论,再解释原因。'
112
+ arena serve --append-system-prompt '只使用中文回复。'
113
+ ```
114
+
115
+ 追加内容会作为独立段落放在运行时 system message 末尾,因此原有日期、workspace、
116
+ 工具和 mode 指令保持不变,也不会修改 canonical prompt fixture。同一会话内只会追加
117
+ 一次,不会随 turn 重复累积。使用 headless CLI 续跑 session 时,需要再次传入相同的
118
+ 追加参数。`--system-prompt` 与 `--append-system-prompt` 可以同时使用,此时先完整
119
+ 替换,再追加指定文本。
120
+
121
+ `arena serve` 是一个例外:未显式传入 `--system-prompt` 时,它会保留现有系统
122
+ 提示词中的日期、workspace、工具和 mode 指令,但默认将身份声明设为 `Qwen`。
123
+ 显式传入 `--system-prompt` 时仍会完整替换系统提示词;仅传
124
+ `--append-system-prompt` 时,则先应用该 Qwen 身份变换,再追加文本。
125
+
126
+ Arena 模式下,如需从 system prompt 的身份模型列表中移除 `Qwen`,可增加
127
+ `--exclude-qwen-from-system-prompt`,或设置
128
+ `ARENA_EXCLUDE_QWEN_FROM_SYSTEM_PROMPT=true`。CLI 开关优先于环境变量:
129
+
130
+ ```bash
131
+ arena -p '分析这个目录' --exclude-qwen-from-system-prompt
132
+ arena tui --exclude-qwen-from-system-prompt
133
+ arena serve --exclude-qwen-from-system-prompt
134
+ ```
135
+
136
+ 三个 Code Arena 模式的兼容性测试集、工具回归测试和模型行为评估入口见
137
+ [`tasks/code-harness/README.md`](tasks/code-harness/README.md);这些开发数据不会
138
+ 进入 npm 或 SEA 发布包。
139
+
86
140
  ## 模型
87
141
 
88
- TypeScript 运行时根据模型前缀选择协议:`openai/` 使用 OpenAI Chat
89
- Completions,`anthropic/` 使用原生 Messages,`gemini/` 使用 Google 的
90
- OpenAI-compatible 接口。LiteLLM 官方提供 Python SDK 或独立 Proxy,因此
142
+ TypeScript 运行时根据模型前缀与名称选择协议:`anthropic/` 使用原生
143
+ Messages,`gemini/` 使用 Google 的 OpenAI-compatible 接口;`openai/` 或
144
+ 无前缀模型使用 OpenAI 传输,其中模型名包含 `gpt`(大小写不敏感)时默认
145
+ 使用 Responses,其余使用 Chat Completions。无前缀且名称包含 `claude` 时
146
+ 使用 Anthropic Messages。显式 provider 前缀与 `ARENA_OPENAI_API` 始终优先。
147
+ LiteLLM 官方提供 Python SDK 或独立 Proxy,因此
91
148
  纯 TypeScript 版本使用
92
149
  [LiteLLM Proxy](https://docs.litellm.ai/) 作为多 provider 网关:
93
150
 
@@ -123,10 +180,19 @@ export OPENAI_API_KEY=...
123
180
  ```
124
181
 
125
182
  `OPENAI_BASE_URL` / `OPENAI_API_KEY` 是统一的模型连接入口,变量名不决定
126
- wire protocol。`OPENAI_MODEL=anthropic/...` 仍会向 `<base>/messages` 发送
127
- Anthropic Messages payload 并使用 `x-api-key`;`openai/...` 则会向
128
- `<base>/chat/completions` 发送 OpenAI payload 并使用 Bearer token。未设置
129
- 统一入口时,Anthropic 可回退到 `ANTHROPIC_BASE_URL` /
183
+ wire protocol。`OPENAI_MODEL=anthropic/...` 会向 `<base>/messages` 发送
184
+ Anthropic Messages payload 并使用 `x-api-key`;OpenAI 路由根据上述规则向
185
+ `<base>/chat/completions` 或 `<base>/responses` 发送 Bearer 请求。Responses
186
+ 路径固定使用 `store=false`,不会发送 `previous_response_id` 或 conversation。
187
+ 每次响应完成时,Harness 会把 `reasoning`、`function_call` 和 assistant message
188
+ 等原生 output items 原样保存在 assistant 历史中;其中 reasoning 的
189
+ `encrypted_content` 是 OpenAI 返回的不可读加密载荷。后续工具 step 和跨 CLI
190
+ 回合会把这些 output items 连同 function output 一起完整回放到下一次 `input`,
191
+ 从而在不依赖 provider 端存储的情况下保留隐藏 reasoning。请求显式包含
192
+ `reasoning.encrypted_content`;GPT-5.6 还会使用 `reasoning.context=all_turns`。
193
+ 设置 `ARENA_OPENAI_API=chat` 或 `responses` 可覆盖 GPT 的自动选择。
194
+ 该开关不作用于 LiteLLM、Gemini 或 Anthropic 路由。如未设置统一入口,
195
+ Anthropic 可回退到 `ANTHROPIC_BASE_URL` /
130
196
  `ANTHROPIC_API_KEY`。
131
197
 
132
198
  Anthropic 直连默认只用 `x-api-key` 鉴权(同时携带 Authorization 会让部分
@@ -143,11 +209,11 @@ Anthropic 直连在原生协议上完整支持 thinking。旧版 Claude 和非 C
143
209
  budget。Anthropic 要求启用 thinking 时不调整 temperature,此时不发送该
144
210
  参数。带签名的 thinking/redacted_thinking block 会原样保存在对应 assistant
145
211
  消息的 `reasoning_details` 字段——
146
- Anthropic 要求 tool-use 续跑时回传签名 thinking block,重放历史时会按
212
+ Anthropic 要求 tool-use 续跑时回传签名 thinking block,恢复历史上下文时会按
147
213
  原顺序还原;无签名的 thinking block(部分网关返回 `signature: ""`)
148
- 不参与重放,因为协议不需要它们,而且某些网关收到会挂起。
214
+ 不会再次发送,因为协议不需要它们,而且某些网关收到会挂起。
149
215
  `reasoning_content` 仍是纯文本推理,历史契约不变。向
150
- OpenAI-compatible 服务重放同一份历史时,`reasoning_details` 与 `usage`
216
+ OpenAI-compatible 服务发送同一份历史时,`reasoning_details` 与 `usage`
151
217
  一样在 wire 边界剥离。usage 会映射为 OpenAI 风格字段(`prompt_tokens`
152
218
  含 cache 创建/读取部分),同时保留 `input_tokens`、
153
219
  `cache_read_input_tokens` 等原生字段。`OPENAI_BASE_URL` 对 anthropic
@@ -174,8 +240,9 @@ ARENA_COMPACTION_SUMMARY_MAX_TOKENS=8192
174
240
  ```
175
241
 
176
242
  可选的 `ARENA_REASONING_EFFORT` 支持 `low`、`medium`、`high`、`xhigh`
177
- 和 `max` 五档;OpenAI-compatible 请求使用 `reasoning_effort`,原生
178
- Anthropic Messages 请求映射为 `output_config.effort`。不会按模型静默
243
+ 和 `max` 五档;Chat Completions 请求使用 `reasoning_effort`,Responses
244
+ 请求使用 `reasoning.effort`,原生 Anthropic Messages 请求映射为
245
+ `output_config.effort`。不会按模型静默
179
246
  降档;目标模型或兼容网关不支持所选档位时,由服务端返回明确错误。
180
247
  `ARENA_DISALLOWED_TOOLS` 是 JSON 字符串数组,工具名精确匹配:
181
248
 
@@ -190,13 +257,17 @@ ARENA_DISALLOWED_TOOLS=["bash","write_file"]
190
257
  参数覆盖,也会原样写入 `history.json` 的
191
258
  `metadata.parameters.extra_body`。
192
259
 
193
- `DASHSCOPE_API_KEY` 单独供搜索、图片生成和语音工具使用,不会作为模型连接的
194
- 密钥。
260
+ `ROUTIFY_API_KEY` 供 `web_search`、`image_search`、`generate_image` 和
261
+ `generate_speech` 使用;开启
262
+ `CODE_HARNESS_ROUTIFY_WEB_FETCH_ENABLED` 后,Arena 的 `fetch_page` 和 Code Arena 的
263
+ `web_fetch` 也复用该密钥,通过 Routify 先调用 Firecrawl,失败时回退到
264
+ Jina Reader。
265
+ 该密钥不会作为主模型连接的密钥。
195
266
 
196
267
  Harness 会在每次模型调用前估算消息、reasoning、工具 schema 和图片占用;达到
197
268
  `ARENA_CONTEXT_WINDOW × ARENA_CONTEXT_COMPACTION_RATIO`(默认 80%)时,调用
198
269
  当前模型把已完成的旧回合生成结构化续作摘要。只有发给模型的内存上下文会把
199
- 旧前缀替换为与 Arena trace 一致的 leading `<arena-system-message>`;持久化的
270
+ 旧前缀替换为符合既有会话格式的 leading `<arena-system-message>`;持久化的
200
271
  `history.json.messages` 保留原始消息,不会因压缩删除或替换前缀。当前用户回合及
201
272
  其后的 tool-call / result 链在模型上下文中保持原样。摘要重点保留目标、硬约束、
202
273
  发现、已完成/未完成事项、下一步和相关文件路径;摘要调用默认关闭 thinking,
@@ -204,20 +275,27 @@ Harness 会在每次模型调用前估算消息、reasoning、工具 schema 和
204
275
  `ARENA_COMPACTION_SUMMARY_MAX_TOKENS` 控制(默认 8192)。
205
276
 
206
277
  `ARENA_CONTEXT_WINDOW` 应设置为所用模型的真实上下文窗口;默认 `1000000` 是
207
- 当前 trace 模型的暂定值。token 数目前是本地保守估算,并非 provider tokenizer
278
+ 当前默认模型的暂定值。token 数目前是本地保守估算,并非 provider tokenizer
208
279
  的精确计数。如果摘要请求失败,Harness 会保留原历史并继续正常模型请求,避免
209
280
  摘要服务的瞬时故障直接中断任务。
210
281
 
282
+ Responses 与其他协议一样,会在每个工具 step 前检查是否需要压缩。由于所有
283
+ reasoning/function-call 状态都由本地 history 携带,压缩不再需要切断或重建 provider
284
+ 端 response 链。压缩会把已完成的旧回合替换成续作摘要,同时保留当前 user turn 里的
285
+ 加密 reasoning、function call 和 function output;下一次 Responses 请求直接回放这份
286
+ 压缩后的本地上下文。持久化的原始 `history.json` 仍保持 append-only。
287
+
211
288
  每次模型请求都会通过 OpenAI-compatible 的 `stream_options.include_usage`
212
289
  请求 token 用量,并把服务端在最终 chunk 上报的 `usage` 对象原样记录到该轮
213
290
  assistant 消息的 `usage` 字段(`history.json` 与事件流中均可见),供训练
214
- 和成本统计使用。向模型重放历史时,`usage` 会在 wire 边界剥离,不会发回
291
+ 和成本统计使用。再次发送历史上下文时,`usage` 会在 wire 边界剥离,不会发回
215
292
  服务端。`model.complete` 事件同样携带该轮 `usage`;上下文压缩的摘要调用
216
293
  不会在历史中留下消息,其用量通过 `compaction` 事件上报,只在事件流中可
217
294
  见。不支持 `stream_options` 的服务可通过 `ARENA_EXTRA_BODY` 覆盖该参数。
218
295
 
219
296
  模型请求遇到网络错误、HTTP 408/409/429 或 5xx 时会自动重试,默认最多重试
220
- 5 次。没有 `Retry-After` 时使用带随机抖动的指数退避;可通过
297
+ 10 次。没有 `Retry-After` 时使用带随机抖动的指数退避,单次等待默认最多
298
+ 2 分钟;可通过
221
299
  `ARENA_MODEL_MAX_RETRIES`、`ARENA_MODEL_RETRY_BASE_MS` 和
222
300
  `ARENA_MODEL_RETRY_MAX_MS` 调整。设 `ARENA_MODEL_MAX_RETRIES=0` 可关闭。
223
301
 
@@ -228,6 +306,14 @@ assistant 消息的 `usage` 字段(`history.json` 与事件流中均可见)
228
306
  `model.retry` 事件(TUI 会显示「reconnecting after a network error」提示)。
229
307
  用户主动取消(abort)不会被当作可重试的网络故障。
230
308
 
309
+ OpenAI Responses 还会检查 HTTP 200 SSE 中的 `error` / `response.failed`
310
+ 事件。带 408/409/429/5xx 或明确瞬时错误类型的事件会整轮重试;上游只给文本时,
311
+ 仅明确的 `experiencing high demand`、`maximum usage size allowed during peak
312
+ load`、`Provisioned Throughput` 容量错误会重试。容量错误最多重试 5 次
313
+ (`ARENA_MODEL_MAX_RETRIES` 可进一步降低或关闭),每次均重新发送完全相同的
314
+ 模型输入,并沿用 full-jitter 指数退避。上下文超限、参数、鉴权、媒体和安全错误
315
+ 等永久错误不会因正文中出现 `error` 一词而重试。
316
+
231
317
  Agent turn 默认没有模型 step 数上限;设置 `ARENA_MAX_TURNS` 后,达到指定上限
232
318
  会返回 error,已经完成的 assistant/tool step 仍会保留。整轮没有内建墙钟时长
233
319
  上限,会持续到模型正常结束、请求用户输入、发生不可恢复错误、达到显式配置的
@@ -292,12 +378,22 @@ Arena 还维护一份有顺序的模型输入模态表,在第一次 HTTP 调
292
378
  未知模型默认尝试发送,具体路由能力仍以端点返回为准。可以分别用
293
379
  `ARENA_MODEL_SUPPORTS_IMAGE_INPUT`、`ARENA_MODEL_SUPPORTS_VIDEO_INPUT`、
294
380
  `ARENA_MODEL_SUPPORTS_AUDIO_INPUT` 显式覆盖;覆盖值优先于内置表。
381
+ 当主模型被明确判定为不支持图片时,可设置
382
+ `ARENA_IMAGE_INPUT_FALLBACK_MODEL`,让普通 Arena 的图片 `read_file` 先由该模型
383
+ 转换为文字;主模型支持图片、模型未知或 Code Arena 模式均不触发此 fallback。
384
+ fallback 不继承 `ARENA_EXTRA_BODY` 或主模型生成参数;原生 Anthropic Messages
385
+ 因协议要求会单独使用 `max_tokens: 16384`。
386
+ 同一条件下,用户直接附加的图片不会发送给主模型;已有附件文件列表会保留,并提示
387
+ 模型调用 `read_file` 获取 fallback 生成的文字版本。
295
388
 
296
389
  输出事件及 JSON 结果中的 `session_id` 就是路径里的 `<sessionId>`。
297
390
  `history.json` 的 `messages` 是只追加的原始轨迹;上下文压缩只影响发送给模型的
298
391
  临时副本。`metadata` 记录 session、模型以及本次实际生效的 `max_turns`、
299
- `context_window`、生成和压缩参数。daemon 完成一次 turn 后还会写入
300
- `version: 3` 和 `metrics`,后者是截至该 turn 的 praise vs. complaint、
392
+ `context_window`、生成和压缩参数。`tools` 记录最近一次保存时实际提供给模型的
393
+ 完整工具定义,包括针对 workspace 的描述改写和不可用工具(未接 `ask_user`
394
+ handler、`ARENA_DISALLOWED_TOOLS`)的过滤结果。daemon 完成一次 turn 后还会写入
395
+ `version: 4`;开启动态指标统计(`ARENA_METRICS_SIDE_QUERY_ENABLED`,默认关闭)
396
+ 时还会写入 `metrics`,后者是截至该 turn 的 praise vs. complaint、
301
397
  steerability、Bash recovery 与 tool hallucination 增量快照。即使结果为
302
398
  `needs_input` 或 `error` 也会保存;旧的 `{"messages":[...]}` 历史仍可直接加载。
303
399
  需要跨命令续写同一份显式历史时,可另外传入
@@ -331,17 +427,17 @@ arena -p 'redraw this photo with the reference face' \
331
427
  每个附件会被保存到工作区 `uploads/`(模型内可通过
332
428
  `/home/user/uploads/<name>` 访问;重名自动加 `-2`、`-3` 后缀,URL 无扩展名
333
429
  时按图片字节特征补全扩展名)。本轮 user message 与 daemon 附件上传共用同
334
- 一构造逻辑,并使用与 Arena trace 相同的原生部件格式:图片部件
430
+ 一构造逻辑,并使用统一的会话部件格式:图片部件
335
431
  `{"image": ...}` 在前,随后是 prompt 文本 `{"text": ...}`,最后追加一条
336
432
  `<arena-system-message>`,列出保存到 `/home/user/uploads/` 的文件名。单条
337
433
  消息最多 10 个附件,单个附件上限 20 MB、总量上限 50 MB,与 daemon 一致。
338
434
 
339
435
  多模态 user 消息(附件、图片 `read_file` 回灌、中断通知)在历史与事件流中
340
- 一律使用 Arena 原生部件 `{"image": ...}` / `{"text": ...}`,与 baseline
341
- trace 逐字段同构;仅在向模型发起 chat-completions 请求的边界处转换为
436
+ 一律使用 `{"image": ...}` / `{"text": ...}` 原生部件并保持字段格式一致;仅在
437
+ 向模型发起 chat-completions 请求的边界处转换为
342
438
  OpenAI 风格的 `image_url` 部件。`--attach` 传 URL 时 `image` 直接记录该
343
- URL(与 baseline 引用附件的方式一致,模型服务端会自行下载;重放历史要求
344
- 该 URL 仍可访问);本地文件没有可引用的 URL,`image` 存自包含的 base64
439
+ URL(模型服务端会自行下载;恢复历史上下文时要求该 URL 仍可访问);本地文件
440
+ 没有可引用的 URL,`image` 存自包含的 base64
345
441
  data-URI。
346
442
 
347
443
  `stream-json` 每行一个 JSON 对象。`start`、`message`、`result` 事件中的
@@ -355,7 +451,12 @@ data-URI。
355
451
 
356
452
  `assistant.delta` 实时提供 content、reasoning 与 tool-call 参数碎片;
357
453
  `model.start`、`model.complete`、`model.retry`、`tool.start`、`tool.complete`
358
- 标记阶段。最后的 `result` 带完整可重放消息。
454
+ 标记阶段。最后的 `result` 带完整消息。
455
+
456
+ Headless 会向模型提供 `present_file`,以保持生成轨迹与 Arena 工具契约一致。
457
+ 该工具在 headless 中只校验目标是 workspace 内已有文件,并返回
458
+ `{"path":"<原始路径>","status":"success"}`;不会连接前端或打开 viewer。
459
+ 自动打开 viewer 仅由 daemon/WebUI 消费同一工具结果后触发。
359
460
 
360
461
  `ask_user` 只有在注入 handler 时才会提供给模型。TUI 和 WebUI 使用真人交互
361
462
  handler;headless(`-p`)默认仍不开放该工具,但可配置远程 User Agent handler:
@@ -399,8 +500,9 @@ arena tui --workspace . --model mock/ask-user --session-id my-session
399
500
  workspace 默认是启动命令时的当前目录(可用 `--workspace` 覆盖)。TUI 会把
400
501
  实际 workspace root 和 cwd 动态注入 system prompt 与工具 schema;因此本机
401
502
  运行时使用本机路径,部署进沙箱后会自然使用沙箱内的实际路径,不需要切换模式。
402
- TUI 没有内置文件 viewer,因此不会向模型提供 `present_file`;模型会直接报告
403
- 生成文件的实际路径。
503
+ TUI 会向模型提供 `present_file`,以保持生成轨迹与 Arena 工具契约一致。由于
504
+ TUI 没有内置文件 viewer,该工具只校验 workspace 内的目标文件并返回 canonical
505
+ 成功结果,不会打开文件;模型仍会在正文中报告生成文件的实际路径。
404
506
 
405
507
  assistant 的 reasoning、正文和工具调用状态会边生成边输出。每轮结束都会把
406
508
  完整消息原子写入与 headless、daemon 相同的 `history.json`;再次传入相同的
@@ -472,6 +574,17 @@ for await (const event of session.stream({ input: "设计一个网站" })) {
472
574
  }
473
575
  ```
474
576
 
577
+ 如果用户选择跳过问题,可提交结构化响应;这会写入
578
+ `{"answers":[],"skipped":true}` 并继续当前 turn,不会像 `cancel()` 那样
579
+ 中断 turn:
580
+
581
+ ```ts
582
+ await session.respondToInput(request.requestId, {
583
+ answers: [],
584
+ skipped: true,
585
+ });
586
+ ```
587
+
475
588
  持久连接、恢复和取消分别使用:
476
589
 
477
590
  ```ts
@@ -542,7 +655,7 @@ workspace,可设置 `ARENA_SESSION_WORKSPACE_MODE=empty`。
542
655
  | `GET /api/chat/sessions/:id/messages` | 恢复完整消息历史 |
543
656
  | `POST /api/chat/sessions/:id/retry` | 从最后一个失败模型节点继续,不追加 user 消息 |
544
657
  | `POST /api/chat/sessions/:id/uploads` | 上传当前消息的附件 |
545
- | `POST /api/chat/sessions/:id/input-response` | 回答 `ask_user` |
658
+ | `POST /api/chat/sessions/:id/input-response` | 回答或跳过 `ask_user` |
546
659
  | `POST /api/chat/sessions/:id/cancel` | 取消当前 turn |
547
660
  | `POST /api/chat/sessions/:id/undo` | 回滚最近完成的 turn,并返回原输入 |
548
661
  | `GET /api/chat/sessions/:id/workspace/:path` | 鉴权获取该 session 的 workspace 文件 |
@@ -577,6 +690,13 @@ part 放入实时消息。每个文件最大 20 MB,每条消息最多 10 个
577
690
  SSE 支持 `Last-Event-ID` 断线续传。`ask_user` 会暂停同一个 Agent Promise;
578
691
  用户提交答案后生成标准 `role=tool` 消息并继续循环。
579
692
 
693
+ Session SSE 会原样透传 `model.start`、`model.retry`、`model.complete` 和
694
+ `compaction` 生命周期事件。`model.complete.usage` 是当前模型请求的用量;
695
+ 压缩摘要不会进入消息历史,其用量只存在于 `compaction.usage`。WebUI 同时通过
696
+ `data-agent-status` 显示模型重试和上下文压缩状态。收到 `model.retry` 时,客户端
697
+ 应丢弃该次模型请求在此前产生的 reasoning、text 和 tool-call delta;重试后的
698
+ 增量会从干净状态重新开始。
699
+
580
700
  模型自动重试耗尽时,daemon 会把 `history.json` 保持在该模型调用之前的最后
581
701
  一个完整 checkpoint,并将对应的历史哈希和失败元数据原子记录到
582
702
  `model-retry.json`;消息内容仍以 `history.json` 为唯一真相源。手动重试只重新
@@ -628,7 +748,7 @@ curl -N -X POST \
628
748
 
629
749
  ## 工具
630
750
 
631
- 原始规范在源码仓库中逐字保存在:
751
+ 消息与工具契约配置保存在:
632
752
 
633
753
  - `spec/message.json`
634
754
  - `spec/tools.json`
@@ -637,14 +757,56 @@ curl -N -X POST \
637
757
  `spec/` 目录,运行时也不依赖仓库文件。执行工具前仍使用 AJV 校验原始
638
758
  Draft-07 schema。repo mode 的动态提示词模板保存在 `src/prompts.ts`:
639
759
 
640
- `bash`、`ask_user`、`edit_file`、`read_file`、`fetch_page`、`web_search`、
641
- `write_file`、`image_search`、`present_file`、`generate_image`、
642
- `generate_speech`。
643
-
644
- `web_search`、`image_search`、`generate_image`、`generate_speech` 都使用
645
- 阿里云百炼 DashScope,并从工作区 `.env` 读取 `DASHSCOPE_API_KEY`。默认
646
- 调用公共北京地域地址;若使用业务空间专属域名或新加坡地域,可设置
647
- `DASHSCOPE_API_BASE`(可带或不带 `/api/v1`)。
760
+ `bash`、`ask_user`、`add_voice`、`edit_file`、`read_file`、`fetch_page`、
761
+ `web_search`、`write_file`、`image_search`、`present_file`、`stop_process`、
762
+ `start_process`、`generate_image`、`generate_speech`、`get_process_output`。
763
+
764
+ 新版生产导出的前 50 条脱敏请求单独保存在
765
+ [`tasks/v2`](tasks/v2/README.md),没有与原有 replay 数据混放。该目录记录了
766
+ 2 GB 源文件的 SHA-256、全量解析统计、tool schema 变体和可重复执行的拆分命令;
767
+ `npm run refresh:arena-v2-contract` 用它刷新 canonical prompt/tool fixtures 并重新
768
+ 生成 `src/canonical-data.ts`。
769
+
770
+ `bash` 只用于一次性命令:超时结果为 `status=timeout`,被信号终止为
771
+ `status=killed`,并会清理命令遗留的后台进程。需要跨 tool call 存活的 server、
772
+ watcher 或任务应使用 `start_process`;随后可用 `get_process_output` 按日志、端口
773
+ 或退出状态阻塞等待,并用 `stop_process` 停止整个进程组。完整合并日志保存在
774
+ 工作区的 `.arena/processes/`,工具返回对应的 `log_tail`;`stop_process` 对齐生产
775
+ 只返回最后 100 行。assistant tool call 和这些 JSON tool result 会原样写入
776
+ `history.json`,供后续 turn replay。`start_process` 在 E2B 环境中还会用
777
+ `E2B_SANDBOX_ID` 组成 `{port}-{sandboxId}.e2b.app`,分别探测本地 Host 和用户
778
+ 预览 Host;如果新开的公网端口仅拒绝后者,结果会按需增加 `warnings`。其他预览
779
+ 代理可用 `ARENA_PREVIEW_HOST_TEMPLATE` 配置 Host 模板。没有告警时不返回该字段,
780
+ 保持普通捕获的结果 shape 不变。
781
+
782
+ `web_search` 使用 Routify 网关提供的 Brave LLM Context,从工作区 `.env`
783
+ 读取 `ROUTIFY_API_KEY`,默认通过内置的 ModelRouter 兼容接口调用
784
+ `brave.llm-context` 路由。实现按 depth 请求最多 3/5/8 条结果,并将
785
+ `grounding.generic` 的内容片段及 `sources` 的来源信息转换成 Arena 的既有工具
786
+ 结果。请求固定考虑最多 20 个搜索候选,再通过 `maximum_number_of_urls` 将
787
+ 最终来源数限制为 3/5/8。depth 还分别对应 1024/3072/8192 的总 token 预算和
788
+ 512/552/2048 的单 URL token 预算;description 使用预算内的全部 grounding
789
+ snippets,仅在没有 grounding 内容时回退到来源摘要。三个 Code Arena 模式
790
+ 使用独立的模式参数:来源数 3/5/10、总 token 预算 1280/4096/6144、单 URL
791
+ token 预算 512/1280/1536。
792
+ 可用 `ROUTIFY_WEB_SEARCH_URL` 和 `ARENA_WEB_SEARCH_MODEL` 覆盖默认值。
793
+
794
+ `image_search` 使用 Routify 网关提供的 Brave Images Search,同样读取
795
+ `ROUTIFY_API_KEY`,通过 `X-Subscription-Token` 发送 GET 请求。默认使用内置的
796
+ ModelRouter 兼容接口,可用 `ROUTIFY_IMAGE_SEARCH_URL` 覆盖。实现优先下载 Brave
797
+ 返回的原图地址,原图不可用
798
+ 时回退到 Brave 代理缩略图,并保持既有工具结果格式和工作区文件命名不变。
799
+
800
+ Arena 的 `fetch_page` 和 Code Arena 的 `web_fetch` 默认仍使用本地抓取与正文
801
+ 解析。设置 `CODE_HARNESS_ROUTIFY_WEB_FETCH_ENABLED=true` 后,两者改走 Routify
802
+ 页面读取接口,并在首选读取服务不可用时使用备用服务;两者都失败则返回普通工具
803
+ 错误。`fetch_page` 仍按 8000 字符分块并缓存同一 URL 的后续 chunk。认证复用
804
+ `ROUTIFY_API_KEY`。
805
+
806
+ `generate_image`、`generate_speech` 使用 ModelRouter/Routify,并从工作区
807
+ `.env` 读取同一个 `ROUTIFY_API_KEY`。图片使用 Google 兼容协议调用;可用
808
+ `ROUTIFY_VERTEX_BASE_URL` 覆盖默认根地址。语音使用 ModelRouter 的火山引擎
809
+ `/protocol/volcengine/api/v1/tts` 路由,不需要新增服务密钥。
648
810
 
649
811
  `fetch_page` 使用 Defuddle 将 HTML 正文转换为 Markdown,并使用
650
812
  `pdf-parse` 提取 PDF 的前 30 页。响应按 Markdown 边界稳定分块,同一轮内
@@ -659,11 +821,19 @@ Draft-07 schema。repo mode 的动态提示词模板保存在 `src/prompts.ts`
659
821
  内容继续发送给模型。所有工具结果另有 1,000,000 字符的最终兜底。当前不做
660
822
  OCR,扫描版 PDF 只会返回无可提取文字的提示。
661
823
 
662
- 可用 `ARENA_WEB_SEARCH_MODEL`、`ARENA_IMAGE_SEARCH_MODEL`、
663
- `ARENA_IMAGE_MODEL`、`ARENA_SPEECH_MODEL` 覆盖默认模型。图片生成结果和
824
+ 图片和语音的默认 ModelRouter 路由分别为 `gemini-2.5-flash-image` 和
825
+ `doubao-tts`,可用 `ARENA_IMAGE_MODEL`、`ARENA_SPEECH_MODEL` 覆盖。图片生成结果和
664
826
  搜索到的图片会立即下载到工作区;每次图片搜索的结果按生产命名保存为
665
827
  `image-search/<query slug>-<序号>.<扩展名>`,并作为可预览文件显示在 Web UI。
666
- 语音生成目前按 DashScope HTTP API 支持 `.mp3`、`.wav` 和 `.opus` 输出。
828
+ 语音生成先由 `add_voice` 生成两个试听候选并让用户选择,选择结果注册为稳定的
829
+ `voice_id`;后续 `generate_speech` 必须传入该 id。
830
+ 无交互 handler 的 headless 模式仍会开放 `add_voice`,但不会生成无法播放的试听;
831
+ 它会按语言和 `voice_identity` 确定性注册一个默认音色,并在结果中以
832
+ `virtual: true` 标记。返回的 `voice_id` 可直接用于后续 `generate_speech`。
833
+ 也可用
834
+ `ARENA_SPEECH_VOICE` 强制覆盖底层音色。豆包响应中的 Base64 数据已经是 MP3、RIFF/WAV
835
+ 或 Ogg Opus;`.ogg` 与 `.opus` 都保存 Ogg Opus 数据,所有支持的输出均不依赖
836
+ 本地 `ffmpeg` 或 `lame`。
667
837
 
668
838
  所有路径工具都限制在 `--workspace` 内并检查符号链接逃逸。`bash` 是本机
669
839
  子进程,不是操作系统安全沙箱;多租户或不可信模型部署应使用容器或 VM。
@@ -679,4 +849,7 @@ npm test
679
849
  ```
680
850
 
681
851
  测试覆盖消息契约、工具校验、headless、`/query`、实时双通道、
682
- `ask_user`、`present_file` 和 daemon 优雅退出。
852
+ `ask_user`、`add_voice`、后台进程工具、`present_file` 和 daemon 优雅退出。
853
+ 配置 `.env` 中的 `ROUTIFY_API_KEY` 后,可运行
854
+ `npm run verify:live-speech-voices` 对 headless `add_voice` 到真实
855
+ `generate_speech` 的多音色链路做烟测。
package/dist/agent.d.ts CHANGED
@@ -2,16 +2,17 @@ import { type ArenaConfig } from "./config.js";
2
2
  import { EventSink } from "./protocol.js";
3
3
  import { type HarnessMode } from "./mode.js";
4
4
  import { ToolExecutor } from "./tools.js";
5
- import type { AgentResult, AskUserHandler, ClientContext, Message, ModelClient, RepoContext, UserTurnMessage, WorkspaceContext } from "./types.js";
5
+ import type { AgentResult, AskUserHandler, ClientContext, Message, ModelClient, RepoContext, ToolDefinition, UserTurnMessage, WorkspaceContext } from "./types.js";
6
6
  interface AgentOptions {
7
7
  client?: ModelClient;
8
8
  tools?: ToolExecutor;
9
9
  askUserHandler?: AskUserHandler;
10
10
  sessionId?: string;
11
+ sessionAffinityId?: string;
11
12
  signal?: AbortSignal;
12
13
  clientContext?: ClientContext;
13
14
  workspaceContext?: WorkspaceContext;
14
- onCheckpoint?: (messages: readonly Message[]) => Promise<void>;
15
+ onCheckpoint?: (messages: readonly Message[], tools: readonly ToolDefinition[]) => Promise<void>;
15
16
  onModelFailure?: (messages: readonly Message[], error: unknown) => void;
16
17
  }
17
18
  interface AgentRunOptions {
@@ -27,6 +28,7 @@ export declare class Agent {
27
28
  private readonly client;
28
29
  private readonly tools;
29
30
  private readonly workspaceContext?;
31
+ private readonly sessionAffinityId;
30
32
  readonly sessionId: string;
31
33
  constructor(config: ArenaConfig, sink: EventSink, options?: AgentOptions);
32
34
  run(prompt: string, history?: Message[], runOptions?: AgentRunOptions): Promise<AgentResult>;