# Changelog

All notable changes to this project will be documented in this file.

Format follows [Keep a Changelog](https://keepachangelog.com/).

---

## [2.0.1] - 2026-03-27

### Added

- **Web 用户输入注入**：WebSocket 新增 `user_input` 消息类型，支持在 Agent 运行过程中向其发送消息。前端输入框在 streaming 时不再禁用，用户可随时追加指令

---

## [2.0.0] - 2026-03-26

### Added

- **CLI 多模态支持**：`cody run --image screenshot.png "fix this bug"` 支持附带图片，可重复使用
- **CLI 熔断控制**：`--max-tokens`、`--max-cost`、`--max-steps` 参数控制单次运行的资源上限（`run`、`chat`、`tui` 均支持）
- **CLI 工具过滤**：`--include-tools grep,read_file` 限制可用工具集，`--exclude-tools exec_command` 排除指定工具
- **TUI 多模态支持**：`/image <path> <message>` 命令发送图片给 AI
- **TUI 技能管理**：`/skills` 列表、`/skills enable X`、`/skills disable X`
- **TUI 设置面板**：`/settings` 查看、`/settings model X` 切换模型、`/settings thinking on|off` 切换思考模式
- **TUI Token 统计**：状态栏实时显示累计 token 用量
- **Web Metrics 端点**：`GET /metrics` 返回 total_runs、total_tokens、total_cost_usd、uptime 等运行时指标
- **Web 熔断配置**：`PUT /config` 支持 `cb_max_tokens`、`cb_max_cost_usd`、`cb_max_steps` 字段；`RunRequest` 支持 per-request 熔断覆盖
- **Web 工具过滤**：`RunRequest` 和 chat WebSocket 均支持 `include_tools` / `exclude_tools` 参数
- **自定义工具注册 API**：支持通过 SDK Builder `.tool(func)` 注册自定义 async 工具函数。自定义工具与内置工具一同注册到 Agent，享有相同的错误重试（`ModelRetry`）和输出截断机制。Core 层 `register_tools()` 新增 `custom_tools` 参数，`AgentRunner` 构造函数新增 `custom_tools` 参数
- **System Prompt 自定义**：SDK Builder 新增 `.system_prompt(text)` 替换默认 persona，`.extra_system_prompt(text)` 在所有内置 prompt 后追加自定义指令。两者可组合使用。CODY.md、项目记忆、Skills 等内置 prompt 部分不受 `system_prompt` 替换影响
- **Per-run 工具选择**：`run()` / `stream()` 新增 `include_tools` 和 `exclude_tools` 参数，支持每次运行指定可用工具集。`include_tools=["read_file", "grep"]` 只启用指定工具，`exclude_tools=["exec_command"]` 排除指定工具。同时新增 `list_tool_names()` 辅助函数用于工具名称发现
- **非流式 `run()` 可取消**：`run()` 新增 `cancel_event: asyncio.Event` 参数。设置后，`run()` 与 cancel event 竞速，被取消时返回 `output="(cancelled)"` 的结果。从 core `AgentRunner.run()` 到 SDK `AsyncCodyClient.run()` 全链路支持
- **Step hook / 中间件**：SDK Builder 新增 `.before_tool(hook)` 和 `.after_tool(hook)` 方法，支持在工具调用前后注入自定义逻辑。`before_tool` hook 接收 `(tool_name, args_dict)` 可修改参数或返回 `None` 拒绝调用；`after_tool` hook 接收 `(tool_name, args_dict, result)` 可转换结果。多个 hook 按注册顺序链式执行。Hook 通过 `CodyDeps` 注入，在 `_with_model_retry` 包装器中统一调用
- **存储层抽象**：新增 `core/storage.py` 定义 `SessionStoreProtocol`、`AuditLoggerProtocol`、`FileHistoryProtocol` 三个 Protocol 接口。SDK Builder 新增 `.session_store(store)`、`.audit_logger(logger)`、`.file_history(history)` 方法，允许注入自定义存储实现（PostgreSQL、DynamoDB 等）替换默认 SQLite。不传时自动使用默认 SQLite 实现，完全向后兼容。Protocol 使用 `runtime_checkable` 支持 `isinstance()` 检查
- **子 Agent 可恢复**：`SubAgentManager` 新增 `resume(agent_id)` 方法，支持恢复已完成/失败/超时的子 Agent。恢复时自动构建包含原始任务和前次输出/错误的上下文 prompt，让新 Agent 从上次中断处继续。新增 `resume_agent` 工具函数，注册到 `SUB_AGENT_TOOLS`。`SubAgentResult` 新增 `task` 和 `agent_type` 字段用于存储恢复上下文
- **StreamChunk 类型重构**：`StreamChunk` 从 flat 单类重构为继承体系。新增 12 个类型化子类（`TextDeltaChunk`、`ToolCallChunk`、`DoneChunk` 等），每个子类预设 `type` 字段。消费者可用 `isinstance(chunk, TextDeltaChunk)` 进行类型安全的模式匹配。原有 `chunk.type == "text_delta"` 用法和直接构造 `StreamChunk(type=...)` 完全向后兼容
- **`max_steps` 熔断**：`CircuitBreakerConfig` 新增 `max_steps` 字段（默认 0 = 无限制），限制单次 run 的工具调用步数。超限时触发 `CircuitBreakerError("step_limit")`。SDK Builder 的 `.circuit_breaker()` 同步支持 `max_steps` 参数
- **Small Model 配置**：`Config` 新增 `small_model` / `small_model_base_url` / `small_model_api_key` 字段，用于低成本操作（compaction、摘要等）。不配置时自动 fallback 到主模型。Compaction 的模型 fallback 链：`compaction.model → small_model → model`。支持环境变量 `CODY_SMALL_MODEL` / `CODY_SMALL_MODEL_BASE_URL` / `CODY_SMALL_MODEL_API_KEY`
- **`read_file` 分段读取**：`read_file` 工具新增 `offset`（起始行号）和 `limit`（最大行数）参数，支持分段读取大文件和被截断的工具输出
- **工具输出截断**：所有工具输出在返回 LLM 前自动截断（默认 120K 字符 ≈ 30K tokens）。超长输出保存到临时文件，模型可通过 `read_file()` 按需读取。防止单个工具输出撑爆上下文窗口。配置项：`truncation.enabled`（默认开启）、`truncation.max_output_chars`（120000）
- **LLM API 重试**：`run()` 和 `run_sync()` 自动对 429（rate limit）和 5xx（服务端错误）使用指数退避重试（默认 3 次，2s → 4s → 8s）。对 context overflow、auth 错误等不重试。配置项：`retry.enabled`（默认开启）、`retry.max_retries`（3）、`retry.base_delay`（2.0s）、`retry.max_delay`（30s）
- **Selective Pruning（选择性修剪）**：在全量 compaction 前增加轻量级修剪阶段（灵感来自 OpenCode）。旧的大型工具输出被替换为 `[output pruned at <ts>]` 标记，保留对话结构，无需 LLM 调用。配置项：`compaction.enable_pruning`（默认开启）、`prune_protect_tokens`（保护窗口 40k）、`prune_min_saving_tokens`（最低节省阈值 20k）、`prune_min_content_tokens`（单条最小阈值 200）
- **PruneEvent 流式事件**：`StreamEvent` 新增 `prune` 事件类型，通知消费者（CLI/TUI/Web）修剪发生的详情
- **两阶段上下文管理**：`_compact_history_if_needed` 和 `_compact_history_sync` 现在先尝试修剪，仅在修剪不够时才执行全量压缩
- **结构化摘要模板**：LLM 压缩的 prompt 从通用模板升级为结构化模板（Goal / Instructions / Discoveries / Accomplished / Relevant Files / Key Decisions），减少信息丢失，保留精确的名称、路径、错误信息
- **Token-based 消息保留**：新增 `compaction.keep_recent_tokens` 配置，按 token 预算保留最近消息（代替固定条数 `keep_recent`），对长短消息混合的对话更精确
- **百分比触发阈值**：新增 `compaction.trigger_ratio` + `context_window_tokens` 配置，支持按模型上下文窗口百分比触发压缩（如 75%），无需硬编码 token 数

---

## [1.11.0] - 2026-03-20

### Fixed

- **session_id 始终返回**：`stream()` 不再区分有无 `session_id` 参数，始终走 `run_stream_with_session` 路径。首次调用不传 `session_id` 时 core 自动创建 session 并返回 sid，修复了调用方拿不到 session_id 的 bug
- **interaction_request 流式死锁修复**：`run_stream()` 中 `interaction_request` 事件原先在 `CallToolsNode` 完成后才推送，导致 `question` 工具阻塞等待响应形成死锁。修复为使用合并队列（merged queue）+ 并发任务，在工具执行期间实时推送交互事件

### Added

- **SessionStartEvent**：新增 `session_start` 流式事件，作为 stream 的第一个事件 yield，确保调用方在 AI 调用前就能拿到 `session_id`（即使后续 AI 报错也不影响）
- **CLI/TUI/Web 完整 Human-in-the-Loop 支持**：
  - **CLI**：`interaction_request` 事件自动暂停并在终端提示用户输入，支持选项选择和自由文本回答
  - **TUI**：交互请求在对话气泡中显示黄色高亮问题，用户在输入框直接回答
  - **Web**：黄色交互卡片 UI 显示 AI 提问，支持选项按钮快捷回答或文本框输入
  - **WebSocket**：所有 WS 端点（`/ws`、`/ws/chat/{id}`、`/ws/chat/task/{id}`）新增 `submit_interaction` 消息类型
- **CircuitBreaker 止损熔断**：新增 `CircuitBreakerConfig` 配置块，支持 token 上限（`max_tokens`）、成本上限（`max_cost_usd`）、死循环检测（`loop_detect_turns` + `loop_similarity_threshold`）。超限时 `run()` 抛出 `CircuitBreakerError`，`run_stream()` 产出 `CircuitBreakerEvent` 后停止
- **CircuitBreaker Builder 支持**：`CodyBuilder` 新增 `.circuit_breaker()` 方法，支持关键字参数或 `CircuitBreakerConfig` 对象。配置优先级：SDK 代码 > 项目配置 > 全局配置
- **Human-in-the-Loop 交互**：`CodyBuilder` 新增 `.interaction(enabled, timeout)` 方法。开启后 `question` 工具和 CONFIRM 级别工具（`exec_command`、`write_file` 等）均暂停等待人类响应，超时抛出 `InteractionTimeoutError` 终止 run。`run_sync()` 忽略此配置，始终自动批准
- **StructuredOutput 结构化输出**：`CodyResult` 新增 `metadata: TaskMetadata` 字段，包含 `summary`（一句话摘要）、`confidence`（AI 自评置信度，解析 `<confidence>` 标记）、`issues`、`next_steps`。不破坏现有 `result.output` 接口
- **HumanInteraction 统一交互层**：新建 `core/interaction.py`，定义 `InteractionRequest`（kind=question/confirm/feedback）和 `InteractionResponse`（action=approve/reject/revise/answer），通过 `request_id` 匹配。`AgentRunner` 新增 `submit_interaction()` 方法，`StreamEvent` 新增 `InteractionRequestEvent`
- **ProjectMemory 跨任务记忆**：新建 `core/memory.py`，`ProjectMemoryStore` 按 workdir 哈希存储跨会话记忆（conventions/patterns/issues/decisions 四类 JSON 文件），AgentRunner 初始化时自动加载并注入 system prompt

---

## [1.10.3] - 2026-03-17

### Added

- **流式取消支持**：`run_stream()` / `run_stream_with_session()` 新增 `cancel_event` 参数（`asyncio.Event`），SDK `stream()` 同步支持。取消时 yield `CancelledEvent`，session 中保存 `"(cancelled)"` 占位消息保持一致性
- **CancelledEvent 事件类型**：新增 `CancelledEvent`，统一 core/SDK/TUI/Web 的取消事件通知

### Fixed

- **read_file 目录崩溃**：`read_file` 传入目录路径时不再抛 `IsADirectoryError` 崩溃 session，改为返回 `ToolInvalidParams` 让 AI 自行修正

---

## [1.10.2] - 2026-03-13

### Added

- **自定义 Skill 目录**：`SkillConfig` 新增 `custom_dirs` 字段，支持通过配置文件、环境变量 `CODY_SKILL_DIRS`、SDK Builder `.skill_dir()` / `.skill_dirs()` 添加额外的 Skill 搜索目录，优先级高于项目级

---

## [1.10.1] - 2026-03-13

### Fixed

- **exec_command 边界检查**：`strict_read_boundary` 开启时，`exec_command` 现在会检测命令中的绝对路径并拦截越界访问
- **SDK tool() 配置丢失**：`client.tool()` 直接调用工具时从磁盘重新加载 Config，导致 SDK Builder 设置的 `strict_read_boundary` 等配置丢失
- **SDK _get_config() 安全配置同步**：SDK 的 security 配置（`strict_read_boundary`、`allowed_roots`、`blocked_commands`）现在正确同步到 core Config
- **Web Backend create_full_deps()**：补传 `strict_read_boundary` 到 `CodyDeps`

---

## [1.10.0] - 2026-03-13

### Added

- **严格读边界**：新增 `security.strict_read_boundary` 配置项，开启后读操作（`read_file`、`grep`、`glob`、`list_directory`、`search_files`）也受 `workdir` + `allowed_roots` 边界限制
- **SDK Builder**：`CodyBuilder` 新增 `.strict_read_boundary()` 方法，SDK `config()` 函数新增 `strict_read_boundary` 参数

### Improved

- **工具错误信息**：优化路径越界提示，AI 收到的拒绝信息现在包含可访问目录列表，便于模型自动调整路径重试

---

## [1.9.2] - 2026-03-13

### Added

- **PyInstaller 打包流水线**：新增 `scripts/build-binary.sh` 和 `scripts/cody_web_entry.py`，支持将 cody-web 打包为独立可执行文件
- **GitHub Actions 自动发布**：新增 `release-binary.yml`，tag push 时自动构建 Linux/macOS/Windows 多平台二进制包并发布到 GitHub Releases

### Fixed

- **测试环境隔离**：`test_config.py` 新增 autouse fixture 清理 `CODY_*` 环境变量，避免宿主环境干扰测试

---

## [1.9.1] - 2026-03-12

### Fixed

- **Python 3.14 兼容性**：`glob` 工具新增绝对路径 pattern 防御性校验，避免 Python 3.14 `pathlib.glob()` 拒绝非相对模式导致报错
- 增强 `glob` 工具 docstring，明确 pattern 必须为相对路径，减少 AI agent 误用

---

## [1.9.0] - 2026-03-11

### Added

- **MCP HTTP 传输**：`MCPClient` 新增 HTTP transport 支持，可连接远程 MCP 服务器（如飞书、Lark 等），与原有 stdio 传输并存
- **SDK MCP Builder API**：`CodyBuilder` 新增 `.mcp_stdio_server()` 和 `.mcp_http_server()` 便捷方法，链式配置 MCP 服务器
- **SDK MCP 自动启动**：新增 `auto_start_mcp` 参数（默认 `False`），设为 `True` 时首次 `run()` / `stream()` 自动启动 MCP 服务器
- **SDK 动态添加 MCP 服务器**：新增 `add_mcp_server()` 方法，运行时动态添加并立即启动 MCP 服务器，无需重建客户端
- **SDK MCP 直接调用**：新增 `mcp_list_tools()` 和 `mcp_call()` 方法，SDK 用户可直接列出和调用 MCP 工具
- **动态 MCP 系统提示**：Agent 运行时通过 `@agent.system_prompt` 动态注入 MCP 工具描述，AI 自动感知可用的 MCP 工具
- **SDK 配置新增 `MCPServerConfig`**：SDK 层独立的 MCP 服务器配置数据类，支持 `to_dict()` 序列化
- 7 个新 HTTP transport 测试（`test_mcp.py` 共 21 个测试）

### Fixed

- 修复 SDK MCP 配置丢失问题：`_get_config()` 未将 SDK 配置的 MCP 服务器传递给 core Config

---

## [1.8.4] - 2026-03-10

### Fixed

- 修复 `prepare_session()` 中 mypy 类型错误：compaction checkpoint 分支的 `history` 变量类型注解与 fallback 赋值 `None` 不兼容

### Changed

- CI workflow 扩大触发范围：`feature/**` 分支 push 也会自动运行 lint + mypy + tests

---

## [1.8.3] - 2026-03-10

### Added

- `cody run` 支持 `--session <id>` 和 `--continue` 参数，可续接历史会话执行单次任务
- `cody run` 每次执行自动创建 session，完成后打印 session ID，方便后续续接

### Fixed

- 修复 Web 测试用例 TC-WEB-003/004 断言不兼容 API 返回的 dict 包装格式（`{sessions:[...]}` / `{skills:[...]}`）
- 修复 Web 测试用例 TC-WEB-006 URL 路径缺少 `/api` 前缀（`/directories` → `/api/directories`）
- 修复 Web 测试用例 TC-WCHAT-001/002 URL 路径缺少 `/api` 前缀（`/projects` → `/api/projects`）

---

## [1.8.2] - 2026-03-09

### Fixed

- 修复 `test_config_load_requires_workdir_when_no_path` 测试用例，适配 `Config.load()` 默认 workdir 为 cwd 的行为变更
- 移除 `tests/test_config.py` 中未使用的 `pytest` 导入，修复 ruff F401 告警

---

## [1.8.0] - 2026-03-08

### Changed
- **CLI/TUI 完全回归 SDK 流式 API** — CLI 和 TUI 不再直接使用 `runner.run_stream()`，改为通过 `client.stream()` 消费 `StreamChunk`。CLI 不再维护 `message_history` 变量，SDK 通过 `session_id` 自动管理会话消息
- **Web Backend DI 清理** — 删除 6 处 `session_store` fallback 死代码（projects/tasks/chat/task_chat），提取 `resolve_chat_runner()` 共享函数消除 chat/task_chat 50 行重复逻辑
- **SDK 类属性别名移除** — 删除 `_get_runner = get_runner` / `_get_session_store = get_session_store` 不安全别名，内部统一使用 `self.get_runner()` / `self.get_session_store()`

### Added
- **LLM 上下文压缩** (`core/context.py`) — 新增 `compact_messages_llm()` 异步函数，用轻量 pydantic-ai Agent 生成语义摘要替代硬截断。支持增量合并（多轮压缩时融合旧摘要）、独立模型配置、失败自动 fallback 到原有截断逻辑。通过 `CompactionConfig` 配置，默认关闭
- **CompactionConfig** (`core/config.py`) — 新增压缩配置模型：`use_llm`、`model`、`model_base_url`、`max_tokens`、`keep_recent`、`max_summary_tokens`。支持 `CODY_COMPACTION_USE_LLM` / `CODY_COMPACTION_MODEL` 环境变量
- **CompactEvent.used_llm** — 压缩事件新增 `used_llm` 布尔字段，标识本次压缩使用了 LLM 还是截断
- **统一日志系统** (`core/log.py`) — 所有日志写入 `~/.cody/logs/cody.log`，RotatingFileHandler 自动轮转（5 MB / 3 备份），CLI/TUI/Web 统一接入。`cody run -v` 同时输出到 stderr
- **StreamChunk.tool_call_id** — 流式事件新增 `tool_call_id` 字段，`tool_call` 和 `tool_result` 类型均携带工具调用 ID
- **AsyncCodyClient.start_mcp()** — 新增公开方法，用于在 `stream()` 前启动 MCP 服务器

---

## [1.7.4] - 2026-03-07

### Changed
- **工具系统模块化** — `core/tools.py`（1307 行）拆分为 `core/tools/` 包（14 个子模块），按类别组织：file_ops、search、command、skills、agents、mcp、web、lsp、history、todo、user、registry。通过 `__init__.py` re-export 保持完全向后兼容
- **CLI/TUI 走 SDK 层** — CLI 和 TUI 不再直接导入 `core.AgentRunner`/`core.SessionStore`，改为通过 `AsyncCodyClient` SDK 进行会话管理和流式渲染
- **Web Backend 依赖注入** — 路由端点从直接调用全局 getter 改为 FastAPI `Depends()` 注入（`session_store_dep`、`audit_logger_dep`），提升可测试性
- **pydantic-ai 版本下限** — `>=0.0.14` → `>=0.1.0`

### Added
- **mypy 类型检查** — CI 流水线新增 mypy 检查步骤，`pyproject.toml` 新增 `[tool.mypy]` 配置。修复 4 个类型错误
- **SDK 会话管理方法** — `AsyncCodyClient` 新增 `get_latest_session()`、`get_message_count()`、`add_message()`、`update_title()`、`messages_to_history()`
- **StreamChunk 增强** — 新增 compact 事件详情字段和 done 事件 `message_history` 字段

---

## [1.7.3] - 2026-03-05

### Fixed
- **CI Trusted Publisher** — 修正 PyPI trusted publisher workflow 名称配置

---

## [1.7.2] - 2026-03-05

### Fixed
- **CI pytest-timeout** — dev 依赖补充 `pytest-timeout`，修复 CI `--timeout=60` 参数报错
- **CI 前端测试** — 移除不稳定的前端测试 job，保留 Python 测试矩阵

### Changed
- **Python 版本要求** — 最低版本从 3.9 提升至 3.10（支持 `X | None` 联合类型语法）
- **CI 测试矩阵** — 3.10 / 3.11 / 3.12 / 3.13

---

## [1.7.1] - 2026-03-05

### Added
- **SDK `run_stream()` 方法** — `stream()` 的别名，提供更直观的 API 命名
- **SDK `RunResult.thinking`** — 非流式模式下也可获取模型思考内容
- **SDK `StreamChunk` 增强字段** — `tool_name`、`args`（tool_call 事件）、`usage`（done 事件）
- **SDK Builder `.on()` 方法** — 在 Builder 链中直接注册事件处理器，自动启用 events
- **SDK `on()` 字符串支持** — `client.on("tool_call", handler)` 等效于 `client.on(EventType.TOOL_CALL, handler)`
- **SDK 自动 Session** — `run()` 自动创建 session，首次调用即返回 `session_id`，无需手动 `create_session()`
- **SDK 非流式工具事件** — 非流式 `run()` 完成后从 `tool_traces` 补发 `TOOL_CALL`/`TOOL_RESULT` 事件

### Fixed
- **SDK 模型覆盖 Bug** — `AsyncCodyClient()` 不传 model 时，SDK 默认值不再覆盖环境变量 `CODY_MODEL`
- **Web `estimate_tokens` TypeError** — 多模态消息（ImageUrl）不再导致 token 估算崩溃
- **Web AgentRunner 性能** — 按 workdir 缓存 AgentRunner（5 分钟 TTL），避免每次消息重建

### Changed
- **Web Stop 按钮移除** — pydantic-ai 无优雅停止机制，移除前端 Stop 按钮避免误导

---

## [1.7.0] - 2026-03-05

### Added
- **SDK 增强模块** (`cody/sdk/`) — Builder 模式、事件系统、指标收集、增强错误处理
  - `Cody()` Builder — 链式配置，`Cody().workdir("/path").model("...").build()`
  - `EventManager` — 事件钩子系统，支持同步/异步 handler
  - `MetricsCollector` — Token 使用、工具调用、会话级指标收集
  - 10 个细粒度错误类（CodyModelError、CodyToolError、CodyPermissionError 等）
  - 4 个示例文件（basic、streaming、events、tools）
  - 65 个 SDK 测试
- **依赖分层** — `pyproject.toml` 拆分为核心依赖 + 可选依赖组
  - `pip install cody-ai` — 只装核心（pydantic-ai, anthropic, pydantic, httpx）
  - `pip install cody-ai[cli]` — 加 CLI（click, rich）
  - `pip install cody-ai[tui]` — 加 TUI（textual）
  - `pip install cody-ai[web]` — 加 Web（fastapi, uvicorn）
  - `pip install cody-ai[all]` — 全部
- **Web 中间件测试** — 新增 `web/tests/test_middleware.py`（9 个测试覆盖认证、限流、审计）
- **CLI/TUI 补充测试** — CLI 新增 9 个测试（参数传递、流式渲染、会话恢复），TUI 新增 5 个测试（批量刷新、状态栏、命令处理）
- **WebSocket 鉴权** — WebSocket `/ws` 和 `/ws/chat` 端点现需认证 token（S3）
- **CLI/TUI 共享工具库** — 新增 `cody/shared.py`，提取 CLI/TUI 重复工具函数（R1）
- **ToolContext 依赖注入** — 新增 `core/deps.py:ToolContext`，统一工具直接调用上下文（R3）
- **可扩展命令黑名单** — `SecurityConfig.blocked_commands` 允许用户自定义禁用命令模式（S5）
- **Web 路由测试补充** — 新增 21 个 Web 路由测试覆盖 config/agent/audit/skills 端点（R9）
- **目录浏览路径限制** — `/api/directories` 限制在用户 home 目录内（R10）

### Changed

- **CLI/TUI 拆分为 Python 包** — `cody/cli.py`（830 行）拆为 `cody/cli/` 包（main.py + commands/ + rendering.py + utils.py），`cody/tui.py`（566 行）拆为 `cody/tui/` 包（app.py + widgets.py）。所有导入路径向后兼容。
- **SDK 合并** — `cody/sdk/` 成为唯一 SDK 实现，直接包装 core（单层），`cody/client.py` 变为向后兼容 re-export shim
  - 新增 `cody/sdk/types.py` — SDK 响应类型（RunResult、Usage、StreamChunk 等）
  - `cody/sdk/client.py` 重写 — 不再双层包装（sdk → client → core），改为直接包装 core
  - 所有导入路径向后兼容：`from cody import ...`、`from cody.client import ...`、`from cody.sdk import ...`
- **TUI 性能优化** — StreamBubble 改为 30fps 批量渲染 + 滚动节流
  - 工具参数显示截断（超 120 字符自动截断）
  - ToolResultEvent 显示摘要行（工具名 + 结果长度）
  - 长对话消息回收（超 200 条自动移除旧 widget）
- **CLI 工具参数截断** — 与 TUI 一致的 `_truncate_repr` 截断显示
- **Web 前端版本同步** — `web/package.json` 版本号从 1.3.0 → 1.6.0
- **pyproject.toml 描述更新** — 对齐框架定位
- **Web CORS 白名单可配置** — 支持 `CODY_CORS_ORIGINS` 环境变量覆盖（默认仍为 localhost 开发地址）
- **Web 后端异步化 SQLite** — 路由层使用 `asyncio.to_thread()` 包装阻塞式 ProjectStore/SessionStore 调用
- **Web 后端异常处理统一** — `projects.py`、`directories.py` 中的 `HTTPException` 统一改为 `raise_structured()`
- **Web 后端密钥遮蔽加强** — Config 端点 API Key 显示从部分展示改为全量遮蔽 `***`
- **WebSocket 指数退避重连** — 前端 WebSocket 断线重连从固定 2s 改为指数退避（2s → 60s 上限）
- **SDK 指标异常安全** — `MetricsCollector.end_run()` 移至 `finally` 块，异常时不丢失指标
- **前端删除确认** — Sidebar 删除项目前增加 `window.confirm()` 确认
- **Token 估算改进** — CJK 字符按 1.5 token 估算（原为 0.25）
- **MCP 启动失败清理** — `start_all()` 返回失败列表，失败时清理残留进程和 reader task
- **目录浏览安全** — 跳过符号链接，防止目录遍历
- **Config 缓存 TTL** — Web Backend 的 Config 缓存增加 60 秒 TTL，自动刷新（R7）
- **AuditEvent 枚举化** — `AuditEvent` 从普通类改为 `str, Enum`（O4）
- **API Key 占位改进** — 未设置 API Key 时使用空字符串代替 `"not-set"`（O2）
- **文件操作 UTF-8** — `FileHistory.undo()/redo()` 写入时指定 `encoding="utf-8"`（O3）
- **Refresh Token 权限校验** — `AuthManager.refresh()` 增加 scope 验证（R8）
- **client.py 导出清理** — 移除私有符号 `_event_to_chunk`/`_usage_from_result` 的 re-export（O8）

### Fixed
- **子代理循环依赖** — `sub_agent.py` 延迟导入处添加注释说明
- **run_sync 上下文压缩缺失** — `run_sync()` 现在与 `run()` 一致调用 `_compact_history_if_needed()`（S1）
- **HTML 解析器嵌套 skip 标签 bug** — `_HTMLToMarkdown` 将 `_skip` 布尔值改为 `_skip_depth` 计数器（S2）
- **exec_command 白名单绕过** — 管道/链式命令现在逐段检查白名单（S5）
- **CodyBuilder _permissions 默认值** — 改为 `field(default_factory=dict)` 避免共享可变对象（O6）

### Removed
- `python-dotenv` — 从依赖中移除（代码未使用）
- `pylint.yml` — 删除过时的 Pylint CI 工作流，统一使用 ruff（S4）
- **SDK apply_env()** — 移除环境变量自动覆盖配置的方法（R4）

### Refactored

- **CLI Skills 命令** — 使用 `SkillManager` 直接管理 Skills，不再经过 `AgentRunner`（R2）
- **CLI Chat REPL** — 单 event loop 重构，消除重复 `asyncio.run()` 调用（R6）
- **CI 工作流重写** — Python 版本矩阵（3.9-3.13）、PR 触发、Web 测试覆盖（S4）
- **前端 summarizeArgs 去重** — 提取到 `web/src/utils/summarizeArgs.ts`，ChatWindow 和 MessageBubble 共享
- **前端项目状态管理** — 新增 `useProjects` hook，HomePage 和 Sidebar 共享项目列表
- **ChatWindow 拆分** — 提取 `useStreamBuffer` hook 和 `StreamingBubble` 组件，主文件从 510 行缩减到 ~300 行
- **前端响应式布局** — `index.css` 添加 768px 断点移动端适配
- **文件历史持久化** — FileHistory 支持可选 SQLite 持久化（Web 模式启用）

---

## [1.6.0] - 2026-03-03

### Added
- **交互式配置向导** — 新增 `cody config setup` 命令，引导用户选择模型提供商、输入 API Key 等
  - 首次使用 `cody run`/`chat`/`tui` 时如果未配置会自动触发
  - `cody init` 完成后也会提示配置
- **Config.is_ready()** — 配置完整性检查方法，判断是否有足够的 API 凭证
- **Config.missing_fields()** — 返回缺失配置项的描述列表
- **model_api_key 持久化** — API Key 现在保存到配置文件，无需环境变量
- **model_api_key Anthropic 路径** — 配置了 `model_api_key` 但无 `model_base_url` 时，自动使用 Anthropic API
- **config show 脱敏** — `cody config show` 显示 API Key 时自动脱敏（如 `sk-ant...xyz`）
- **config set 扩展** — 支持设置 `enable_thinking`、`thinking_budget`

### Removed
- **claude_oauth_token** — 删除 OAuth 认证路径，统一使用 `model_api_key`
- **CLI --model-base-url / --model-api-key** — 从 `run`/`chat`/`tui` 命令删除，改用 `cody config setup` 配置
- **CLAUDE_OAUTH_TOKEN 环境变量** — 不再读取
- **ANTHROPIC_API_KEY 环境变量** — 不再隐式使用，统一走 `model_api_key`

### Changed
- `Config.save()` 现在保存 `model_api_key` 到配置文件
- `model_resolver.py` 简化为 3 条路径：base_url → api_key(Anthropic) → 默认字符串
- 新增 `cody/core/setup.py` 提供配置向导的数据层

---

## [1.5.0] - 2026-03-03

### Added
- **Web 图片上传** — Web 前端支持多模态输入，用户可以粘贴截图或选择图片文件随消息一起发送
  - 支持 Ctrl+V 粘贴剪贴板图片、点击按钮选择文件（`image/*`）
  - 发送前预览已选图片，支持单独删除
  - 消息气泡中展示历史图片
  - 图片以 base64 存储在 SQLite 中，会话恢复时自动加载
- **多模态 Prompt 类型** — 新增 `cody/core/prompt.py`，定义 `ImageData`、`MultimodalPrompt`、`Prompt` 类型
  - `Prompt = Union[str, MultimodalPrompt]` — 类型安全的多模态提示，向后兼容纯文本
  - `prompt_text()` / `prompt_images()` — 类型安全的提取函数
  - 核心引擎 `AgentRunner` 通过 pydantic-ai `BinaryContent` 将图片传递给支持多模态的模型（如 Qwen3.5-plus）
- **Session 图片持久化** — `Message` 新增 `images` 字段，SQLite 自动迁移 `ALTER TABLE messages ADD COLUMN images`

### Changed
- `AgentRunner.run()` / `run_stream()` / `run_sync()` / `run_with_session()` / `run_stream_with_session()` 签名从 `prompt: str` 扩展为 `prompt: Prompt`（纯 `str` 仍然兼容）
- `CodyClient` / `AsyncCodyClient` SDK 签名同步扩展
- Web Backend `RunRequest` 新增 `images` 可选字段
- Web Backend 路由（chat、run、websocket）统一使用 `build_prompt()` 构造多模态提示
- `messages_to_history()` 支持重建带图片的多模态对话历史

---

## [1.4.0] - 2026-03-02

### Added
- **Streaming status indicators** — All three UIs (CLI, TUI, Web) now show real-time processing status with elapsed time during agent execution
  - **CLI**: "Thinking..." spinner at stream start, tool-specific spinners with elapsed time, "Completed in Xs" summary at end
  - **TUI**: Unified status line indicator cycling through "Thinking..." → "Running {tool}..." → "Generating..." with elapsed time
  - **Web**: Streaming status bar with pulse animation, smart status text, elapsed timer, and **Stop** button to cancel generation
- **Web Stop button** — Users can now manually stop a stuck or unwanted streaming response
- **Web idle timeout** — Streaming auto-stops after 120 seconds of no events, showing a timeout message
- **Web disconnect recovery** — If WebSocket disconnects during streaming, the UI resets gracefully with a "Connection lost" message instead of hanging forever
- **GFM Markdown** — Web frontend now renders GitHub Flavored Markdown (tables, strikethrough, task lists) via `remark-gfm`

### Changed
- **CLI spinner refactored** — `_tool_spinner` replaced with generic `_status_spinner` supporting custom labels and done messages
- **TUI tool spinner unified** — Replaced per-tool `_start_tool_spinner`/`_stop_tool_spinner` with unified `_start_processing`/`_set_processing_state`/`_stop_processing`

### Fixed
- **FileNotFoundError auto-retry** — `FileNotFoundError` now triggers `ModelRetry` (alongside `ToolError`), allowing the AI to self-correct file paths instead of crashing
- **Web streaming stuck** — Fixed bug where WebSocket disconnection during streaming left the UI in permanent "Thinking..." state

---

## [1.3.0] - 2026-03-02

### Added
- **Web frontend** — React + TypeScript + Vite single-page application (`web/`)
  - Project wizard with directory browser, name/description fields
  - Real-time chat via WebSocket with streaming message display
  - Project sidebar with create/delete/navigate
  - Dark theme UI
  - 33 frontend tests (Vitest + Testing Library)
- **Web backend** — Unified FastAPI application (`web/backend/`, port 8000)
  - Serves both Web-specific (projects, chat) and RPC endpoints (run, tool, sessions, skills, agents, ws)
  - Own SQLite database (`~/.cody/web.db`) for project management (CRUD)
  - Imports core directly (no HTTP intermediary)
  - WebSocket `/ws/chat/{project_id}` endpoint for real-time chat relay
  - Directory browsing API (`GET /api/directories`)
  - 20 backend tests (pytest)
  - Dependency injection via FastAPI `Depends()` for clean testing
- **CODY.md project instructions** — Cody now reads `CODY.md` at the start of every session and injects its content into the system prompt, similar to Claude Code's `CLAUDE.md`.
  - Two-layer loading: `~/.cody/CODY.md` (global user-level) + `<workdir>/CODY.md` (project-level); both are optional and additive
  - Global instructions come first, project instructions appended after a `---` separator
  - `cody init` always (re-)generates `CODY.md` via AI analysis — shows "Created" on first run, "Updated" on subsequent runs; if `.cody/` already exists, scaffold is skipped but `CODY.md` is still regenerated; fails loudly on error (no silent fallback)
  - New module `cody/core/project_instructions.py` with `load_project_instructions()`, `generate_project_instructions()`, `CODY_MD_FILENAME`, `CODY_MD_TEMPLATE`
- **`read_file` encoding safety** — tool now reads with `encoding="utf-8", errors="replace"` instead of platform default, preventing `UnicodeDecodeError` on binary or non-UTF-8 files
- **Tool error auto-retry** — `ToolError` exceptions (e.g. `ToolInvalidParams` from `edit_file` when text is not found) are now converted to pydantic-ai `ModelRetry`, allowing the model to self-correct and retry (up to 2 retries per tool call) instead of breaking the entire agent run

### Changed
- **Architecture simplification** — eliminated `cody/server.py` (standalone RPC server)
  - All RPC endpoints merged into `web/backend/` as a unified FastAPI app
  - Web backend imports core directly (no HTTP SDK intermediary)
  - Single server on port 8000 replaces two servers (8000 + 5001)
- **Python SDK refactored** — `cody/client.py` is now an in-process wrapper around core
  - No HTTP, no server required — imports core directly
  - Same API surface (`AsyncCodyClient`, `CodyClient`, `RunResult`, `StreamChunk`, etc.)
  - Removed `CodyConnectionError`, `CodyTimeoutError`, retry logic (no longer needed)
- **Go SDK removed** — simplified project (use Python SDK or HTTP API instead)

### Architecture
- All shells (CLI, TUI, Web) import core directly — "thick engine, thin shells"
- Unified architecture: `React → Web Backend (port 8000, web.db) → Core Engine`
- Python SDK: `CodyClient → core/* (in-process, no HTTP)`

---

## [1.2.0] - 2026-03-01

### Added
- **Multi-workdir support (`allowed_roots`)** — File tools can now access directories beyond the primary `workdir`. Two distinct concepts:
  - `workdir` — execution anchor (config discovery, subprocess cwd, session tag, LSP root)
  - `allowed_roots` — access boundary (directories tools can read/write)
  - Configurable via `security.allowed_roots` in `.cody/config.json`, `--allow-root` CLI flag (run/chat/tui), and `allowed_roots` field in Server requests
  - Additive merging: config + CLI/request roots are combined; `workdir` is always implicitly allowed
  - Config entries must be absolute paths (relative paths raise `ValueError` at startup)
- **Tool execution spinner** — CLI and TUI now show animated progress indicator with elapsed time during tool execution, replacing the previous "stuck" behavior
- **Context compression notification** — New `CompactEvent` in stream events; CLI prints a yellow notification line, TUI shows a system bubble when context auto-compaction occurs
- **TUI slash command hints** — Status line shows matching commands with descriptions as user types `/` in the input field

### Changed
- `_resolve_and_check()` in `tools.py` now accepts `allowed_roots` parameter; error message updated to "outside all permitted directories"
- `AgentRunner.__init__` accepts optional `extra_roots: list[Path]` parameter
- `CodyDeps` gains `allowed_roots: list[Path]` field (defaults to `[]`, backward compatible)
- `SecurityConfig` gains `allowed_roots: list[str]` field (defaults to `[]`)
- `Config.apply_overrides()` gains `extra_roots` parameter (additive semantics)
- `_compact_history_if_needed` now returns `(history, CompactResult)` tuple instead of just history
- `StreamEvent` union type now includes `CompactEvent`

---

## [1.1.1] - 2026-02-28

### Changed
- **Declarative tool registry** — Tools organized into categorized lists (`FILE_TOOLS`, `SEARCH_TOOLS`, etc.) with `register_tools()` / `register_sub_agent_tools()` replacing 48 lines of individual `agent.tool()` calls
- **Typed tool exceptions** — New `ToolError` hierarchy (`ToolPermissionDenied`, `ToolPathDenied`, `ToolInvalidParams`) replacing generic `ValueError`/`PermissionError` with string matching in server error handlers
- **Server caching** — Config cached per-workdir (deep copy on access); SessionStore as global singleton; SkillManager always fresh from disk
- **Complete CodyDeps** — `/tool` endpoint and sub-agents now create full `CodyDeps` with audit_logger, permission_manager, file_history (previously missing)

## [1.1.0] - 2026-02-28

### Added
- **Thinking Mode** — `--thinking` / `--thinking-budget` flags for CLI (run/chat/tui), Server request params, env vars `CODY_ENABLE_THINKING` / `CODY_THINKING_BUDGET`
- **CodyResult** — Rich result model with `output`, `thinking`, `tool_traces`, `usage`; `ToolTrace` records every tool call
- **StreamEvent system** — 5 structured event types (`ThinkingEvent`, `TextDeltaEvent`, `ToolCallEvent`, `ToolResultEvent`, `DoneEvent`)
- `run_stream()` now uses pydantic-ai `run_stream_events()` API, yields structured events instead of raw strings

### Changed
- CLI `run` and `chat` commands switched from sync `run_sync()` to async streaming `run_stream()` with typewriter effect
- TUI consumes `StreamEvent`, uses `DoneEvent.result.all_messages()` for accurate message history
- Server SSE (`/run/stream`) and WebSocket (`/ws`) emit structured JSON events (thinking/tool_call/tool_result/text_delta/done)
- `_serialize_stream_event()` unifies SSE and WebSocket serialization

### Fixed
- TUI message history reconstruction bug — no longer manually rebuilds ModelRequest/ModelResponse pairs

## [1.0.1] - 2026-02-26

### Added
- **Agent Skills open standard** — YAML frontmatter + Markdown format, aligned with [agentskills.io](https://agentskills.io/) (26+ platforms)
- **Progressive disclosure** — Only YAML frontmatter (~50-100 tokens/skill) loaded at startup; full body on demand
- **Aliyun Bailian Coding Plan** — `--coding-plan-key` / `--coding-plan-protocol` for bundled model access (Qwen3.5, GLM-5, Kimi K2.5, etc.)
- **Claude OAuth token** authentication support

### Changed
- All 11 SKILL.md files migrated to YAML frontmatter format
- `SkillManager` rewritten with frontmatter parser (zero external deps), name validation, `to_prompt_xml()` for system prompt injection
- Plain Markdown files without frontmatter no longer loaded (breaking change)

## [1.0.0] - 2026-02-20

### Added
- **RPC Server** — FastAPI HTTP/WebSocket server with RESTful API (`/run`, `/run/stream`, `/tool`, `/skills`, `/sessions`, `/ws`, `/health`)
- **Python SDK** — `CodyClient` (sync) + `AsyncCodyClient` (async) with retry and exponential backoff
- **Go SDK** — Zero-dependency Go client with full API coverage, auto-retry, context cancellation
- **30+ AI Tools** — File I/O, search (grep/glob/patch), shell, sub-agents, MCP, web, LSP, file history, task management
- **11 Built-in Skills** — git, github, docker, npm, python, rust, go, java, web, cicd, testing
- **Sub-Agent System** — 4 types (code/research/test/generic), asyncio concurrency (max 5)
- **MCP Integration** — stdio JSON-RPC to external MCP servers (GitHub, databases, etc.)
- **LSP Intelligence** — Python (pyright), TypeScript (tsserver), Go (gopls) diagnostics, go-to-def, references, hover
- **CI/CD Templates** — GitHub Actions for AI code review, auto-fix, test generation
- **Session Persistence** — SQLite-backed multi-session management
- **Security** — Tool permissions, path traversal protection, dangerous command detection, audit logging, rate limiting, OAuth 2.0
- **Multi-Model** — Anthropic Claude, OpenAI GPT, Google Gemini, DeepSeek, GLM, Qwen, and any OpenAI-compatible API
- **CLI** — `cody run`, `cody chat`, `cody init`, `cody skills`
- **TUI** — Full-screen Textual terminal UI with streaming, session management, slash commands
- **Context Management** — Auto-compact conversations, smart file chunking
