# Repository Guidelines (Code-Verified)

> Last verified against current codebase: 2026-06-25
> Scope: `/Users/jojo/Desktop/agents/正在修复中/agents`

这份文档基于当前仓库实际代码重写。若与未来代码冲突，以代码为准并及时更新本文档。

> 和用户交流时默认使用中文。

## 0) 项目名称

本项目（仓库 `agents`）的暂定产品名为 **Astrion**。

- **构词**：`Astr-`（希腊语 *astron*，星星/天体）+ `-ion`（粒子/状态后缀，如 photon、electron）。
- **意象**：星之子、星际粒子、来自宇宙尘埃的微小发光体。
- **寓意**：Agent 像星际间传递信号的粒子，把人类意图传递到工具、文件、终端与子智能体之间；同时把零散任务像星尘聚合成恒星一样，聚合成可执行结果。
- **状态**：暂定名，后续若变更需同步更新 `AGENTS.md`、`claude.md` 与项目记忆 `astrion_naming`。
- **中文参考**：阿斯特里恩；简称可考虑 **星子**。

## 1) 当前项目结构（按代码现状）

- **后端入口**
  - `main.py`: 统一启动入口（当前默认走 Web 模式 + thinking_mode=True）
  - `server/app.py`: 推荐的 Web 服务入口（封装并转发到 `server/app_legacy.py`）
  - `web_server.py`: 兼容入口，已标记 deprecated，但仍可启动
- **后端核心目录**
  - `server/`: Flask 业务主线（chat/task/status 已拆分为子包：`server/chat/`、`server/status/`、`server/tasks/`；REST 任务轮询为主，Socket.IO 主要用于兼容与实时辅助通道）
  - `core/`: 终端与工具编排（`main_terminal.py`、`web_terminal.py`、`main_terminal_parts/*`；其中 `main_terminal_parts/context/` 和 `main_terminal_parts/tools_definition` 已拆分为 base + mixin 子包）
  - `modules/`: 可复用能力模块（terminal/file/memory/sub_agent/upload_security/user 等；`file_manager`、`persistent_terminal`、`terminal_ops`、`mcp_client_manager` 已拆分为子包）
  - `config/`: 配置拆分（`api.py`, `limits.py`, `terminal.py`, `paths.py` ...），由 `config/__init__.py` 聚合并加载 `.env`
  - `utils/`: API client、日志、上下文与对话工具等公共函数（`api_client`、`tool_result_formatter`、`context_manager`、`conversation_manager` 已拆分为子包；原入口文件保留为兼容入口）
- **前端目录**
  - `static/src/`: Vue 3 + TS 前端
  - `cli/src/`: React 19 + Ink 6 + TypeScript CLI 前端（正在重写中）
  - 监控动画相关核心文件：
    - `static/src/components/chat/monitor/MonitorDirector.ts`
    - `static/src/stores/monitor.ts`
    - `static/src/components/chat/monitor/*`
- **CLI 相关文档**
  - `docs/cli_ui_display_spec.md`: CLI 显示层设计说明
  - `docs/cli_slash_commands_spec.md`: CLI `/` 指令设计说明
- **其他子项目/资源**
  - `android-webview-app/`: Android WebView 客户端工程
  - `modules/sub_agent/`: 子智能体执行逻辑（主进程内 `asyncio.Task`，工具调用复用主进程链路）
  - `easyagent/`: 旧版 Node.js 子智能体实现，暂时保留但已不再使用
  - `_experiments/`: 本地实验残留与历史文档归档目录（**不纳入 git**，见 §7）

## 1.5) 运行态数据目录与路径变量（2026-06 更新）

> 设计目标：运行态数据（对话、用户工作区、日志等）默认存放在用户主目录的 `~/.astrion/astrion/` 下，对齐 `~/.claude`、`~/.codex` 惯例，**不落在源码树内**。配置实现见 `config/paths.py`。

### 1.5.1 默认位置与模式分流

运行态数据根目录（`data_root`）默认为 `~/.astrion/astrion`，可通过 `ASTRION_DATA_ROOT` 整体搬迁。在该根目录下，按运行模式自动分流（由 `TERMINAL_SANDBOX_MODE` 决定）：

- 宿主机模式（`TERMINAL_SANDBOX_MODE=host`）→ `~/.astrion/astrion/host`
- 其它模式（默认 docker / web）→ `~/.astrion/astrion/web`

数据根目录下还包含：`settings.json`（唯一配置文件）、`config/`（部署级配置，host/web 共享）。每个模式目录下包含：`data/`（对话、用户库、记忆、sub_agents.json、sub_agent_tasks 等）、`users/`（web 多用户工作区）、`api/users/`（API 用户工作区）、`logs/`（日志）。

### 1.5.2 路径解析优先级（从高到低）

1. **具体目录环境变量**：`DATA_DIR` / `LOGS_DIR` / `USER_SPACE_DIR` / `API_USER_SPACE_DIR`（单独覆盖某个目录，最高优先级）
2. **数据根目录环境变量**：`ASTRION_DATA_ROOT`（整体搬迁运行态数据根目录，默认 `~/.astrion/astrion`）
3. **兜底默认**：`~/.astrion/astrion/<mode>`

具体目录变量支持相对路径（相对仓库根目录展开）、绝对路径与 `~`。

> 注意：`config/*.json` 分两类：
> - **程序能力**（`docker_risk_markers.json`、`skill_hints.json`）：是程序行为的一部分，随版本演进，仍锚定源码树。`prompts/`、`agentskills/` 同理。
> - **部署级配置**（`custom_models`、`host_workspaces`、`auto_approval`、`goal_review`、`forbidden_commands`、`host_sandbox_policy`）：因部署/机器而异或含密钥，外置到 `~/.astrion/astrion/config/`（即 `DEPLOY_CONFIG_DIR`，可单独用该环境变量覆盖，host/web 共享）。读取走 `config.resolve_deploy_config(name)`，回退链：部署目录 → 源码树 `.json` → 源码树 `.json.example`，因此开发环境不必先跑 setup 也能用源码树种子。含密钥/机器特定的 5 个（除 `forbidden_commands`）不纳入 git，仓库仅留 `.example`。

### 1.5.3 Host / Web 双路径机制（2026-06 新增）

`paths.py` 新增三个固定变量，支持 host 模式同时读取 web/ 数据：

- **`IS_HOST_MODE`**：当前是否为 host 模式（布尔值）
- **`WEB_DATA_DIR`**：固定指向 `<data_root>/web/data`（不受当前模式影响）
- **`WEB_USER_SPACE_DIR`**：固定指向 `<data_root>/web/users`（不受当前模式影响）

**host 模式**（`modules/user_manager.py`）：
- 用户列表：同时加载 `host/data/users.json` + `web/data/users.json`（web 用户不覆盖已有）
- 工作区列表：合并 host/ 和 web/ 两处
- 工作区定位：已有 web 工作区直接复用；新工作区写入 host/
- 写操作（创建/删除/重命名）：始终走 host 路径

**web 模式**：只读取 web/ 数据，无变化。

> 设计意图：host 模式 = host + web 数据都可用；web 模式 = 仅 web 数据可用。

### 1.5.4 日志策略（2026-06）

- API 请求体落盘（dump）默认**关闭**，由 `AGENT_API_DUMP_ENABLED` 控制（`1/true/yes/on` 开启）。
- 日志混合轮转（实现见 `utils/log_rotation.py`）：
  - 追加型单文件（`host_workspace_debug.log`、`chunk_*.log`、`conn_diag.log`、`api_debug.log`、TaskLogger/ErrorLogger）按大小轮转，默认单文件 20MB、保留 3 份。
  - 按份计量目录（`api_requests/` 一请求一文件）只保留最近 N 个，默认 30。
  - 阈值可覆盖：`AGENT_LOG_ROTATE_MAX_BYTES` / `AGENT_LOG_ROTATE_BACKUPS` / `AGENT_DUMP_KEEP`。

### 1.5.5 数据迁移

- 迁移脚本：`scripts/migrate_runtime_data.py`（源码树 → 运行态根；复制+备份+可回滚+幂等，`logs/` 丢弃不迁）。
  - 脚本 `import config` 复用程序同一套路径解析（含 `.env`），**模式由 `.env` 决定**；执行前务必先 `--dry-run` 确认目标。
- 清理误迁副本：`scripts/cleanup_misplaced_web.sh`。

## 2) 当前可用启动/构建命令（已按代码核对）

### Python / 服务端
- 安装依赖：`pip install -r requirements.txt`
- 推荐启动 Web：`python -m server.app`
- 可选参数：`python -m server.app --path ./project --port 8091 --debug --thinking-mode`
- 兼容启动方式：`python web_server.py`
- 统一入口：`python main.py`（当前实现会默认进入 Web 启动流程）

### Frontend（根目录）
- 安装依赖：`npm install`
- 构建：`npm run build --silent 2>&1 | tail -n 5`（默认只看最后 5 行，减少 Vite 构建资源列表等无关输出）
- 开发监听（当前脚本是 build watch）：`npm run dev`
- Lint：`npm run lint`

### CLI（React / Ink）
- 安装依赖：`npm --prefix cli install`
- 开发启动：`npm run cli` 或 `npm --prefix cli run dev`
- 构建：`npm run cli:build`
- 类型检查：`npm run cli:typecheck`
- 可执行命令名（构建后）：`agents` / `agents-cli`

## 3) 测试现状（不要再写过时命令）

- 当前仓库内可见自动化冒烟：`test/test_server_refactor_smoke.py`（`unittest`）
  - 运行方式：`python -m unittest test.test_server_refactor_smoke`
- `test_system_message.py` 依赖外部 `MOONSHOT_API_KEY` 与网络，不属于离线稳定 CI 用例。
- 当前仓库未发现 `pytest.ini`/`pyproject.toml`/`tox.ini`；不要默认要求 `pytest` 作为唯一入口。
- CLI 当前最小可复现验证：
  - `npm --prefix cli run typecheck`
  - `npm --prefix cli run build`
- 若改动 `server/chat/`、`server/status/`、`server/tasks/` 等后端接口适配，补充：
  - `python3 -m py_compile server/chat/*.py server/status/*.py server/tasks/*.py`
  - `python -m unittest test.test_server_refactor_smoke`

## 4) 代码修改约定（实用版）

- **最小改动原则**：只改与需求直接相关文件，避免顺手重构。
- **精确读取原则**：禁止整文件通读。阅读/修改前先用搜索工具（grep / 符号查找 / 关键字定位）锁定需要的行段，只精确提取并阅读相关片段，不要把整个文件一次性读进来。
- **文件编辑方式**：修改文件时优先使用 `apply_patch` 或其他原生文件编辑工具；尽量不要用 `bash`/`python` 脚本批量改文件，除非原生工具明显不适合。
- **后端改动优先级**：先改 `modules/`、`server/` 内对应模块，最后才动入口。
- **前端改动优先级**：按 `static/src` 现有分层改（`app/`、`stores/`、`components/`、`composables/`）。
- **CLI 改动优先级**：优先在 `cli/src/App.tsx`、`cli/src/components.tsx`、`cli/src/eventMapper.ts`、`cli/src/api.ts` 内做最小闭环修改。
- 涉及 monitor 动画/事件联动时，至少同步检查：
  - `MonitorDirector.ts`（动画与场景执行）
  - `stores/monitor.ts`（事件队列与状态机）

## 5) 风格与质量

### 文件长度与拆分

- **单文件最好不超过 1000 行**；超过时应优先考虑合理拆分，避免维护成本快速上升。
- **行数不是硬性指标**：文件是否拆分取决于**可维护性**，而不是必须小于某个固定行数。
- 当单一文件超过约 500 行时，应评估是否具备以下拆分价值：
  - 业务逻辑过重、职责不单一；
  - 存在清晰的边界（如按工具类型、按资源、按生命周期阶段）；
  - 拆分后能提高复用性、可读性或测试便利性。
- **不要为了拆分而拆分**。边界模糊、耦合紧密或改动频率低的文件，保持现状可能更划算。
- Vue SFC 因模板、样式、脚本耦合较深，拆分风险较高；优先抽离 composable 和子组件，而非直接切分 `.vue` 文件。

- Python：4 空格、`snake_case` 函数、`PascalCase` 类，新增公共函数尽量补 type hints。
- Vue/TS：保持现有代码风格，不做无关风格清洗。
- CLI React/TS：保留现有 Ink 渲染方式与光标修正逻辑，不要轻易重写输入框定位策略。
- 日志：优先复用现有 logger/日志路径，不引入大量临时 `print`。
- 提交前至少做与改动相关的最小验证（命令输出或手工步骤要可复现）。
- 运行根目录前端构建时，默认使用 `npm run build --silent 2>&1 | tail -n 5`。

### CLI 当前交互约束（2026-05-15）

- CLI 连接的是现有本地 Web API（默认 `127.0.0.1:8091`），不是独立 agent runtime。
- 启动 CLI 时应清屏、连接本地服务、创建新会话，并将输入区固定在底部。
- 当前目录若不在工作区中，应先弹出“是否添加到工作区”的选择。
- 思考内容当前默认隐藏，只显示“思考中 / 思考完成”标题；相关折叠代码保留，后续可继续修。
- 不要在未获得用户要求的情况下运行交互式 TUI 压测或长时间模拟输入，以免刷屏占满上下文。

## 5.5) 前端设计风格统一规范（强制）

> 适用范围：所有前端 UI（以 `static/src` Web 为主；`cli/src` 在视觉可类比处同样适用；以及未来新增的任何界面）。
> 约束级别：写新 UI 或改动 UI 时**必须遵守**；遇到存量违规应在最小改动允许范围内顺手修正。
> 配色基础设施：两层 token（原始层 + 语义层）定义在 `static/src/styles/base/_tokens.scss`（`:root[data-theme='classic'|'light'|'dark']` + 首屏回退 `:root:not([data-theme])`），切换逻辑见 `static/src/utils/theme.ts`；`.stylelintrc.cjs` 三条规则做防回退栏杆，`build` 含 `stylelint` 步骤。

1. **禁止边缘光晕**：不使用任何 glow / 外发光 / 彩色光晕效果（大范围彩色 `box-shadow` 扩散、`filter: drop-shadow` 光圈、`::before/::after` 模糊光晕等）。已有的要移除。允许的只是中性、克制的投影——沿用 `--shadow-*` 既有 token，不自造发光阴影。**仅在用户明确允许或要求时才可使用光晕效果。**
2. **禁止原生浏览器组件**：不直接使用浏览器默认外观的 `<select>`、`<input type=checkbox/radio/range/date/color>`、`alert/confirm/prompt`、原生右键菜单、原生 tooltip 等，一律换成与项目风格统一的自定义组件（下拉 / 开关 / 单选 / 滑块 / 模态 / Toast）。
3. **禁止圆角套娃**：不允许「圆角矩形套圆角矩形套圆角矩形」的多层嵌套卡片——典型垃圾审美，且极大占据有效显示面积。容器层级要扁平，优先用分隔线 / 留白 / 底色区分，不要多包一层带边框圆角的盒子。
4. **图标外框对齐**：同一组图标按钮的外框（点击热区 / 容器）必须统一对齐，不能一会居中、一会左对齐、一会右对齐；同组固定相同尺寸与对齐方式。
5. **图标视觉对齐而非理论对齐**：按图标视觉重心对齐而非几何边界盒；对重心偏移的图标（三角形 / 播放键 / 放大镜等）做微调，使其「看起来居中」。
6. **颜色遵循三模式切换 + 两层 Token**：所有颜色走原生三模式，禁止写死颜色字面量（裸 hex / `rgb()`/`hsl()` 字面色 / `var(--x, #hex)` 兜底），一律用**中性语义** CSS 变量（`--surface-*` / `--text-*` / `--border-*` / `--accent*` / `--state-*`）。三主题定位：**经典抄 Claude 亮色盘**（暖奶油 + 暖橙 primary `#cc785c`）、**浅色抄 ChatGPT 亮色盘**（冷白 + 灰 + 近黑 primary `#181818`）、**深色自研中性灰阶**。亮色表面靠灰阶拉层次（经典 `--surface-base #faf9f5` < `soft #f5f0e8` < `card #efe9de` < `muted #e8e0d2`），不许全塌成白。primary 克制（CTA-only），hover/选中/运行态走中性灰。半透明 tint 用 `color-mix(in srgb, var(--token) N%, transparent)` 派生。`--claude-*`/`--theme-*` 是过时别名，新代码勿用。新增颜色必须三主题（含首屏回退）补齐。
7. **带文字容器固定高度**：所有含内部文字的选项 / 按钮 / 标签 / 容器固定高度，禁止因文字多少被撑大撑高；过长文字用省略号或内部滚动处理。
8. **窗口设最大尺寸 + 内部滚动**：所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`，超出由内部容器滚动，不顶大整个窗口；滚动条要么隐藏，要么做样式适配，不暴露原生粗滚动条。
9. **实体面板禁止半透明**：实体 UI 容器（对话区 / 侧栏 / 下拉/二级菜单 / 抽屉 / 对话框本体 / 状态条 / tooltip / 输入栏）背景必须不透明，且不加 `backdrop-filter` 磨砂。唯一例外：遮罩层 scrim（`--overlay-scrim`，`position:fixed;inset:0`）和刻意玻璃质感装饰保留半透明。

## 5.6) 工具结果显示规范（强制）

> 适用范围：所有使用 `tool-result-meta` / `tool-result-content` 结构渲染的工具结果（如 `static/src/components/chat/actions/ToolAction.vue`、`toolRenderers.ts` 等）。
> 约束级别：新增或修改工具结果渲染时**必须遵守**。

1. **meta 部分只放参数**：`tool-result-meta` 仅用于展示工具调用的参数、配置、状态标识等元信息。例如：状态、ID、路径、任务描述、超时时间等。禁止在 meta 区域放置执行结果、统计摘要、输出内容等。
2. **content 部分只放结果**：`tool-result-content` 仅用于展示工具执行后的结果、输出、统计、总结等。例如：文件内容、命令输出、搜索命中、执行统计（工作时间 / 调用次数 / 工具次数）、最终回复摘要等。禁止在 content 区域重复展示工具参数。

## 5.7) 调试输出规范（强制）

> 约束级别：需要临时加调试日志排查问题时**必须遵守**。

### 前端调试日志

- **统一筛选词**：所有临时 `console.log` 必须使用**同一个**筛选前缀（例如 `[route-debug]`），方便用户在浏览器控制台用该前缀一次性筛选。
- **控制数量与时机**：
  - 禁止一次性输出上百上千条日志刷屏。
  - 只在关键路径（入口、分支判断、状态变化、导航动作）打印，避免在循环、高频事件、每帧渲染中输出。
  - 需要大量结构化数据时，优先用 `console.group` / `console.table` 或对象快照，而不是逐条打印。
- **用完即清**：问题定位后应及时删除或注释掉临时调试日志，不要长期留在代码里。

### 后端调试日志

- **必须写入文件**：后端调试信息禁止直接 `print` 到终端刷屏，必须写入日志文件。
- **复用现有 logger**：优先复用项目已有 logger（如 `utils/logger.py`、`modules/multi_agent/debug_logger.py` 等），按模块落到 `~/.astrion/astrion/<mode>/logs/` 下。
- **关键状态转换必打**：多智能体、子智能体、任务轮询等复杂链路应在关键状态转换点写结构化日志，便于复现问题后按时间线追溯。

## 6) Git 工作流（开发 + Review）

### 6.1 核心原则

- 默认直接在 `main` 上开发，不强制新建功能分支。
- 每个功能建议在 `main` 上保持一个清晰 commit，历史干净可回滚。
- 使用 **Conventional Commits**：`feat:` `fix:` `refactor:` `chore:` `docs:` `test:` `perf:`。
- 个人项目，不设 PR 和 Review 流程；如用户临时要求隔离开发，再按需新建分支。

### 6.2 工作流（唯一步骤）

AI 执行以下流程时，每一步都要向用户说明在做什么：

```
0. 检查工作区状态
   git status
   如有未提交的改动：
   - 若无关：提醒用户先 commit 或 discard
   - 若有关：git stash push -m "WIP: <简短描述>" 暂存

