Skip to content

Fix/multi issue cleanup 2 - #437

Open
1982167424-art wants to merge 22 commits into
bytedance:mainfrom
1982167424-art:fix/multi-issue-cleanup-2
Open

Fix/multi issue cleanup 2#437
1982167424-art wants to merge 22 commits into
bytedance:mainfrom
1982167424-art:fix/multi-issue-cleanup-2

Conversation

@1982167424-art

Copy link
Copy Markdown

Description

More Information

Validation

Linked Issues

1982167424-art and others added 22 commits July 24, 2026 11:36
将 Trae agent 分支 trae/agent-wVWm4d 合并到 main,引入 mimo-gateway 子项目。

- 新增 mimo-gateway/app.py: FastAPI OpenAI 兼容网关
- 新增 mimo-gateway/sanitizer.py: PII 脱敏核心
- 新增 mimo-gateway/local_llm.py: 本地 LLM 封装
- 新增 mimo-gateway/mimo_client.py: 远端 MiMo 客户端
- 新增 mimo-gateway/start.sh: 启动脚本
- 新增 mimo-gateway/.env.example: 配置模板
- 新增 mimo-gateway/README.md: 完整说明文档
…vl-a3b) (#2)

* feat: 修改模型为mimo-v2.5-pro

Co-authored-by: traeagent <traeagent@users.noreply.github.com>

* feat: 修改模型为mimo-v2.5-pro

Co-authored-by: traeagent <traeagent@users.noreply.github.com>

* feat: 添加 Kimi-VL-A3B-Instruct 模型配置

- 新增 Ollama Modelfile 用于加载 Kimi-VL-A3B-Instruct (Q4_K_M GGUF + f16 视觉投影器)
- trae_config.yaml.example 增加 kimi_vl_a3b_instruct 条目
- .gitignore 忽略 *.gguf/*.safetensors/*.bin 大文件及下载缓存,保留 Modelfile

* feat: 修改模型为mimo-v2.5-pro

Co-authored-by: traeagent <traeagent@users.noreply.github.com>

---------

Co-authored-by: 1982167424-art <1982167424-art@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
- 新增 doubao provider: 默认使用火山引擎 Doubao-Seed-2.0-mini (云端, 质量最优)
- 新增 start.sh: 解决 httpx 与 Shadowrocket 系统代理冲突, 一键启动交互模式
- 集成 Playwright MCP: 安装到 node_modules, 通过 node 命令直接加载 (避免 npx 远程下载卡住)
- 添加 node_modules 和 package-lock.json 到 .gitignore
问题原因:
1. _run_simple_interactive_loop 和 Agent.run() 各自创建了一个 cli_console.start() 协程,
   两个 console 轮询互相干扰
2. 每轮结束后 agent_execution 还是上一轮的 COMPLETED 状态,
   下一轮 start() 的 while 条件立即为 False,直接跳过等待
3. console_step_history 没清理,step number 冲突

修复:
- 删除 _run_simple_interactive_loop 中重复的 console_task 创建,
  让 agent.run() 内部的 console 协程独占负责显示
- 每轮开始前重置 agent_execution=None 和 console_step_history
模仿 Claude Code / OpenCode 风格扩展 trae-agent:

1. Skills 系统 (trae_agent/skills/loader.py)
- 从 ~/.trae-agent/skills/ 和 ./.trae-agent/skills/ 加载 .md 文件
- YAML frontmatter 支持 name/description/tools
- 自动注入到 agent system prompt 第二条消息
- 4 个内置 skills: debug, commit, refactor, review
- trae-cli skills {list,show,open-dir} 命令

2. Plan/Build 双模式 (trae_agent/agent/trae_agent.py)
- trae-agent.py 新增 TraeAgentPlanToolNames (bash/sequentialthinking/task_done)
- TRAE_AGENT_PLAN_PROMPT 追加只读提示
- TraeAgent.plan_mode 标志控制
- 新增 trae-cli plan '...' 子命令
- Plan 模式强制 max_steps=30 防失控

3. 全局安装脚本 (install.sh)
- curl | bash 一键安装,复用 opencode/openclaw 安装流程
- 检测 Python ≥3.10 + uv
- uv tool install 安装 + 暴露 trae 命令
- --system / --uninstall 选项
- 检测 Ollama 并提示

4. TUI 升级
- trae-cli version 命令(带 banner)
- 启动时渲染 DOUBLE_EDGE banner

5. README 增补
- Fork 特性 section 列出上述能力
修复安装脚本的两个 bug:

1. uv tool install 创建的是 trae-cli(pyproject.toml 的 entry-point 名),
   不是 trae。脚本试图为 trae 创建符号链接,找不到源文件。
   → 改为给 trae-cli 创建 trae 软链接(同时在 --system 模式下给
     INSTALL_BIN 也建一份)。

2. trae-agent 的 base_agent.py 无条件 import docker_manager,
   docker_manager 又 import docker。这些依赖是 evaluation extras 里。
   uv tool install 默认不装 extras,运行时直接 ModuleNotFoundError。
   → 改用 PEP 508 语法 --with 'trae-agent[evaluation]',并写注释
     说明原因。

3. 验证脚本既检查 trae 又检查 trae-cli。
## P0 严重/安全

- P0-1: 所有 from typing import override → from typing_extensions
  (14 个文件)。typing.override 是 3.12+ 才有;改用 typing_extensions
  后兼容 3.10+,符合 pyproject.toml 写的 python>=3.10。
  同时把 legacy_config.py 移到 legacy/ 子目录(对应 import 路径)。

- P0-2 (sanitizer.py): _llm_sanitize_once 失败时不再 return 原 PII 文本。
  改为返回 [SANITIZE_FAILED] 占位符 + raise SanitizeFailedError,
  由调用方选择 abort 或占位符替换。

- P0-3 (sanitizer.py): sanitize_messages 递归处理 tool_calls[*].
  function.arguments 和 tools[*].function.description/parameters。
  PII 可能藏在工具调用的 JSON 参数里。

- P0-4 (app.py + mimo_client.py): _stream_proxy 改成真流式 — 用
  httpx.AsyncClient.stream + aiter_lines,不再 list(gen()) 进内存。

- P0-5 (app.py): 审计 chunk 移到 [DONE] 之后发送。OpenAI SDK 期望首条
  是 chat.completion.chunk,审计 chunk 在首条会破坏协议。

- P0-6 (sanitizer.py): Sanitizer.stats 全局共享 → 改成 sanitize()
  返回 per-request SanitizeStats。FastAPI 并发下 base_regex 会被
  其他请求污染。

- P0-7 (trajectory_recorder.py): 每步 save_trajectory 全量 json.dump
  阻塞 event loop → 改为 thread-pool 异步写,finalize 时同步刷盘。
  同时加 dict 索引加速 update_lakeview(P1-13)。

## 关键 P1

- P1-1 (trae_agent.py): plan_mode 工具初始化条件永远不触发 →
  改为显式 tool_names 优先,plan_mode 强制覆盖,其他保持。

- P1-4 (install.sh): --uninstall 删 uv tool + pipx + 所有 ~/.local/bin
  别名 + ~/.local/share/uv/tools/trae-agent 残留。

- P1-5 (install.sh): --system 缺 sudo 时明确报错并 exit,不再 ⚠
  静默继续。

- P1-6 (trae_agent.py): get_git_diff 不再用 os.chdir 改全局 cwd,
  改用 subprocess.run(..., cwd=self.project_path)。

- P1-15 (base_agent.py): _tool_call_handler 把 reflection 标
  role='assistant' → 改为 role='user',符合语义并避免部分 provider 拒绝。

## 额外安全/可用性

- app.py 加可选 GATEWAY_TOKEN bearer auth 中间件。
- app.py upstream 错误不再回显原始 e,只记日志 + 502 给客户端,
  避免 API key / 请求体通过错误信息泄露(P1-7)。
- mimo_client.py: stream_chat_completion_async 函数(P0-4 用)。
- sanitizer.py: PRIVKEY 正则用锚定 + 非贪婪 + end-anchor,
  避免 catastrophic backtracking(P1-8)。
P1-2 修复:
Plan mode 现在通过 ReadOnlyBashTool 在工具层硬拦截危险命令,
而不是只靠 prompt 软约束。LLM 没办法绕过 — 危险命令直接报
[PLAN-MODE BLOCKED] 错误并 exit 1。

实现:
- ReadOnlyBashTool 继承 BashTool,execute() 入口先 check_command()
- BLOCKED_PATTERNS 覆盖:rm/rmdir/unlink、> />> 文件重定向、
  mv/cp/mkdir/touch/ln/truncate、sed -i/perl -i/tee、chmod/chown、
  pip/npm/brew/apt/uv install、git push/commit/reset/stash drop、
  curl POST/wget POST、shutdown/reboot/kill -9/mkfs/dd of=、
  systemctl/killall、docker rm
- SAFE_PREFIXES 短路纯读操作:ls/cat/head/tail/grep/find/.../git status/...
- TraeAgent.new_task() 在 plan_mode=True 时把 'bash' 工具替换为
  ReadOnlyBashTool

trae-cli resume:
从保存的 trajectory JSON 恢复 LLM 完整对话历史 + step 计数,
从下一个 step 继续执行。修复了 base_agent 不累积 messages 的问题:

实现:
- trae_agent/utils/trajectory_loader.py:load_trajectory() 读
  trajectory,恢复 last llm_interactions 或 last agent_steps 的
  llm_messages,提供 ResumableTrajectory dataclass
- BaseAgent.resume_from_messages() 替换 _initial_messages 并把
  max_steps 加上已完成步数(让 --max-steps N 语义 = 额外 N 步)
- _accumulated_messages 跟踪每步完整消息,record_agent_step 用它
  而不是局部 messages 变量(之前 trajectory 存的是每步 0-1 条消息,
  没法恢复)
- Agent.set_trajectory_file() 让 resume 写到新文件而不是覆盖原文件
- trae-cli resume <file> [--provider X] [--model Y] [--max-steps N]
  [--dry-run] [--trajectory-file-output PATH] 新增 CLI 子命令

端到端验证:
- 跑 Count Python files (4 步, 超过 max_steps)
- trae-cli resume --dry-run 列出 task/completed_steps/history length
- trae-cli resume --max-steps 3 实际从 step 5 继续,新 trajectory
  含 step 5/6/7 各 4/5/6 messages

P1-11 顺手修:
- cli.py:_setup_telemetry(config) 是死代码(_setup_telemetry 函数
  不存在,config.telemetry 也不存在),resume 命令触发,删除该调用
- anthropic_client: input 改为 dict(json.dumps 触发 API 报错);多 content block 合并为单条 assistant message;max_tokens None 兜底 4096;top_k=0 传 NOT_GIVEN
- openai_client: top_p=None 传 NOT_GIVEN;处理 refusal block(不再静默吞掉)
- edit_tool: _view 目录列举改为纯 Python(os.walk)杜绝 shell 注入;validate_path 加 working_dir 越权防护;read_file 加 2MB 上限;CRLF 文件 str_replace/_insert 可正常匹配
- docker_manager: docker cp 改用 argv+shell=False 杜绝注入;expect 补全 zsh(%)/fish(>)/Powerline(❯) 提示符;命令结束哨兵改用 UUID 避免输出碰撞
- mcp_client: connect_and_discover 失败时释放 exit_stack,避免 stdio 子进程泄漏
- cli: plan 模式尊重用户 --max-steps(不再强制 cap 30);build 用 shutil 替换 os.system 跨平台
- local_llm: n_threads 默认 CPU 核数;n_gpu_layers 默认 -1 自动 offload
- P0: resume 断点恢复修复(new_task 不再无条件重置 _initial_messages,
  resume_from_messages 注入的历史消息不会被丢弃)
- P0: Skills allowed_tools 实际生效(_compute_skills_allowed_tools 计算交集后裁剪 _tools)
- P1: Plan 只读加固(python3 -c/node -e/git branch -D/git clean 加入黑名单;
  git branch 从 SAFE_PREFIXES 移除防漏放)
- P2: mimo-gateway 默认监听 127.0.0.1;token 比较改用 secrets.compare_digest(常量时间)
- P2: run 命令的 dead code("绝对路径"校验)移除
- P2: bash_tool _buffer 私有 API 加防御性 try/except
- P2: clear_older_ckg 加每小时节流,避免并发多 agent 时竞态
- bash_tool.py: 修复 _BashSession.run() 中 buffer 和 output 变量未初始化导致的 NameError
- bash_tool.py: ReadOnlyBashTool 增加 git add/merge/rebase/tag/stash 的 plan 模式拦截
- base_agent.py: 将 _close_tools() 和 cleanup_mcp_clients() 移入 finally 块防止 CancelledError 导致资源泄漏
- docker_manager.py: 分离 stop/remove 操作并增加 force=True 确保容器彻底清理
P0 安全修复:
- bash_tool.py: ReadOnlyBashTool 黑名单优先检查,防止 cd /tmp && rm -rf ~ 绕过
- docker_tool_executor.py: 用 shlex.quote() 替代单引号拼接,杜绝命令注入

P1 崩溃/错误修复:
- bash_tool.py: 修复后台子shell导致退出码永远为0的问题
- google_client.py: role="tool" 改为 role="user",修复 Gemini API 非法角色
- google_client.py/anthropic_client.py/ollama_client.py/openai_client.py:
  消息历史改为替换而非累加,防止 agent 传全量历史时重复
- ollama_client.py: 改用 Ollama 原生消息格式替代 OpenAI Responses API
- lake_view.py: 修复 tags_re.findall 为空时 IndexError
- trae_agent.py: Plan 模式下跳过 MCP 工具发现
- trajectory_recorder.py: 用 _async_save_pending 标志防止快照竞态覆盖
- openai_client.py: Resume 时清理孤立 function_call 防止 API 拒绝
P0 严重/安全(全部 4 项):
- P0-1 ReadOnlyBashTool default-deny:
  原 black-list + safe-prefix 默认放行 → 改为必须同时满足
  "未命中 BLOCKED_PATTERNS" AND "首命令在 SAFE_PREFIXES" 才允许。
  shred/hdiutil/osascript/xargs rm 等未列出的危险命令现在被拦截。
  SAFE_PREFIXES 扩到 ~50 个常用只读命令(ls/cat/grep/echo/...)。
  execute() 错误消息针对 "not in safe-allowlist" 给出具体提示。
- P0-2 Docker 路径遍历:
  _translate_path() 之前用 os.path.commonpath 单独检查,符号链接和
  ../ 路径可绕过(/work vs /workspace 这种 commonpath 巧合)。
  改用 os.path.realpath() 解析符号链接 + commonpath + os.sep 边界
  双重检查;workspace-evil、symlink-to-outside、.. 全部测试通过。
- P0-3 Ollama 双客户端:
  __init__ 创建了 openai.OpenAI 客户端(self.client)但从未使用,
  实际调用走 ollama_python chat()。删除 self.client + openai import。
- P0-4 Anthropic system_message 状态泄漏:
  parse_messages 之前直接修改 self.system_message,同 client 复用
  多个任务时上一个任务的 system prompt 污染下一个。
  parse_messages 改为返回 (messages, system_message) tuple,
  chat() 用局部变量传给 _create_anthropic_response。

关键 P1(6 项):
- P1-5 stderr._buffer 私有属性:
  改用公共 read() API + asyncio.wait_for 2s 超时。
- P1-6 MCP allow_mcp_servers=None 应表"全部允许":
  原代码 None → 直接 return,啥都不连接。改为 None = 不限制,[] = 拒绝。
- P1-7 TrajectoryRecorder 竞态:
  _async_save_pending 布尔标志 → _pending_future Future 对象。
  _sync_save 先 result() 等 pending future 完成(10s 兜底超时),
  再写最新快照。
- P1-8 Windows ! 字符展开:
  cmd.exe /v:on 启用延迟变量展开,用户命令中 ! 被当成变量。
  Windows 路径加 .replace("!", "^^!") 预处理。
- P1-9 _BashSession 无换行阻塞:
  原 readline() 等 \n,printf "hello" 等无换行输出卡住。
  改用 read(65536) + sentinel 字符串直接检测,不依赖换行。
- P1-10 GoogleClient hasattr 冗余:
  ToolResult 是 @DataClass,hasattr 永远为 True。
  简化为只检查 not tool_result.name。

P2 清理(3 项):
- P2-11 _serialize_tool_result call_call_id typo:用 getattr(call_id)。
- P2-14 _output_delay:从未使用,删除。
- P2-15 start.sh 强制清代理:TRAE_KEEP_PROXY=1 保留系统代理
  (给国内用户访问 OpenAI API 用);默认仍清代理(httpx/Shadowrocket 兼容)。

功能增强(⭐⭐⭐):
- 工具输出智能截断 (trae_agent/utils/output_truncation.py):
  bash output > 30 KiB 或 > 2000 行时,自动保留 head/tail + 中间
  marker + 提示用 sed/head 重读。bash_tool 接入,默认阈值可调。
  5KB 测试数据 → 6.6 KB 输出,保留头尾 + 跳行提示。
- 对话导出 Markdown/HTML (trae_agent/utils/trajectory_exporter.py):
  `trae-cli trajectory export <file> [--format md|html] [-o output]`
  把 trajectory JSON 渲染成可分享的文档(task/provider/steps/
  tool calls/tool results/reflection/error 完整展开)。
- Skill 校验 (trae_agent/skills/loader.py):
  validate_skill() 严格校验 frontmatter / name (kebab-case 正则) /
  description (必填, 1-500 字符) / body (非空) / tools (list of str)。
  load_skill_file 仍宽容(None + warning),但错误信息明确,
  不再静默吞掉。

兼容性:
- 所有 CLI 子命令验证通过 (version/plan/resume/skills/trajectory/skills list)。
- bash tool 实测 echo/printf/ls-fail 三个场景全部 OK。
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
1. [严重] allow_mcp_servers None→[] 转换使 P1-6 修复失效:
   构造函数把 None 和 [] 都转成 [],导致"None=允许所有"语义
   永不触发,用户配了 MCP server 但不写 allow_mcp_servers 时
   所有 server 仍被跳过。修复:保留 None 语义,config/legacy
   默认值改为 None。
2. openai_compatible_base.py: top_p 未处理 None,与 openai_client
   不一致,补充 NOT_GIVEN 降级。
3. ollama_client.py: str(None) 返回 "None" 字符串,改为 `or ""`。
4. base_agent.py: 错误分支未设置 execution.final_result,导致
   trajectory 和 CLI 看不到错误信息。
5. cli.py: plan 命令静默覆盖用户配置的 max_steps,增加 warning。
6. bash_tool.py: 静态哨兵可能被命令输出误触发,改为每次调用
   生成 UUID 哨兵(与 pexpect 版本修复一致)。
7. mcp_client.py: `raise e` 改为 `raise`,避免 traceback 混淆。
8. openai_compatible_base.py: 双 docstring 合并为一个。

Co-authored-by: traeagent <traeagent@users.noreply.github.com>
fix: 增强沙箱、MCP 和 LLM 客户端的健壮性与类型安全

本次合并主要针对代码健壮性、缺陷修复和类型安全进行改进,涉及沙箱执行、MCP 配置、LLM 客户端和 Bash 工具等多个模块。核心变更包括修复 Bash 哨兵字符串碰撞问题、将 allow_mcp_servers 改为可空类型、增强对空值和异常的防御性处理,并改进 Docker 工作区路径的动态获取。
- Add MiniMaxClient and NvidiaClient following OpenAICompatibleClient pattern
- Register MINIMAX and NVIDIA in LLMProvider enum and match statement
- Fix supports_tool_calling in OpenAICompatibleClient to delegate to
  provider_config, combining user config flag with provider capability check
- Add provider configs and 3 preset models (MiniMax-M3, kimi-k2.6, deepseek-r1)
  to trae_config.yaml.example
- API keys handled via MINIMAX_API_KEY and NVIDIA_API_KEY environment variables
- Add unit tests for both new providers (11 test cases, all passing)
…cker

Issues fixed:
1. allow_mcp_servers: fix truthy check that broke None=allow-all semantics
   (agent.py: changed from 'if allow_mcp_servers' to 'if mcp_servers_config')
2. Plan mode max_steps: unify behavior (explicit --max-steps wins, else default 30)
   (cli.py: removed misleading cap-and-warn logic)
3. _setup_telemetry: already removed (no dead code remaining)
4. top_k=0 semantic: add default=0, fix Google client to skip when 0
   (config.py, google_client.py: Gemini requires top_k>=1)
5. Integration tests: noted as future work (mock tests only currently)
6. install.sh uv --with syntax: verified compatible with modern uv
7. Docker workspace path: add container_workspace parameter (was hardcoded /workspace)
8. Trajectory size limits: add field truncation (50KB) and interaction cap (200)
   (trajectory_recorder.py: _truncate_field, _MAX_INTERACTIONS, _serialize_content)
9. Windows ! handling: already correct (^^! escape in cmd.exe /v:on)
10. Skills file permissions: skip world-writable .md files to prevent prompt injection
13. Built-in skills allowed_tools: validation already enforced via known_tool_names
14. Trajectory export memory: add 50MB file size warning in export()
    Also fix missing 'import json' in cli.py (NameError on trajectory export)
15. GPU memory warning: n_gpu_layers=-1 already falls back silently; documented
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant