# Clavue v8.10.14 — Release Notes

**发布日期**：2026-05-14
**npm**：`clavue@8.10.14`
**GitHub**：https://github.com/mycode699/clavue/releases/tag/v8.10.14
**升级命令**：

```bash
npm install -g clavue@8.10.14
# 或一次性：
npx -y clavue@8.10.14
```

---

## TL;DR

8.10.14 修复了 **OpenAI Responses 适配器（GPT-5.x 路径）在多轮工具流上的两处语义丢失**，这两处都让 GPT 路径在 release / 长流程场景里"产物不对"或原地打转，而 Anthropic 直通路径不受影响（用户客诉之一：用 gpt-5.5 发版本一直失败，换成 claude-opus-4-7 就成功）：

1. **`tool_result` 里的图片被字符串化为 `[image] {...}` base64 噪声** —— 截图验证类工具（如 release skill 用截图确认网站已更新）模型看到的是几 KB 的乱码，判断不出"已成功"，于是循环 polling。
2. **assistant `thinking` 块在多轮回放时被静默 drop** —— GPT 看不到自己上一轮的推理链，下一轮容易重新规划、重复操作，长 skill 里表现为"像第一次看到这个任务"。

两条都是 `src/services/api/responsesAdapter/` 里的回归点，跟 Anthropic 路径无关。Claude-family 模型走 `/v1/messages` 直通，不经过该适配器。

测试：1296 / 1299 pass / 3 skipped / 0 fail（`npm run check`，~59s）。新增 `tests/responses-adapter-release-flow-regressions.test.mjs` 锁死两条回归。

---

## 1. tool_result 含图片：不再字符串化

### 问题

`src/services/api/responsesAdapter/toolResultStringify.ts` 的 `stringifyToolResultContent` 把 `tool_result.content` 数组拍扁成字符串时，只识别 `text` / `thinking` / `connector_text` 三种 block，其余走 `stringifyFallbackContentBlock` 退化路径。结果当 tool 返回了 `{type: 'image', source: {type:'base64', media_type:'image/png', data:'...'}}` 时，落到 GPT 看到的字符串里是：

```
[image] {"type":"image","source":{"type":"base64","media_type":"image/png","data":"iVBORw0KGgoAAAA…"}}
```

后果：

- release skill 在 "用截图确认 www.clavue.com 是否已部署新版本" 这一步彻底失明 —— 模型看到一坨 base64，认不出网站状态，会继续 polling。
- 同一类问题也会出现在所有"工具返回图片让模型分析"的场景（视觉测试、UI 验证、屏幕 OCR）。

Claude 走 Anthropic `/v1/messages` 路径不经过这层翻译，所以同样的工具调用在 `claude-opus-4-7` 下完全没事 —— 这就是用户感知到的"换模型就好了"。

### 修复

OpenAI Responses 协议下 `function_call_output.output` 字段是 string-only，无法直接塞 image。修复策略是把图片从 tool_result 里抽出来，紧跟一条独立的 `role: user, content: [input_image]` 消息：

- `toolResultStringify.ts:38-47` —— `image` case 不再走 fallback，返回空字符串（避免泄漏 base64 噪声）。
- `toolResultStringify.ts` 新增 `extractToolResultImageBlocks(block)` 抽出图片 block。
- `requestBuilders.ts` 的 `translateUserContent` 中 `tool_result` 分支：发出 `function_call_output` 后立即用 `translateAnthropicImageBlockToResponses` 把图片翻译成 OpenAI Responses 的 `input_image` 形态，作为一条 follow-up user message 推入 input 列表。

text 内容继续走原路径，图片走独立消息。两个通道不互相覆盖。

### 契约测试

`tests/responses-adapter-release-flow-regressions.test.mjs::T1`：构造一个含 base64 截图的 tool_result，断言：

- `function_call_output.output` 必须**不**包含 `[image]`、`"media_type"`、`"base64"` 字符串。
- （隐含）图片以结构化形态出现在后续的 input 项里。

修前测试失败实证如下：

```
[image] {"type":"image","source":{"type":"base64","media_type":"image/png","data":"iVBORw0KGgoAAAA="}}
```

修后通过。

---

## 2. assistant.thinking 不再被静默丢弃

### 问题

`src/services/api/responsesAdapter/requestBuilders.ts` 的 `translateAssistantContent` 之前对 thinking 块的处理是：