1. 确认在 main 并同步最新代码
   git checkout main && git pull origin main

2. 开发并提交
   git add <修改的文件>
   git commit -m "feat(scope): 简短描述"
   （多轮迭代可用 git commit --amend 保持一个 commit）

3. 推送到远程（备份 + 方便切设备）
   git push origin main
```

**重要约束**：
- AI 必须在本地验证修改能正常运行，再建议用户提交。
- 修改完成后按改动规模汇报：大修改说明核心变更点与验证结果；小修改只说明核心变更点。
- 禁止 AI 在未经用户确认的情况下执行 `git push`。
- 不需要每次都执行或输出 diff；如用户要求 Review，再在同一对话内使用 `git diff` 或 `git diff HEAD~1..HEAD`。

### 6.3 常用命令速查

| 操作 | 命令 |
|------|------|
| 暂存未完成工作 | `git stash push -m "WIP: xxx"` |
| 修改最近 commit | `git commit --amend` |
| 同步 main | `git checkout main && git pull origin main` |
| 同对话内 Review diff | `git diff` 或 `git diff HEAD~1..HEAD` |
| 查看提交历史 | `git log --oneline -10` |

## 7) 安全与仓库卫生

- 严禁提交真实密钥（`.env`、token、cookie、用户隐私）。
- 运行态数据默认在 `~/.astrion/astrion/<mode>/`（`data/`、`users/`、`logs/`、`api/`），不在源码树内；详见 §1.5。`.gitignore` 仍忽略源码树内的 `logs/`、`data/`、`users/`、`api/`、`project/` 等，以防通过具体变量指回源码树或历史遗留产生污染。分享前需脱敏。
- **`_experiments/` 用途**：归档本地实验残留与历史文档（调试记录、旧变更日志、模型测试脚本、翻译资料、旧子智能体文档等）。该目录**不纳入 git**（已在 `.gitignore`）。需要保留但不属于当前主线、又不想直接删的零散文件，统一放这里，不要散落在根目录。
- 不要把本地构建产物（如 `static/dist/`、`node_modules/`）纳入提交。

## 8) Android App 发布联动要求（重要）

当修改前端并需要发布 Android WebView App 时，必须同步执行以下步骤（依据 `doc/android_app_release_and_update.md`）：

1. 同时更新版本信息  
   - `android-webview-app/app/build.gradle.kts`：递增 `versionCode`，更新 `versionName`
2. 同时更新更新说明  
   - `android-webview-app/APP_CHANGELOG.md`：在顶部新增当前版本说明
3. 完成修改后提醒用户运行上传脚本  
   - 在发布流程中运行：`bash /Users/jojo/Desktop/agents/正在修复中/agents/upload_android_apk.sh`

> 禁止只改前端代码而不更新版本号/更新说明，否则会导致客户端更新提示与分发信息不一致。

## 9) 给 Agent 的硬性要求

- 先读当前代码再执行，不依赖历史文档记忆。
- 输出结果按改动规模决定：
  - 大修改：必须包含核心变更点、验证结果（已执行命令/未执行原因）。
  - 小修改：只需包含核心变更点。
- 不要求每次列出所有修改文件；仅在用户要求、改动复杂或有助于说明时列出。
- 不要求每次执行或展示 diff；仅在用户要求 Review 或排查差异时使用。
- 如果发现本文档过时，直接更新 `AGENTS.md` 并在结果中说明依据。

## 10) 宿主机权限模式与沙箱执行机制（2026-05 更新）

完整说明文档：`docs/host_sandbox_and_permission_model.md`

为避免误解，当前系统有两套独立但叠加的控制：

1. **权限模式（Permission Mode）**：`readonly` / `approval` / `auto_approval` / `unrestricted`  
2. **执行环境（Execution Mode）**：`sandbox` / `direct`（仅宿主机模式可切换）

### 10.1 权限模式

- `readonly`
  - `run_command` 可调用，但在只读沙箱执行；写入由系统拒绝
  - `write_file` / `edit_file` 等写入类工具直接拒绝
- `approval`
  - `run_command` 先走只读沙箱
  - 若出现权限拒绝（例如 `Operation not permitted` / `Permission denied`），触发前端审批
  - 审批通过后，仅该次命令以可写沙箱重试；工具结果返回“重试后的最终结果”
- `auto_approval`
  - `write_file` / `edit_file`：工作区内路径直接执行，工作区外路径进入审批流程
  - `run_command` 先走只读沙箱；触发权限拒绝后由后台审批智能体自动审核
  - 自动审核拒绝时，工具返回“被拒绝+理由”并继续主循环（不强制结束任务）
- `unrestricted`
  - 不做权限模式拦截；是否沙箱由执行环境决定

### 10.2 执行环境

- `sandbox`（默认）：使用 OS 沙箱执行（macOS: `sandbox-exec`，Linux: `bwrap + seccomp`，Windows: `WSL2`）
- `direct`：宿主机直接执行（高风险，仅建议短时使用）
- 宿主机下支持会话级 TTL 自动回退 `direct -> sandbox`

### 10.3 路径授权语义

- 前端“路径授权”支持两类路径：
  - **可读可写**
  - **仅可读**
- 语义：
  - 可读集合 = 可读可写 + 仅可读
  - 可写集合 = 可读可写

### 10.4 维护约束

- 不要在提示词里暴露内部实现细节（例如“命令文本猜测”等）
- 文案应面向用户能力边界与操作建议，不描述内部判定算法

### 10.5 自动审批智能体配置与调试

- 自动审批配置文件：`config/auto_approval.json`
  - 建议字段：`name` / `url` / `key` / `model` / `extra_params`
  - 额外控制：`timeout_seconds` / `max_rounds` / `max_command_timeout`
- 调试开关（代码变量）：`modules/approval_agent.py` 中 `DEBUG_SAVE_APPROVAL_AGENT_TRANSCRIPT`
  - 开启后写入：`logs/approval_agent/`
  - 记录以累积 `messages` 为主，便于对齐主/子智能体会话格式

## 11) 多智能体模式（multi-agent mode）

> 对话级开关 `metadata.multi_agent_mode = true` 启用，URL 入口 `/multiagent/<conv_id>`，数据目录 `~/.astrion/astrion/host/mutiagents/`（保留原拼写）。完整设计与踩坑记录见项目记忆 `multi_agent_mode_design`，本节只列 Agent 改代码时必须知道的硬约束。

### 11.1 角色与实例

- 主智能体显示名固定为 `Team Leader`，不需要专门的预置角色文件。
- 子智能体 = `role_id`（如 `ui-operator` / `full-stack-engineer` / `code-reviewer` / `researcher`）+ `agent_id`（同一 role_id 下从 1 递增）。显示名格式 `{Role Name}_{agent_id}`，例如 `UI Operator_1`，后缀永远带数字。
- 主→子 / 子→主 / 子→子三种通信通过工具完成，工具签名见 `modules/multi_agent/tools.py`。
- **`send_message_to_sub_agent` 与 `ask_sub_agent` 语义不同，必须保留两者**：前者插入引导消息不阻塞，后者阻塞等待一轮回答。
- 子智能体间通信要求同时向主智能体输出汇报，不允许「偷偷沟通」。

### 11.2 子智能体执行机制

- 子智能体在主进程内 `asyncio.Task`，跑在独立后台事件循环线程里（避开 Flask-SocketIO threading 冲突）。工具调用复用主进程沙箱/容器链路，网络调用走 `utils.api_client.DeepSeekClient`。
- 子智能体在多智能体模式下：
  - `create_sub_agent` 强制 `run_in_background=False`，不触发 `sub_agent_waiting` 事件，不阻塞前端输入区。
  - 子智能体自然的 assistant 输出结束（无 tool_calls）即本轮任务结束，进入 `idle`，上下文保留，不算 failed。
  - 不能把 `output.success == null` 直接判为 `failed`—`_check_task_status()` 对多智能体任务 `status=running/idle` 视为正常态（见 `modules/sub_agent/state.py`）。
  - idle 等待必须用 `asyncio.Event`，不能用 `threading.Event`，跨线程唤醒用 `call_soon_threadsafe`。
  - 任务记录里 `multi_agent_mode` 字段必须显式写入，缺失会导致传统后台通知池把多智能体任务当后台任务处理。`reconcile_task_states()` 已对旧任务补回。

### 11.3 消息池与派发链路（重点）

**输出端 ↔ 接收端分离**：
- 输出端（`SubAgentTask._forward_output_to_master`）：子智能体每次 assistant 文本输出都封为标准格式消息并 `push_master_message` 到会话的 `MultiAgentState.pending_master_messages`，不再做事。
- 接收端（`MultiAgentState` + dispatch）：根据主智能体当前状态选择插入方式：
  - **情况1（主运行中）**：在 `execute_tool_calls` 末尾 `process_multi_agent_master_messages(inline=True, after_tool_call_id=...)` 插入到下一轮模型 messages 列表里（详见 `server/chat_flow_tool_loop.py:1326`）。
  - **情况2（主空闲）**：`poll_multi_agent_notifications` spawn 出后台 poll，主对话空闲且 pool 有消息就 drain，调 `_dispatch_multi_agent_idle_messages` 创建 `task_type="notice"` 的后续 task，触发新一轮工作。完整顺序见项目记忆 multi_agent_mode_design 中的「情况2完整链路」图。
  - **情况3（主最后一轮无工具调用）**：主循环 `if not tool_calls:` 分支调 `process_multi_agent_master_messages(inline=False)` 后 `continue` 继续迭代，Team Leader 在同 task 续跑处理子智能体输出。（实现上与情况2同路径，即情况3通过后续 task 完成情况2）。

**情况2 硬约束（调试踩过坑）**：
  1. **Pool 优先，不能等所有 running 退出再 drain**：`ask_master` await 期间 status=running，但子智能体本身不会产生新输出，只有等主对话回答才能解套。pool 有消息就立即 drain，不管 running 状态。「对话处于运行状态」只决定前端显示态，不阻塞 pool 消费。
  2. **`_dispatch_multi_agent_idle_messages` 持久化不要重复**：前置 N-1 条调 `inject_multi_agent_master_message` 绑定持久化。最后一条只 emit 给在线客户端、不持久化、在后续 `task_manager.create_chat_task` 走的 `handle_task_with_sender` 里才 `add_conversation`。否则历史里会有两条相同 user 消息，前端刷新会被渲染两遍（一条多智能体渲染、一条通知渲染）。
  3. **Metadata `visibility` 必须显式为 `"chat"`**：`_user_message_ui_defaults("sub_agent")` 默认给 `{visibility: "compact"}`；在 `_dispatch_multi_agent_idle_messages` 构造 `auto_user_message_payload`时，如果有`**ui_defaults`，必须在它之**后**写 `"visibility": "chat"`（dict 字面量中后出现的key 胜出）。同样，在`handle_task_with_sender`处理多智能体消息时，`user_message_metadata.update(multi_agent_meta)`之后要显式`user_message_metadata["visibility"] = "chat"`。
  4. **必须传 `auto_message_type`**：前端 `isMultiAgentMessage()` 只看 `auto_message_type.startsWith('multi_agent_')`。`auto_user_message_payload` 和 `preceding_user_notices[i].payload` 都必须显式写 `auto_message_type`，字段不能用空值，否则前端 fallback 起通知渲染。

### 11.4 通知池轮询器完全独立

- 传统后台子智能体 / 后台 `run_command` 通知：`poll_completion_notifications`，在 `handle_task_with_sender` 结尾的 `needs_completion_poll` 。
- 多智能体通知：`poll_multi_agent_notifications`，在 `needs_ma_poll`。两者完全独立，避免 task_manager 单工作区互斥竞争。
- `task_complete` 事件中：
  - `has_running_sub_agents` 只算传统后台任务，不算多智能体。
  - `has_running_multi_agent` 多智能体专用字段，同时包含 _running_ 实例和 pending master 消息（详见 `server/chat_flow_task_main.py:2324`）。前端通过该字段走独立的 `startMultiAgentTaskProbe`（不启动 `sub_agent_waiting`）。

### 11.5 渲染与前端约定

- 多智能体消息渲染条件是**两个独立判断**：
  - `isMultiAgentMessage()`：看 `auto_message_type.startsWith('multi_agent_')`。
  - `getMessageVisibility()`：看 `metadata.visibility`。两者都必须正确，消息才能走多智能体渲染分支。任一错都会 fallback 到通知渲染。
- 多智能体消息不显示新的 assistant 回复头部（Astrion/工作时间），即 `metadata.starts_work=false`。但前端通过 `has_running_multi_agent` 在 `task_complete` 中单独处理恢复轮询。
- 子智能体进度弹窗：输出与工具按真实时间线混排，默认 3 行，过长用省略号或内部滚动；颜色走 `--text-primary` 等语义 token。
- 全局工具规范统一适用于多智能体权限：UI 不能引入 `compact` 以外的 fallback 样式习惯复制到多智能体渲染。

### 11.6 工具结果格式化与前端渲染位置

新增多智能体工具时，必须同步补齐「后端结果格式化」和「前端结构化渲染」，否则前端会 fallback 到原始 JSON 或空白。

**后端 formatter（主智能体工具）**
- 实现位置：`utils/tool_result_formatter/agent_context.py`
- 注册位置：`utils/tool_result_formatter/dispatch.py` 的 `TOOL_FORMATTERS`
- 处理入口：所有主智能体工具执行结果，最终由 `format_tool_result_for_context()` 转换为自然语言摘要，写入对话历史。

**后端 formatter（子智能体通信工具）**
- 实现位置：`modules/sub_agent/toolkit.py` 的 `_format_tool_result()`
- 覆盖工具：`ask_master` / `ask_other_agent` / `answer_other_agent` / `list_active_sub_agents`
- 处理入口：子智能体 tool call 结果回填到子对话上下文前。

**前端 renderer（主路径）**
- 实现位置：`static/src/components/chat/actions/toolRenderers.ts` 的 `renderEnhancedToolResult()`
- 调用方：
  - `static/src/components/chat/MinimalBlocks.vue`
  - `static/src/components/chat/StackedBlocks.vue`
- 注意：这两个视图已移除 `enhanced_tool_display` 开关判断，**所有工具块都强制走结构化渲染**，不再显示原始 JSON。

**前端 renderer（备用路径）**
- 实现位置：`static/src/components/chat/actions/ToolAction.vue` 的 `renderToolResult()`
- 调用方：`static/src/components/chat/ChatArea.vue`
- 同样已移除原始 JSON fallback，仅作为 ChatArea 独立工具块渲染的备用。

**新增工具 checklist**
1. `modules/multi_agent/tools.py` 定义工具签名。
2. `core/main_terminal_parts/tools_execution.py` 实现工具 handler 并返回标准 dict（`success` + 业务字段 + 可选 `error`）。
3. `utils/tool_result_formatter/agent_context.py` 新增 `_format_<tool_name>()`，`dispatch.py` 注册。
4. `static/src/components/chat/actions/toolRenderers.ts` 新增对应 `render<PascalCaseToolName>()` 并在 `renderEnhancedToolResult()` 中分发。
5. 如果是子智能体通信工具，同步在 `modules/sub_agent/toolkit.py` 的 `_format_tool_result()` 中补 formatter。

### 11.7 调试机制

- 调试日志统一走 `modules/multi_agent/debug_logger.py` 的 `ma_debug()` 函数，写入 `~/.astrion/astrion/host/logs/multi_agent_loop.log`。
- 关键状态转换点必须打 `ma_debug`，便于复现 bug：
  - `handle_task_with_sender_start`（含 `pending_master_messages_count`）
  - `state_push_master_message` （交出 `queue_len_before`）
  - `poll_ma_tick` （每 0.5s 一次，包含 `instance_count` / `statuses` / `pending_count` / `main_active` 的完整状态快照）
  - `dispatch_ma_idle_enter` / `dispatch_ma_idle_before_create_task` / `dispatch_ma_idle_task_created` / `dispatch_ma_idle_sender_user_message` / `dispatch_ma_idle_exit_ok`
  - `create_chat_task` 异常 / `provide_answer` 跨循环回写
  - `runtime_injected` 字段标记（metadata 里能区分多智能体 inline / idle / ask插入路径）

### 11.8 已知坑

- 多智能体模式下根本不 `emit` `sub_agent_waiting` 事件，避免前端进入「等待后台子智能体」的输入区阻塞态。
- `_announced_sub_agent_tasks` / `notified` 等标记仅适用于传统后台子智能体任务，多智能体任务不走这条通知路径。
- 多智能体任务的 `output.json` 中 `status` 在子智能体自然进入 idle 时会被写为 `"idle"`；在 `_check_task_status` 中由 `_check_task_status_keep_alive` 跳过防止错误判定为 `failed`。
- 传统后台通知池 `_collect_pending_completion_notices` 在 `task.get("multi_agent_mode")` 为真时跳过该 task；多智能体派出走独立的 `poll_multi_agent_notifications` 路径，不当混用。
- `_has_pending_completion_work` 主动排除 `multi_agent_mode=True` 的任务；两者永远独立。

### 11.9 对话切换与状态保留

- 切换会话不清理 `_running_tasks` 和 `_sub_agent_instances`。`SubAgentManager` 的全局 tasks 字典按 `task_id` 保持，多智能体状态由 `conversation_id` 在 `get_multi_agent_state` 中查。
- 子智能体对话存在 `~/.astrion/astrion/host/host/data/sub_agents/`。重启后走 `manager.restore_sub_agent` 恢复实例引用。
- `MultiAgentState` 实例在 Manager 上常驻（dict by `conversation_id`）；进程重启走 `from_snapshot` 恢复。

---

注：本节按「现有架构 + 多智能体分支」方案描述，与项目记忆 `multi_agent_mode_design` 同步；如两者冲突以代码为准并同步修订本节。