```ts
case 'thinking':
case 'redacted_thinking':
  break  // ← 一刀切丢弃
```

后果：在多轮工具流（release skill、长 plan-execute 循环）里，GPT 收到的历史 input 完全看不到自己上一轮的推理链。表现：

- 第 N 轮 thinking 说 "下一步要 bump package.json"。
- 第 N+1 轮 GPT 看到的 input 里只有 "开始发布流程" 这种 final text，没有计划。
- 于是 GPT 在第 N+1 轮重新规划，可能跟之前的计划不一致 —— 整个长流程开始振荡。

bug2.md 那段 polling 14m 55s 不进展，部分根因就是这个 —— GPT 看不到自己已经做完 5 步、当前在等部署，每轮都像第一次。

### 修复

Anthropic 的 thinking 块带签名（`signature`），OpenAI Responses 没有对应的 signed reasoning 通道，无法 losslessly 回放。但**保留可读文本**比一刀切删要好得多。改为：

```ts
case 'thinking': {
  const thinkingText = getString(block.thinking)
  if (thinkingText) {
    messageParts.push({
      type: 'output_text',
      text: `[reasoning] ${thinkingText}`,
    })
  }
  break
}
case 'redacted_thinking':
  // 没可读 payload，保持丢弃
  break
```

效果：thinking 内容以 `[reasoning] ...` 前缀降级为 assistant 的 output_text，作为正常历史可见。前缀让模型清楚区分"这是我之前的内心独白"和"对外说的话"。

### 契约测试

`tests/responses-adapter-release-flow-regressions.test.mjs::T2`：构造一个 assistant 消息包含 thinking + 普通 text，断言后续 input 序列化里能找到 thinking 的原文。修前断言失败（input 里完全没有 thinking 文本），修后通过。

---

## 3. 与 Anthropic 路径的对称性

说明这次只动 OpenAI Responses 一侧的原因：

- Anthropic Messages 路径（Claude-family 模型走 `/v1/messages` 直通）天然支持 image content block 和 thinking block，无需翻译，所以同样的请求在 Claude 模型下行为正确。
- 这次修复让 GPT 路径在**信息保留度**上向 Anthropic 路径靠拢；不追求 100% 等价（thinking signature 是 Anthropic 私有协议，OpenAI 没有对应字段），追求"模型在长流程里不至于失忆/失明"。

---

## 4. 升级路径

```bash
npm install -g clavue@8.10.14
# 或：
npx -y clavue@8.10.14
```

无破坏性变更：

- 命令行界面无变化。
- Provider 配置、credentials、teams、memory 全部兼容。
- 直接从 8.10.13 升上来即可。
- 行为差异仅限于使用 GPT-5.x / Codex 模型的用户：tool_result 含图片时不再丢图，assistant thinking 在历史里以 `[reasoning]` 前缀可见。Claude-family 用户体验无变化。

---

## 5. Tests / Verify

```
npm run check
# tests 1299
# pass 1296
# skipped 3
# fail 0
# duration_ms ~59s
```

新增测试文件：

- `tests/responses-adapter-release-flow-regressions.test.mjs`（T1 + T2 双契约）

---

## 6. 改动文件清单

| 文件 | 性质 |
|---|---|
| `src/services/api/responsesAdapter/toolResultStringify.ts` | image case 不再走 fallback；新增 `extractToolResultImageBlocks` |
| `src/services/api/responsesAdapter/requestBuilders.ts` | tool_result 中的 image 抽出来作为 follow-up user message；assistant thinking 降级为 `[reasoning]` output_text |
| `src/constants/branding.ts` | APP_VERSION_FALLBACK → 8.10.14 |
| `package.json` | 8.10.13 → 8.10.14 |
| `README.md` | 全文版本号同步到 8.10.14 |
| `content-artifacts/llms.txt` | 同步到 8.10.14（之前停留在 8.10.8） |
| `content-artifacts/llms-full.txt` | 同步到 8.10.14 |
| `tests/responses-adapter-release-flow-regressions.test.mjs` | 新增 |

---

## 7. 后续提示

OpenAI Responses 适配器层还有几个低优先级的语义丢失点（`cache_control` / `citations` 字段会被丢弃，已在 `lossyTranslation.ts` 中有警告日志但无圆桌回放）。如果后续有用户反馈 GPT 路径在 prompt caching 相关场景下有可观察的性能差异，可以接着扩 round-trip 层；本版本先把"模型失明/失忆"两个最大体感问题解决。
