[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80100":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":38,"readmeContent":39,"aiSummary":40,"trendingCount":15,"starSnapshotCount":15,"syncStatus":41,"lastSyncTime":42,"discoverSource":43},80100,"emperor-agent","TheSyart\u002Femperor-agent","TheSyart","Local Emperor-style AI agent with Vue WebUI, multi-provider LLMs, streaming chat, tools, skills, memory, and token telemetry.","https:\u002F\u002Fgithub.com\u002FTheSyart\u002Femperor-agent",null,"Python",84,25,1,0,14,20,27,42,4.24,false,"main",[24,25,26,27,28,29,30,31,32,33,34,35,36,37],"ai-agent","aiohttp","deepseek","llm","memory","multi-provider","python","skills","tailwindcss","token-usage","tools","vue","websocket","webui","2026-06-12 02:03:58","\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fbrand\u002Fog-cover.png\" alt=\"Emperor Agent — 皇帝密探 \u002F 大内总管\" width=\"780\" \u002F>\n\u003C\u002Fp>\n\n\u003Ch1 align=\"center\">Emperor Agent · 皇帝智能体\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\n  \u003Cb>本地运行的个人 Python 智能体\u003C\u002Fb>\u003Cbr\u002F>\n  多 Provider · 三层记忆 · 子代理派遣 · 流式 WebUI · 像素风纸质美术\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-chat-active.png\"    width=\"42\" alt=\"Chat\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-model-active.png\"   width=\"42\" alt=\"Model\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-tokens-active.png\"  width=\"42\" alt=\"Tokens\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-skills-active.png\"  width=\"42\" alt=\"Skills\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-tools-active.png\"   width=\"42\" alt=\"Tools\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-team-active.png\"    width=\"42\" alt=\"Team\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-scheduler-active.png\" width=\"42\" alt=\"Scheduler\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-configs-active.png\" width=\"42\" alt=\"Configs\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-mcp-active.png\"     width=\"42\" alt=\"MCP\" \u002F>\n  \u003Cimg src=\"assets\u002Fnav\u002Fnav-memory-active.png\"  width=\"42\" alt=\"Memory\" \u002F>\n\u003C\u002Fp>\n\n---\n\n> 用户下旨，主智能体（\"大内总管\"）统筹上下文、工具、记忆与子代理；把任务拆解、执行、校验后回禀。\n> 项目重点不是教学材料，而是一个可持续演进的个人 Agent 工程。\n\n---\n\n## ✨ 快速开始\n\n### 1. Python 后端\n\n```bash\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate                 # Windows: .venv\\Scripts\\activate\npip install -r requirements.txt\npip install -e .                           # 提供 emperor-agent 命令\n\nemperor-agent init                         # 终端向导配置 provider \u002F model \u002F WebUI 参数\n# 也可以使用：python agent.py init\n```\n\n### 2. CLI 模式\n\n```bash\nemperor-agent chat\n# 兼容入口：python agent.py\n```\n\n常用命令：\n\n```bash\nemperor-agent status\nemperor-agent doctor\nemperor-agent web --host 127.0.0.1 --port 8765 --open\n```\n\n启动时若发现热 `memory\u002Fhistory.jsonl` 中有活跃对话，会先调用模型做一次启动压缩。\n\n### 3. WebUI 模式\n\nWebUI 用 **Vue 3 + Vite + Tailwind + vue-router** 构建，**首次使用前必须先打包前端产物**：\n\n```bash\ncd webui\nnpm install\nnpm run build              # 产出 webui\u002Fdist\u002F\ncd ..\nemperor-agent web --open\n# 兼容入口：python webui.py\n# 浏览器打开 http:\u002F\u002F127.0.0.1:8765\n```\n\n后端 aiohttp 服务器把 `webui\u002Fdist\u002F` 作为 SPA 静态资源托管，并代理 `\u002Fapi\u002F*` 与 `\u002Fws`。前端开发模式可以另起 `cd webui && npm run dev`（Vite 5173），并配 proxy 指到 8765。\n\n### 4. 可选桌宠\n\n桌宠 companion 默认关闭。首次使用需单独安装 Electron 依赖，然后在 WebUI「配置文件」页开启，或用 CLI 管理：\n\n```bash\ncd desktop-pet\nnpm install\ncd ..\n\nemperor-agent pet status\nemperor-agent pet start\nemperor-agent pet stop\nemperor-agent pet restart\n```\n\n桌宠窗口位置写入 `memory\u002Fdesktop_pet\u002Fwindow.json`；运行时会用顶部气泡展示简短动作摘要，不显示用户原文或工具参数。若 Electron 依赖缺失，WebUI 只显示安装命令，不会影响主服务启动。\n\n### 5. 质量检查\n\n开发环境建议安装 dev 依赖：\n\n```bash\n.venv\u002Fbin\u002Fpython -m pip install -r requirements-dev.txt\nmake check\n```\n\n`make check` 会调用 `scripts\u002Fcheck.sh`，固定执行 `git diff --check`、Python 编译、`ruff`、`pytest` 和 WebUI build。需要同时查看本地配置、依赖、持久化状态时可运行：\n\n```bash\nemperor-agent doctor --dev\n```\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fempty\u002Fwelcome-hero.png\" alt=\"御书房 · 准备就绪\" width=\"640\" \u002F>\n\u003C\u002Fp>\n\n---\n\n## 🧧 首次启动会发生什么\n\n`memory\u002F`、`templates\u002FUSER.local.md`、`model_config.json`、`emperor.local.json`、`webui\u002Fdist\u002F`、`webui\u002Fnode_modules\u002F`、`desktop-pet\u002Fnode_modules\u002F` 都被 `.gitignore` 排除，所以新克隆的仓库是**干净**的。首次启动会自动从仓库内的初始化模板生成本地副本：\n\n| 私密文件 | 由谁生成 | 来源 |\n|---|---|---|\n| `memory\u002F`（整个目录） | `MemoryStore._ensure()` | `mkdir` |\n| `memory\u002FMEMORY.local.md` | `MemoryStore._ensure()` | 复制 `templates\u002Finit\u002FMEMORY.md` |\n| `memory\u002Fhistory.jsonl` | `HistoryLog` 首次启动 | 热对话日志，只保留活跃上下文 |\n| `memory\u002Fhistory_index.json` | `HistoryLog` 首次启动 | 热\u002F冷历史统计索引 |\n| `memory\u002Fhistory_archive\u002F*.jsonl.gz` | 压缩后自动生成 | 已压缩原始对话冷归档 |\n| `memory\u002Fversions\u002F` | 记忆写入或恢复时生成 | `MEMORY.local.md`、`USER.local.md` 与情景记忆的本地快照 |\n| `memory\u002Ftokens.jsonl` | `TokenTracker` 首次写入 | append |\n| `memory\u002Fcontrol\u002Fstate.json` | `ControlStore` 首次启动 | 当前 ask \u002F plan 等待状态 |\n| `memory\u002Fruntime\u002Fevents.jsonl` | `RuntimeEventStore` 首次启动 | Chat 行为事件热日志，只保留活跃 turn |\n| `memory\u002Fruntime\u002Findex.json` | `RuntimeEventStore` 首次启动 | Runtime 热\u002F冷统计索引 |\n| `memory\u002Fruntime\u002Farchive\u002F*.jsonl.gz` | Runtime 维护\u002F压缩后生成 | 已归档的旧行为事件 |\n| `memory\u002Fexternal\u002Fstate.json` | `ExternalBridgeStore` 首次写入 | 外部平台基础层的 seen \u002F pending \u002F outbox durable 状态 |\n| `memory\u002Fdesktop_pet\u002F` | 桌宠启动后生成 | 悬浮窗位置、pid 与最近错误 |\n| `memory\u002Fscheduler\u002Fjobs.json` | `SchedulerStore` 首次启动 | 本地持久定时任务热配置 |\n| `memory\u002Fscheduler\u002Faction.jsonl` | `SchedulerStore` 写操作 | 跨入口 action log，用于合并任务变更 |\n| `memory\u002Fscheduler\u002Faction.corrupt-*.jsonl` | `SchedulerStore` 发现坏 action 时生成 | 无法解析或未知 action 的隔离备份，diagnostics 可见 |\n| `memory\u002Fwatchlist.md` | `WatchlistStore` 首次启动 | 主动检查清单，供 Scheduler heartbeat 周期判断 |\n| `memory\u002Fwatchlist_state.json` | Watchlist 检查后写入 | 最近一次 skip\u002Frun 决策与模型信息 |\n| `templates\u002FUSER.local.md` | `AgentLoop._ensure_local_user_file()` | 复制 `templates\u002Finit\u002FUSER.md` |\n| `model_config.json` | `emperor-agent init` 或 WebUI `\u002Fmodel` 保存 | 本地私密模型配置 |\n| `emperor.local.json` | `emperor-agent init` 或桌宠开关 | 本地 CLI\u002FWebUI 偏好：host、port、openBrowser、desktopPet |\n| `webui\u002Fdist\u002F` | **需要手动** `cd webui && npm install && npm run build` | Vite 构建 |\n\n只要按\"快速开始\"两步走，引导链路就完整了，无需再手动建任何目录。\n\n---\n\n## ⚔️ 核心能力\n\n- **多轮对话** — 保留当前会话工作记忆；流式 delta 与工具事件实时显示。\n- **三层记忆** — 工作记忆（`history`）、情景记忆（`memory\u002FYYYY-MM-DD.md`）、长期记忆（`memory\u002FMEMORY.local.md`）协同运转。\n- **自动压缩** — 上下文超过阈值时把旧对话归档为情景记忆，并刷新长期记忆与用户档案。\n- **记忆版本回滚** — 长期记忆、用户档案与每日情景记忆写入前自动保存轻量快照；Memory 页和 `\u002Fmemory-log` \u002F `\u002Fmemory-restore` 可查看 diff 并恢复。\n- **多厂家 + 双模型路由** — DeepSeek、Anthropic、OpenAI、Azure OpenAI、AWS Bedrock、OpenRouter、DashScope（阿里云）、SiliconFlow、Ollama、vLLM、OpenAI Codex、GitHub Copilot 与自定义 OpenAI-compatible endpoint；每个 entry 同时配置主模型与次模型，简单任务自动走次模型，失败后升主模型一次。\n- **MCP 外部工具** — 通过 stdio 或 SSE 连接外部 MCP 服务器，自动发现工具并注册为 `mcp_{server}_{tool}`，与内置工具统一调度。\n- **流式 WebUI** — 网页聊天通过 WebSocket 接收 `message_delta`、`tool_call`、`tool_result`、`subagent_*` 等事件；活跃行为事件持久化到 `memory\u002Fruntime\u002Fevents.jsonl`，旧事件进入 `memory\u002Fruntime\u002Farchive\u002F*.jsonl.gz`，刷新或后端重启后按 seq 回放未压缩会话细节；Scheduler 主动 turn 会显示为中性的“定时任务触发”卡片，不伪装成用户消息；Chat 停止按钮与 `\u002Fstop` 会通过统一 active task registry 取消当前 turn \u002F Scheduler run \u002F Watchlist check。\n- **三模式权限与 Ask \u002F Plan 控制流** — 默认 `ask_before_edit` 会在危险或不确定动作前审批；`auto` 走最高自动权限；`plan` 只允许只读探索、提问和提交 PlanCard，批准或取消后恢复进入 Plan 前的模式。\n- **本地 Scheduler** — 持久保存 `at` \u002F `every` \u002F `cron` 任务，启动 WebUI 后后台 timer 自动恢复；支持触发本地主动 Agent turn、Team wake 与系统维护 heartbeat；Agent 可通过 `scheduler` 工具查看任务，创建\u002F修改\u002F删除\u002F手动运行长期任务会走权限审批。\n- **Watchlist Heartbeat** — `memory\u002Fwatchlist.md` 记录希望系统主动留意的事项；受保护的 `watchlist-check` 定期用次模型判断 `skip\u002Frun`，只有必要时才投递完整主动 turn。\n- **External Bridge 基础** — `agent\u002Fexternal\u002F` 提供通用外部平台适配骨架、入站去重、inbox\u002Foutbox 状态和 runtime 事件；当前不内置任何具体平台实现。外部消息只会汇入唯一主线，不创建多会话、不暴露 `session_id`。\n- **Token 统计** — 按日期、provider\u002Fmodel、使用种类（main_agent \u002F subagent \u002F memory_compaction）汇总，并按“输入缓存命中 \u002F 输入缓存未命中 \u002F 输出 \u002F 总 Token”展示。\n- **工具调用** — 命令执行、网页抓取、文件读写、Glob\u002FGrep 搜索、技能加载、todo 维护、子代理派遣。\n- **任务规划** — 内置 todolist，未完成时自动 nudge 模型继续执行。\n- **子代理派遣** — 把独立任务交给不同身份的子代理（独立 history、独立工具白名单），结果摘要回填主上下文，多个子代理可并发派遣。\n- **Agent Team** — Lead 可召入持久队友，队友拥有 `.team\u002F` 下独立 inbox、thread、状态和 WebUI 工作台；v1 采用“按消息唤醒”，不启动后台常驻轮询。\n- **技能系统** — 按需加载 `skills\u002F` 下的能力包，避免一开始塞满 system prompt。\n- **桌宠 Companion** — 可选 Electron 悬浮桌宠，开启后用 Clawd Tank 像素动画与顶部短气泡实时展示 Agent 思考、工具调用、子代理、Ask \u002F Plan、完成与错误状态；默认关闭，可在 WebUI 或 CLI 启停。\n- **历史保护** — `runner._pair_tool_calls` 保证 OpenAI 格式 history 中 assistant `tool_calls` 与 tool 消息严格配对，运行时异常或压缩切边都不会污染下一次请求。\n- **上下文治理** — 每次 LLM 调用前自动跑两步：单条工具结果硬截断（`_cap_tool_result`，留头尾，默认 8KB 上限）+ 旧大体积工具消息摘要化（`_shrink_old_tool_results`，最近 10 条保留原文，更早的替换为 `[shrunk] name → N chars omitted`）。让长对话从 8-10 轮稳定到 30+ 轮不撞 token 上限。\n- **LLM 错误恢复** — `step_async` 内置两个状态机：模型偶发空响应时自动注入 nudge 重试（≤2 次）；`finish_reason=\"length\" \u002F \"max_tokens\"` 时自动续写并拼接（≤3 次）。前端通过既有 `tool_error` 事件可见 `_empty_response` \u002F `_length_truncation` 提示。\n- **中断恢复 Checkpoint** — `MemoryStore.write_checkpoint` 在每次工具批次完成后把 history 原子写到 `memory\u002F_checkpoint.json`（gitignore），关 tab \u002F Ctrl-C \u002F 模型超时都不丢。`AgentLoop` 启动时 `read_checkpoint` 优先于 `history.jsonl` 未归档段恢复 in-memory history，再 `clear_checkpoint`；`_pair_tool_calls` 兜底处理任何 orphan tool_call。turn 正常落地时自动清理。\n- **对话附件** — Composer 支持点选 \u002F 拖拽上传图片（png \u002F jpeg \u002F webp \u002F gif，≤10MB）和文档（pdf \u002F json \u002F csv \u002F text \u002F markdown，≤25MB），单条消息至多 5 个。文件落盘到 `memory\u002Fattachments\u002FYYYY-MM\u002F{hash8}-{name}.{ext}`，PDF 与文本文档同步抽取 sidecar 文本（`pypdf`），发消息时按 OpenAI 多模态格式装配 user content：vision-capable entry 走 `image_url` block，否则替换为占位提示；文档总把抽出的文本内联进 prompt，并在末尾附落盘路径供 `read_file` 兜底读取。User 多模态消息在 cap\u002Fshrink 链上原样保留，不会被截断；WebUI 刷新恢复时只显示用户原话与附件卡片，不把模型侧提取文本 \u002F 落盘说明塞回气泡。\n- **视觉徽章 + 连通测试** — `\u002Fmodel` 编辑器内置「测试文本」「测试视觉」两个按钮：发一次最小 ping 或一张内置 2×2 红色 PNG 探测图，返回延迟、模型名、响应 sample。视觉测试通过会自动把 `entry.supports_vision = true` 持久化到 `model_config.json`，entry 列表立刻在该条目右侧显示视觉能力像素徽章；Composer 的附件按钮 tooltip 与图片上传路径都依据该徽章决定走视觉链还是占位文字。\n\n---\n\n## 🏯 项目结构\n\n```text\nagent.py                        CLI 兼容入口（转发到 agent.cli）\nwebui.py                        WebUI 启动入口\n\nagent\u002F\n├── loop.py                     主循环、组件装配、CLI 命令处理\n├── cli.py                      Python CLI 命令入口：init \u002F chat \u002F web \u002F status \u002F doctor \u002F pet\n├── onboarding.py               Rich + Questionary 初始化向导、doctor 检查、配置构造\n├── local_config.py             emperor.local.json 本地偏好读写\n├── model_router.py             主\u002F次模型路由：main_agent、memory、subagent、team 的模型选择与 fallback\n├── runner.py                   单轮执行编排、tool_use 循环、并发安全工具组合、tool_call 配对保护、上下文治理（cap\u002Fshrink）、空响应+截断重试\n├── runner_factory.py           子代理 \u002F Team runner 构造入口，集中套用模型路由快照与 fallback\n├── runner_model.py             ModelCaller：模型调用、流式 delta、次模型失败后升主模型一次\n├── memory.py                   三层记忆存储、未归档历史载入、中断恢复 checkpoint\n├── memory_versions.py          记忆快照、diff 与 restore，本地存储在 memory\u002Fversions\u002F\n├── attachments.py              附件落盘 + mime 校验 + PDF\u002F文本抽取 + 引用反查（LRU）\n├── compactor.py                历史压缩与长期记忆 \u002F 用户档案更新\n├── model_config.py             多 provider 模型配置读写\n├── context.py                  system prompt 组装（SOUL.md \u002F TOOL.md \u002F USER.md \u002F MEMORY \u002F Skills）\n├── control\u002F                    Ask \u002F Plan 会话控制：pending interaction、暂停\u002F恢复、Ask Guard\n├── permissions\u002F                Claude Code 风格三模式权限策略：ask_before_edit \u002F auto \u002F plan\n├── runtime\u002F                    WebUI 行为事件冷记录、event payload、seq replay\n├── scheduler\u002F                  本地长期自动运行中枢：job store \u002F timer service \u002F scheduler tool\n├── desktop_pet\u002F                可选 Electron 桌宠进程管理、pid\u002Fstate、偏好读写\n├── watchlist\u002F                  Watchlist heartbeat：本地清单、次模型 skip\u002Frun 决策、主动 turn 过滤\n├── external\u002F                   外部平台适配基础：adapter 抽象、统一消息模型、bridge service、durable store\n├── web\u002F                        aiohttp Web 后端：app\u002Fstate\u002Froutes\u002Fservices 分层\n│   ├── app.py                  aiohttp app 与中间件\n│   ├── container.py            Web 后端组合根，集中 startup \u002F cleanup 生命周期\n│   ├── state.py                共享依赖、广播、bootstrap glue\n│   ├── routes\u002F                 HTTP \u002F WS 路由注册，不拼复杂 payload\n│   └── services\u002F               chat \u002F model \u002F memory \u002F team 业务服务\n├── skills.py                   skill 加载与摘要生成\n├── telemetry.py                token 用量记录、压缩触发判断、按多维度统计\n├── webui.py                    兼容入口，导出 create_app() \u002F main()\n├── providers\u002F                  Provider 抽象层\n│   ├── base.py                 LLMProvider \u002F GenerationSettings \u002F ToolCallRequest \u002F LLMResponse\n│   ├── registry.py             ProviderSpec 表 + 名称查找\n│   ├── factory.py              ProviderSnapshot 与 create_provider()\n│   ├── anthropic_provider.py   Anthropic Messages API\n│   ├── bedrock_provider.py     AWS Bedrock\n│   └── openai_compat.py        OpenAI \u002F Azure \u002F Codex \u002F Copilot \u002F DeepSeek \u002F DashScope \u002F SiliconFlow \u002F Ollama \u002F vLLM \u002F OpenRouter\n├── subagents\u002F                  子代理 spec 与 registry\n├── team\u002F                       Agent Team：持久队友、MessageBus、TeamStore、team tools\n├── tools\u002F                      内建工具\n└── mcp\u002F                        MCP Client（外部工具连接）\n    ├── base.py \u002F registry.py \u002F schema.py\n    ├── shell.py \u002F web.py \u002F filesystem.py \u002F search.py\n    ├── skills.py \u002F todo.py \u002F dispatch.py\n\ntemplates\u002F\n├── SOUL.md                     智能体灵魂档案（已提交）\n├── TOOL.md                     工具使用约定（已提交）\n├── USER.local.md               本地个人用户档案（gitignore，首启自动生成）\n├── init\u002F\n│   ├── MEMORY.md               长期记忆初始化模板（已提交）\n│   └── USER.md                 用户档案初始化模板（已提交）\n├── agent\u002F                      主智能体 prompt 模板（compact_prompt.md \u002F identity.md \u002F skills_section.md ...）\n└── subagents\u002F                  各身份子代理模板\n\nskills\u002F                         技能包，每个目录一个 SKILL.md\nmemory\u002F                         运行期产物（gitignore，首启自动创建）\n├── MEMORY.local.md             长期记忆\n├── history.jsonl               热对话日志：只保存活跃上下文\n├── history_index.json          history 热\u002F冷统计索引\n├── history_archive\u002F            已压缩原始对话冷归档：YYYY-MM.jsonl.gz\n├── versions\u002F                   记忆版本快照：index.json + snapshots\u002F*.json\n├── tokens.jsonl                token 用量明细\n├── control\u002Fstate.json          Ask \u002F Plan 当前模式与等待交互状态\n├── runtime\u002Fevents.jsonl        Chat 行为流：工具、子代理、队友、Ask\u002FPlan、assistant_done\n├── scheduler\u002F\n│   ├── jobs.json               持久 Scheduler jobs：at \u002F every \u002F cron\n│   └── action.jsonl            append-only action log，用于跨入口写入合并\n├── watchlist.md                主动检查清单：每行一个希望系统定期留意的事项\n├── watchlist_state.json        最近一次 Watchlist skip\u002Frun 决策\n├── _checkpoint.json            未完成 turn 的 history 快照（中断恢复用）\n├── attachments\u002F                上传附件落盘：YYYY-MM\u002F{hash8}-{name}.{ext} + sidecar .txt\n└── YYYY-MM-DD.md               每日情景记忆\n\n.team\u002F                          Agent Team 运行期状态（gitignore，首次召入队友自动创建）\n├── config.json                  队友 roster：name \u002F role \u002F agent_type \u002F status\n├── inbox\u002F                       lead 与各 teammate 的 append-only JSONL inbox\n├── threads\u002F                     teammate 独立上下文\n├── checkpoints\u002F                 teammate 未完成 wake 的恢复点\n└── cursors\u002F                     各 actor inbox 已读游标\n\nmodel_config.json               本地私密模型配置（gitignore，由 init 或 \u002Fmodel 保存）\nemperor.local.json              本地 CLI\u002FWebUI 偏好配置（gitignore，由 init 创建）\nmodel_config.example.json       配置范例（已提交）\n\nassets\u002F                         WebUI 像素风素材库（已提交）\n├── desktop-pet\u002Fclawd-tank\u002F     可选 Electron 桌宠使用的 Clawd Tank MIT 动画资产\n├── nav\u002F                        导航图标（默认 + 激活）\n├── tools\u002F                      11 张工具事件图标\n├── actions\u002F                    10 张操作 \u002F 状态图标\n├── attachments\u002F                5 张附件类型图标（image \u002F pdf \u002F markdown \u002F text \u002F file）\n├── model\u002F                      4 张模型能力图标（text \u002F vision \u002F test ok \u002F test fail）\n├── avatars\u002F                    3 张角色头像\n├── brand\u002F                      logo \u002F favicon \u002F og-cover\n├── empty\u002F                      4 张空态插画\n└── textures\u002F                   2 张纸质 \u002F 印章纹理\n\nwebui\u002F                          前端工作台（Vue 3 + Vite + Tailwind + vue-router）\n├── package.json\n├── vite.config.ts \u002F tailwind.config.ts \u002F tsconfig.json\n├── public\u002F                     直接拷贝到产物的静态资源（favicon）\n├── dist\u002F                       Vite 构建产物（gitignore）\n└── src\u002F\n    ├── main.ts                 应用入口，加载 router + brandAssets 设置 favicon\n    ├── App.vue                 二列 shell：NavRail + RouterView，provide\u002Finject 全局状态\n    ├── router.ts               7 条一级路由 + SPA fallback\n    ├── assets.ts               assets\u002F PNG 引用聚合（brandAssets \u002F actionAssets \u002F attachmentIcon \u002F navIcon ...）\n    ├── styles.css              Tailwind base 与全局变量\n    ├── styles\u002F                 layout \u002F chat \u002F activity \u002F panels \u002F responsive 分层样式\n    ├── api\u002Fhttp.ts             fetch 封装\n    ├── commands.ts             斜杠命令解析\n    ├── types.ts                共用 TS 类型\n    ├── composables\u002F\n    │   ├── useBootstrap.ts     拉取 \u002Fapi\u002Fbootstrap、读写 skill \u002F config \u002F memory \u002F model\n    │   ├── useRuntime.ts       WebSocket 生命周期、发送消息、replay 调度\n    │   └── useAppContext.ts    provide \u002F inject 桥接，跨路由保活\n    ├── components\u002F\n    │   ├── layout\u002FNavRail.vue          左侧导航 + 状态卡 + 指标格 + 清屏按钮\n    │   ├── chat\u002F                       MessageList \u002F AssistantFlow \u002F ToolEvent \u002F SubagentTrail \u002F TodoPanel \u002F Composer \u002F PendingBar \u002F MarkdownBlock \u002F ExpandableText\n    │   └── panels\u002F                     ModelPanel \u002F TokensPanel \u002F SkillsPanel \u002F ToolsPanel \u002F ConfigPanel \u002F MemoryPanel\n    ├── runtime\u002F                        events \u002F reducer \u002F selectors \u002F persistence\n    └── views\u002F                          ChatView \u002F ModelView \u002F TokensView \u002F SkillsView \u002F ToolsView \u002F TeamView \u002F SchedulerView \u002F ConfigsView \u002F MemoryView\n\ndesktop-pet\u002F                    可选 Electron 桌宠 companion（需单独 npm install）\n├── package.json\n├── main.js \u002F preload.js\n└── renderer.html \u002F renderer.css \u002F renderer.js\n```\n\n---\n\n## 🗺️ WebUI 架构\n\n二列布局：左侧 **NavRail**（品牌、状态、4 项指标、10 项导航、清屏按钮）+ 右侧 **RouterView**。每个功能一个独立路由，主区独占全宽。\n\n| | 路由 | 视图 | 用途 |\n|---|---|---|---|\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-chat.png\"    width=\"28\" alt=\"\" \u002F> | `\u002Fchat`                    | ChatView    | 默认页，流式聊天，tool \u002F subagent 事件可视化 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-model.png\"   width=\"28\" alt=\"\" \u002F> | `\u002Fmodel`                   | ModelView   | Provider、model、apiBase、apiKey、temperature、maxTokens、reasoningEffort、上下文窗口 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-tokens.png\"  width=\"28\" alt=\"\" \u002F> | `\u002Ftokens`                  | TokensView  | 总量 + 按 model \u002F 用途 \u002F 日期 \u002F KV Cache 统计 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-skills.png\"  width=\"28\" alt=\"\" \u002F> | `\u002Fskills` `\u002Fskills\u002F:name`  | SkillsView  | 列表 + SKILL.md 编辑器 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-tools.png\"   width=\"28\" alt=\"\" \u002F> | `\u002Ftools`                   | ToolsView   | 注册的工具与 MCP 工具一览 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-team.png\"    width=\"28\" alt=\"\" \u002F> | `\u002Fteam`                    | TeamView    | Agent Team 队友、Inbox、执行轨迹 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-scheduler.png\" width=\"28\" alt=\"\" \u002F> | `\u002Fscheduler`             | SchedulerView | 本地长期任务、运行历史与手动调度 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-configs.png\" width=\"28\" alt=\"\" \u002F> | `\u002Fconfigs` `\u002Fconfigs\u002F:path(.*)` | ConfigsView | TOOL.md \u002F USER.md 编辑器 |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-mcp.png\"     width=\"28\" alt=\"\" \u002F> | `\u002Fmcp`                     | McpView     | MCP 服务器配置（JSON 编辑器 + 已加载工具列表） |\n| \u003Cimg src=\"assets\u002Fnav\u002Fnav-memory.png\"  width=\"28\" alt=\"\" \u002F> | `\u002Fmemory`                  | MemoryView  | 长期记忆、今日情景、历史归档列表 |\n\n**跨路由保活**：`useBootstrap` \u002F `useRuntime` 在 `App.vue` 顶层执行，通过 `provide()` 注入；`\u003Crouter-view>` 用 `\u003Ckeep-alive>` 包住，切换路由时 WebSocket、消息流、Composer 草稿、滚动位置都不丢。\n\n**SPA fallback**：`agent\u002Fweb\u002Froutes\u002Fchat.py` 注册静态处理器，对未匹配 `\u002Fapi\u002F*` 与 `\u002Fws` 的路径回退到 `index.html`，刷新 `\u002Fskills\u002Ffoo` 等深层路由不会 404。\n\n**素材管线**：所有 PNG 放在仓库根 `assets\u002F` 下，`webui\u002Fsrc\u002Fassets.ts` 用 `new URL('..\u002F..\u002Fassets\u002F...', import.meta.url)` 引用。Vite 会在 `npm run build` 时把它们指纹哈希后输出到 `webui\u002Fdist\u002Fassets\u002F`。新增图标只需放进对应子目录并在 `assets.ts` 中加一行。\n\n**桌宠素材署名**：可选桌宠使用\n[`marciogranzotto\u002Fclawd-tank`](https:\u002F\u002Fgithub.com\u002Fmarciogranzotto\u002Fclawd-tank)\n在 `6f0093b40e3714db24615409bf31c351f66114f7` 的 MIT SVG 动画资产。仓库只 vendored\n`assets\u002Fdesktop-pet\u002Fclawd-tank\u002F` 下的视觉资源、`LICENSE` 与 `NOTICE.md`，不包含上游 ESP32 固件、SDL2 simulator、BLE daemon 或 menu bar app。\n\n---\n\n## ⚙️ 模型配置\n\n模型配置使用本地 `model_config.json`，允许明文保存 API key。该文件已加入 `.gitignore`。推荐用 `emperor-agent init` 打开终端初始化向导，或在 WebUI `\u002Fmodel` 页编辑；手动复制 `model_config.example.json` 仍然兼容。\n\n默认配置摘要：\n\n```json\n{\n  \"agents\": {\n    \"defaults\": {\n      \"model\": \"deepseek-work\",\n      \"provider\": \"deepseek\",\n      \"maxTokens\": 20000,\n      \"temperature\": 0.1,\n      \"reasoningEffort\": null,\n      \"contextWindowTokens\": 200000\n    }\n  },\n  \"models\": [\n    {\n      \"name\": \"deepseek-work\",\n      \"provider\": \"deepseek\",\n      \"mainModelId\": \"deepseek-reasoner\",\n      \"secondaryModelId\": \"deepseek-chat\",\n      \"apiKey\": \"\",\n      \"apiBase\": null\n    }\n  ],\n  \"providers\": {\n    \"deepseek\":   { \"apiKey\": \"\", \"apiBase\": \"https:\u002F\u002Fapi.deepseek.com\" },\n    \"anthropic\":  { \"apiKey\": \"\", \"apiBase\": null },\n    \"openai\":     { \"apiKey\": \"\", \"apiBase\": null },\n    \"openrouter\": { \"apiKey\": \"\", \"apiBase\": \"https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\" },\n    \"dashscope\":  { \"apiKey\": \"\", \"apiBase\": \"https:\u002F\u002Fdashscope.aliyuncs.com\u002Fcompatible-mode\u002Fv1\" },\n    \"ollama\":     { \"apiKey\": \"\", \"apiBase\": \"http:\u002F\u002Flocalhost:11434\u002Fv1\" },\n    \"vllm\":       { \"apiKey\": \"\", \"apiBase\": \"http:\u002F\u002Flocalhost:8000\u002Fv1\" },\n    \"...\": \"等等\"\n  }\n}\n```\n\nWebUI `\u002Fmodel` 页可以编辑全部字段并热更新。一个模型条目共享同一套 `provider \u002F apiKey \u002F apiBase`，但必须同时填写 `mainModelId` 与 `secondaryModelId`：主模型负责主 Agent、复杂决策和写入型队友；次模型负责记忆压缩、轻量子代理和只读\u002F核验型队友。旧配置里的 `id` 会被兼容读取为 `mainModelId`，启动与普通运行仍可在缺少次模型时降级主模型；但再次保存时必须补齐 `secondaryModelId`。\n\n`\u002Fapi\u002Fmodel-test` 的语义更严格：显式测试 `role=secondary` 时，如果 entry 没有 `secondaryModelId`，后端会返回 `400`，前端也会禁用“测试次模型”按钮并提示先补齐；这条测试路径不会偷偷 fallback 到主模型。视觉测试始终绑定主模型，因为 `supportsVision` 表示主模型附件能力。\n\n终端初始化向导只覆盖核心配置：LLM provider、entry name\u002Flabel、API key、API base、主\u002F次模型、maxTokens、temperature、contextWindowTokens、reasoningEffort、WebUI host\u002Fport\u002FopenBrowser 和默认权限模式。API key 输入为空时会保留已有值，摘要只显示脱敏尾号。向导不会配置 MCP、Scheduler jobs、Team roster 或 External Bridge。\n\n---\n\n## 🧠 记忆系统\n\n\u003Ctable>\n\u003Ctr>\n\u003Ctd width=\"180\" align=\"center\">\n  \u003Cimg src=\"assets\u002Fempty\u002Fempty-memory.png\" alt=\"记忆层\" width=\"160\" \u002F>\n\u003C\u002Ftd>\n\u003Ctd>\n\n| 层 | 载体 | 写入时机 | 读取方式 |\n|----|------|----------|----------|\n| 工作记忆 | `history` 列表（内存） | 每轮对话追加 | 全量传给模型 |\n| 情景记忆 | `memory\u002FYYYY-MM-DD.md` | 压缩触发时生成 | 按需检索 |\n| 长期记忆模板 | `templates\u002Finit\u002FMEMORY.md` | 仓库格式 | 首启自动复制到本地 |\n| 长期记忆 | `memory\u002FMEMORY.local.md` | 压缩或启动归档时更新 | 每轮注入 system prompt |\n| 用户档案模板 | `templates\u002Finit\u002FUSER.md` | 仓库格式 | 首启自动复制到本地 |\n| 用户档案 | `templates\u002FUSER.local.md` | 压缩或 WebUI Config 编辑时更新 | 每轮注入 system prompt |\n| 活跃原始历史 | `memory\u002Fhistory.jsonl` | 每轮 user\u002Fassistant 追加；压缩后原子重写 | 启动时直接载入热日志 |\n| 原始历史冷归档 | `memory\u002Fhistory_archive\u002FYYYY-MM.jsonl.gz` | 压缩完成后写入 | 默认不注入上下文，保留审计\u002F备份 |\n| 行为事件热记录 | `memory\u002Fruntime\u002Fevents.jsonl` | 每个活跃 WebUI runtime 事件 append；压缩\u002F维护后旧事件轮转 | Chat 刷新后重放未压缩 turn 的工具\u002F队友\u002FAsk\u002FPlan 细节 |\n| 行为事件冷归档 | `memory\u002Fruntime\u002Farchive\u002FYYYY-MM.jsonl.gz` | Runtime 维护或压缩后生成 | 默认不重放，保留审计\u002F备份 |\n\n\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftable>\n\nToken 账本：`memory\u002Ftokens.jsonl` 每行记录一次模型调用，字段包括 `input`、`output`、`cache_read`、`cache_create`、`provider`、`model`、`usage_type`、`model_role`，并在可用时记录 `route_reason`、`estimated_input_tokens`、`route_estimated_tokens`、`used_fallback`、`fallback_reason`，用于排查主\u002F次模型路由和 fallback。Anthropic 的 `cache_read_input_tokens` \u002F `cache_creation_input_tokens` 会归一化到 `cache_read` \u002F `cache_create`；OpenAI-compatible 的 `prompt_tokens_details.cached_tokens` 会归一化到 `cache_read`，并从普通 `input` 中扣除以避免重复统计。WebUI Token 页把这些字段派生为 4 个主口径：输入缓存命中 = `cache_read`，输入缓存未命中 = `input + cache_create`，输出 = `output`，总 Token = 前三者合计；大数字按 K \u002F W \u002F M 自动缩写，完整值保留在悬停提示里。\n\n压缩触发：`TokenTracker.should_compact(max_context, threshold=0.7)` —— 上一次调用的 input + cache_read + cache_create 估算超过窗口的 70% 时，下一轮 step 结束后压缩 `history[:-K]`（K 默认 10），保留最近 K 条。压缩输出必须包含 `episode`、`updated_memory`、`updated_user` 三段 XML；缺失时会做一次 repair retry，仍失败则保留原记忆和热历史，并把诊断写入 `memory\u002Fcompact_diagnostics.jsonl`，避免半更新。压缩完成后，`history.jsonl` 会被重写为热日志；旧原始行进入 `memory\u002Fhistory_archive\u002FYYYY-MM.jsonl.gz`，`memory\u002Fhistory_index.json` 记录热日志大小、归档文件数和最近归档时间。\n\nChat 页面只展示未压缩 turn 的完整行为细节；压缩前的工具调用、队友轨迹、Ask\u002FPlan 卡片会从热 `memory\u002Fruntime\u002Fevents.jsonl` 转入 `memory\u002Fruntime\u002Farchive\u002FYYYY-MM.jsonl.gz`，后续可用于审计或历史浏览页。\n\n`memory\u002F`、`.team\u002F`、`templates\u002FUSER.local.md`、`model_config.json`、`emperor.local.json` 都是本地私密文件，**不要提交**。\n\n---\n\n## 🛠️ 内置工具\n\n\u003Ctable>\n\u003Ctr>\n\u003Ctd width=\"180\" align=\"center\">\n  \u003Cimg src=\"assets\u002Fempty\u002Fempty-tools.png\" alt=\"工具箱\" width=\"160\" \u002F>\n\u003C\u002Ftd>\n\u003Ctd>\n\n| | 工具 | 作用 | 并发安全 |\n|---|------|------|----------|\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-shell.png\"    width=\"20\" alt=\"\" \u002F> | `run_command`             | 执行 shell 命令               | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-web.png\"      width=\"20\" alt=\"\" \u002F> | `web_fetch`               | 抓取 URL 内容                 | ✓ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-read.png\"     width=\"20\" alt=\"\" \u002F> | `read_file`               | 工作区文件读取                | ✓ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-write.png\"    width=\"20\" alt=\"\" \u002F> | `write_file`              | 工作区文件写入                | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-edit.png\"     width=\"20\" alt=\"\" \u002F> | `edit_file`               | 局部编辑                      | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-glob.png\"     width=\"20\" alt=\"\" \u002F> | `glob`                    | 工作区路径匹配                | ✓ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-grep.png\"     width=\"20\" alt=\"\" \u002F> | `grep`                    | 工作区内容搜索                | ✓ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-skill.png\"    width=\"20\" alt=\"\" \u002F> | `load_skill`              | 按需加载技能                  | ✓ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-todo.png\"     width=\"20\" alt=\"\" \u002F> | `update_todos`            | 维护当前任务列表              | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-todo.png\"     width=\"20\" alt=\"\" \u002F> | `ask_user`                | 暂停当前 turn，向用户提结构化问题 | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-todo.png\"     width=\"20\" alt=\"\" \u002F> | `propose_plan`            | Plan 模式下提交计划草案并等待评论\u002F批准 | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-todo.png\"     width=\"20\" alt=\"\" \u002F> | `scheduler`               | 查看\u002F管理本地长期定时任务         | ✗（`list` 走权限层放行） |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `dispatch_subagent`       | 派遣子代理独立办差            | ✓（多个子代理可并发） |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `spawn_teammate`          | 召入持久队友并可立即派任务    | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `list_teammates`          | 查看 Agent Team 队友状态      | ✓ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `send_message`            | 向 lead \u002F teammate inbox 留言 | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `read_inbox`              | 读取当前 actor 的 inbox       | ✗（默认会移动已读游标） |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `broadcast`               | 向多个队友广播并可逐个唤醒    | ✗ |\n| \u003Cimg src=\"assets\u002Ftools\u002Ftool-subagent.png\" width=\"20\" alt=\"\" \u002F> | `shutdown_teammate`       | 关闭队友，保留历史记录        | ✗ |\n\n\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftable>\n\n`AgentRunner` 会把同一帧内多个并发安全的工具调用合并为 `asyncio.gather`，按原顺序回填结果。\n\n`dispatch_subagent` 的旧参数 `agent_type` \u002F `task` \u002F `purpose` 继续兼容；新派遣建议补充 `expected_output`、`evidence_required`、`scope_limit`，子代理最终回禀统一包含结论、证据、风险和建议下一步。\n\n附件文档落盘后，主代理也可以主动 `read_file memory\u002Fattachments\u002F...\u002Ffoo.pdf.txt` 读取被抽出来的 sidecar 文本——这是给非视觉模型保留的一条兜底链路。\n\n---\n\n## 🧭 权限模式与 Ask \u002F Plan 控制流\n\n`agent\u002Fcontrol\u002F` 负责可暂停交互，`agent\u002Fpermissions\u002F` 负责 Claude Code 风格的执行模式判断。状态持久化在 `memory\u002Fcontrol\u002Fstate.json`，配合 `memory\u002F_checkpoint.json` 与 `memory\u002Fruntime\u002Fevents.jsonl`，刷新页面或后端重启后仍能恢复 pending interaction 与对应 Chat 卡片。\n\n| 模式 | 入口 | 行为 |\n|---|---|---|\n| `ask_before_edit` | 默认、`\u002Fmode ask` | 读操作直接执行，普通编辑可执行；危险、不确定、破坏性或高影响操作先进入 AskCard 审批 |\n| `auto` | `\u002Fmode auto` 或 Composer 模式选择器 | 工具层不主动审批；仍保留路径安全、schema 校验和工具自身异常保护 |\n| `plan` | `\u002Fmode plan`、`\u002Fplan on` 或 Composer 模式选择器 | 只读探索 + `ask_user` + `propose_plan`；写文件、命令执行、Team 写操作、子代理派遣不可用 |\n\n| 机制 | 入口 | 行为 |\n|---|---|---|\n| Ask | `ask_user(questions, context?)` | Agent 提出 1-3 个结构化问题，每题 2-4 个选项，可带自由补充；Ask Guard 在高影响歧义下会阻止写操作\u002F最终答复并强制进入 Ask；用户回答后把结构化答案注入 history 并继续当前任务 |\n| Plan | `\u002Fplan on` 或 WebUI Chat Composer 模式选择器 | 进入硬门禁模式：模型只看到只读工具 + `ask_user` + `propose_plan`；普通最终文字会被 Runner 包装成 PlanCard 并暂停 |\n| Approve | PlanCard \u002F CLI `approve` | 批准后自动恢复进入 Plan 前的模式（`ask_before_edit` 或 `auto`），并把批准事件作为用户反馈注入 history，随后继续执行 |\n\nPlan 模式不是提示词约束，而是工具层 + 输出层硬门禁：`write_file`、`edit_file`、`run_command`、`dispatch_subagent`、Team 写操作等不会暴露给模型；`scheduler` 在 Plan 模式下只允许 `action=list` 用于了解现有长期任务，创建\u002F修改\u002F删除\u002F运行任务必须等计划批准后再执行。如果模型尝试调用未授权工具，Runner 会返回明确的拒绝结果；如果模型直接普通回复，Runner 会生成 `plan_draft` 并暂停，确保用户必须先看到 PlanCard。执行中或已有 pending Ask\u002FPlan 时，`POST \u002Fapi\u002Fcontrol\u002Fmode` 会返回 409，避免中途切换模式导致工具策略漂移。\n\nWebUI 手动点击被视为用户直接操作，不再二次弹 Agent AskCard；但执行型 mutation 仍会走统一 Web guard：存在 pending Ask\u002FPlan 时拒绝 Scheduler run、Team wake、Team message wake、桌宠启停等操作，Plan 模式下拒绝新增\u002F修改\u002F运行长期任务、Team 写操作与桌宠开关，防止绕过计划门禁。\n\nAsk Guard 当前采用“高影响歧义强制”策略：大范围工程化\u002F重构\u002FUI 取舍、提交推送、删除覆盖、发布部署、安全\u002F权限\u002F成本边界不清时，会要求先 `ask_user`；低风险、目标明确的小任务不打扰；`PLEASE IMPLEMENT THIS PLAN` 或决策完整的计划会跳过主动提问。\n\n权限审批是“一次性同参授权”：用户允许后，同一个工具名 + 参数组合下一次执行会放行一次；用户拒绝后，同参操作下一次会返回明确拒绝，避免审批循环。\n\nScheduler 相关 HTTP API 已接入 Web 后端：`GET \u002Fapi\u002Fscheduler`、`POST \u002Fapi\u002Fscheduler\u002Fjobs`、`PATCH \u002Fapi\u002Fscheduler\u002Fjobs\u002F{id}`、`POST \u002Fapi\u002Fscheduler\u002Fjobs\u002F{id}\u002Frun|pause|resume`、`DELETE \u002Fapi\u002Fscheduler\u002Fjobs\u002F{id}`。运行事件会通过 `scheduler_job_update`、`scheduler_run_start`、`scheduler_run_done`、`scheduler_run_error`、`scheduler_run_cancelled` 进入 runtime 事件流。`memory\u002Fscheduler\u002Faction.jsonl` 合并时若遇到坏行或未知 action，会把坏记录隔离到 `action.corrupt-*.jsonl` 并通过 scheduler diagnostics 暴露，不静默丢弃。\n\n当前运行中的 Chat turn、Scheduler run、Watchlist 手动检查会登记到进程内 active task registry；`POST \u002Fapi\u002Fruntime\u002Fstop`、WebUI 停止按钮与 `\u002Fstop` 共用这一层取消任务，并发出 `runtime_task_cancelled` 事件。刷新后未完成轨迹仍由 runtime event log 还原，取消后的工具 \u002F 子代理段会显示为已中断。\n\n当前可执行 payload：`agent_turn` 会作为“定时任务触发”的主动 turn；`deliver=true` 时写入当前 Chat runtime，`deliver=false` 时只作为后台运行写入 Scheduler run history，不插入当前对话流；`team_wake` 会向目标 teammate 写入 task 消息并唤醒，`deliver=false` 时不把 Team 运行事件挂到当前 Chat；`system_event` 仅允许系统代码注册，普通 API \u002F tool 创建会被拒绝。\n\nScheduler 投递到 Chat 的主动 turn 仍使用主线 `user_message` 事件，但会带 `source=\"scheduler\"` 和 `{ jobId, jobName }` 元数据；前端会把它渲染为“定时任务触发”时间线卡片，而不是右侧用户圣旨气泡。`scheduler_run_done`、`scheduler_run_cancelled` 和 `scheduler_job_update` 的完成提示会短暂显示后自动消失；`scheduler_run_error` 会保持可见，直到下一次 pending 状态覆盖。\n\nWebUI 启动时会自动登记 5 个受保护系统任务：`memory-maintenance`、`runtime-maintenance`、`team-stale-recovery`、`token-ledger-maintenance`、`watchlist-check`。它们在 Scheduler 页可见、可暂停\u002F恢复\u002F手动运行，但不能删除；Memory 页会显示维护任务状态，并提供 `memory\u002Fwatchlist.md` 编辑与手动检查入口。\n\nWatchlist 不是后台常驻聊天，而是“先过滤再投递”：`watchlist-check` 会读取 `memory\u002Fwatchlist.md` 的有效条目，用次模型判断是否需要运行；`skip` 只记录状态，`run` 才把可投递消息包装成一次本地主动 `agent_turn`。这样可以保留长期自主性，同时避免清单项每次心跳都污染 Chat。\n\nExternal Bridge 当前是基础设施层：`ExternalAdapter` 负责未来平台收发，`ExternalBridgeService` 负责入站去重、忙碌\u002FAsk\u002FPlan 时排队、outbox 状态与 `external_*` runtime 事件。Bridge 状态会持久化到 `memory\u002Fexternal\u002Fstate.json`，重启后恢复 `seen`、`pending`、`outbox` 与最近错误；坏状态文件会保留为 `state.json.corrupt-*` 并在 diagnostics 中可见。它不提供真实飞书\u002FSlack\u002FTelegram adapter，也不会生成平台独立历史；外部消息进入模型前会带 `[EXTERNAL_MESSAGE]` 来源上下文，然后作为一次普通主线 turn 运行。\n\n本地诊断统一入口：\n\n| 类型 | 名称 |\n|---|---|\n| HTTP | `GET \u002Fapi\u002Fdiagnostics` |\n| CLI | `emperor-agent doctor --dev` |\n\n`\u002Fapi\u002Fdiagnostics` 返回 model config、local config、scheduler store、runtime event store、external bridge、desktop pet 和依赖提示。`\u002Fapi\u002F*` 未处理异常只返回安全错误与 `errorId`，完整异常细节写入日志，不直接暴露到浏览器。\n\nWebUI 相关接口：\n\n| 类型 | 名称 |\n|---|---|\n| HTTP | `GET \u002Fapi\u002Fcontrol`、`POST \u002Fapi\u002Fcontrol\u002Fmode`、`POST \u002Fapi\u002Fcontrol\u002Finteractions\u002F{id}\u002Fcancel` |\n| HTTP | `GET \u002Fapi\u002Fexternal`、`GET \u002Fapi\u002Fdiagnostics` |\n| WS client message | `interaction_answer`、`plan_comment`、`plan_approve`、`interaction_cancel` |\n| WS server event | `user_message`（可带 `source=\"scheduler\"`）、`scheduler_*`、`runtime_task_cancelled`、`control_mode_update`、`ask_request`、`ask_answered`、`plan_draft`、`plan_comment_added`、`plan_approved`、`interaction_cancelled`、`turn_paused`、`external_inbound`、`external_queued`、`external_outbound_*` |\n\nCLI 也支持 `\u002Fmode ask|auto|plan|status` 与 `\u002Fplan on|off|status`。Ask pending 时会在终端打印问题并读取答案；Plan pending 时支持 `approve`、`comment \u003C内容>`、`cancel`。\n\n---\n\n## 🔌 MCP 外部工具\n\nEmperor Agent 内置 MCP Client，可连接外部 MCP 服务器扩展工具能力。\n\n### 配置方式\n\n创建 `mcp_config.json`（与 `model_config.json` 同级）：\n\n```json\n{\n  \"servers\": {\n    \"filesystem\": {\n      \"transport\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@modelcontextprotocol\u002Fserver-filesystem\", \"\u002Fworkspace\"],\n      \"enabled\": true,\n      \"tool_overrides\": {\n        \"read_file\": { \"read_only\": true }\n      }\n    },\n    \"fetch\": {\n      \"transport\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-server-fetch\"],\n      \"enabled\": true\n    },\n    \"remote\": {\n      \"transport\": \"sse\",\n      \"url\": \"http:\u002F\u002Flocalhost:3001\u002Fsse\",\n      \"headers\": {},\n      \"enabled\": false\n    }\n  },\n  \"defaults\": {\n    \"read_only\": false,\n    \"exclusive\": false\n  }\n}\n```\n\n| 字段 | 说明 |\n|---|---|\n| `transport` | `stdio`（本地命令）或 `sse`（HTTP 服务器） |\n| `command` \u002F `args` | stdio 模式下执行的命令 |\n| `url` \u002F `headers` | sse 模式下服务器地址与请求头 |\n| `enabled` | 是否启用该服务器 |\n| `tool_overrides` | 按工具名覆盖 `read_only` \u002F `exclusive` |\n| `env` | 额外环境变量（支持 `${ENV_VAR}` 插值） |\n\n### WebUI 管理\n\n`\u002Fmcp` 页面提供 JSON 配置编辑器 + 已加载 MCP 工具列表。保存后自动关闭旧连接、重新发现工具并注册。\n\n### 安全提示\n\n- MCP 子进程**仅继承白名单环境变量**（PATH、HOME、USER 等），API key 等敏感变量不会泄露给第三方 MCP 服务器\n- 工具名格式为 `mcp_{server}_{tool}`，与内置工具隔离\n- 单服务器连接失败不影响其他服务器和内置工具\n\n---\n\n## 👥 角色与子代理\n\n主智能体（\"大内总管\"）服侍用户（\"皇上\"），可派遣\"小太监\"作为子代理处理独立差事——子代理拥有独立 `history` 与受限工具白名单，办完只把摘要返回主上下文。WebUI 会通过 `subagent_*` 事件实时显示子代理的进度（delta、tool_call、result、done \u002F error）。轻量只读\u002F核验子代理默认走当前 entry 的 `secondaryModelId`，写入型 `neiguan_yingzao` 仍走 `mainModelId`；次模型失败时自动升主模型重试一次。\n\nAgent Team 是更长期的协作层：Lead 使用 `spawn_teammate` 创建队友，队友状态写入 `.team\u002Fconfig.json`，通信走 `.team\u002Finbox\u002F*.jsonl`，完整上下文保存在 `.team\u002Fthreads\u002F*.json`。v1 不启动后台常驻线程；`send_message(..., wake=true)` \u002F `broadcast(..., wake=true)` 会按消息唤醒队友执行一次。队友按 agent_type 复用同一套主\u002F次模型路由，token 用量记录为 `usage_type=team:\u003Cname>` 并带 `model_role`，主上下文只接收摘要，避免长期协作污染 Lead 的 history。\n\nrole 默认映射：\n\n- `coder` → `neiguan_yingzao`\n- `reviewer` → `shangbao_dianbu`\n- `researcher` → `dongchang_tanshi`\n- `reader` → `sili_suitang`\n- `runner` → `xiaohuangmen`\n- 未知 role → `sili_suitang`\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Favatars\u002Favatar-emperor.png\"  width=\"140\" alt=\"皇上\"     \u002F>&nbsp;&nbsp;\n  \u003Cimg src=\"assets\u002Favatars\u002Favatar-eunuch.png\"   width=\"140\" alt=\"大内总管\" \u002F>&nbsp;&nbsp;\n  \u003Cimg src=\"assets\u002Favatars\u002Favatar-subagent.png\" width=\"140\" alt=\"小太监\"   \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cb>皇上\u003C\u002Fb>（用户）&nbsp;&nbsp;·&nbsp;&nbsp;\n  \u003Cb>大内总管\u003C\u002Fb>（主智能体）&nbsp;&nbsp;·&nbsp;&nbsp;\n  \u003Cb>小太监\u003C\u002Fb>（子代理）\n\u003C\u002Fp>\n\n当前内置子代理身份：\n\n- `xiaohuangmen` — 轻量只读，适合快速确认和短命令。\n- `sili_suitang` — 只读文书，适合阅读代码与整理文档。\n- `dongchang_tanshi` — 只读查访，适合网页抓取和资料探索。\n- `shangbao_dianbu` — 只读核验，适合清点文件、校对清单、检查遗漏。\n- `neiguan_yingzao` — 可读写、可执行命令，适合修改文件、搭建工程、跑验收。\n\n`researcher` 与 `general` 作为兼容别名保留，分别映射到 `dongchang_tanshi` 与 `neiguan_yingzao`。\n\n---\n\n## 📜 技能系统\n\n\u003Ctable>\n\u003Ctr>\n\u003Ctd width=\"180\" align=\"center\">\n  \u003Cimg src=\"assets\u002Fempty\u002Fempty-skills.png\" alt=\"技能卷轴\" width=\"160\" \u002F>\n\u003C\u002Ftd>\n\u003Ctd>\n\n`skills\u002F{name}\u002FSKILL.md` 用 YAML frontmatter 描述触发条件，Markdown 写能力说明。主智能体在需要时通过 `load_skill` 拉取，避免一开始塞满 system prompt；提示词模板不得要求绕过 `load_skill` 直接读取 `SKILL.md`。\n\nWebUI Composer 的 `\u002F` 菜单会同时列出系统命令和当前项目全部 Skill。点击 Skill 会补全 `\u002F\u003Cskill-name> ` 前缀；输入 `\u002F\u003Cskill-name> 任务` 或 `\u002F\u003Cskill-name>-skill 任务` 会作为结构化 `requested_skills` 随消息发送，后端会在本轮强制预加载 Skill 内容。Composer 和 Chat 气泡中，非系统命令的 `\u002Fskill` 前缀会以特殊颜色字体高亮；聊天气泡与刷新恢复仍只显示你的原始输入。\n\n当前内置技能：\n\n- `clawhub` — 技能库搜寻与安装\n- `ddg-web-search` — DuckDuckGo 搜索\n- `github` — GitHub CLI 交互\n- `skill-creator` — 创建或更新技能\n- `summarize` — URL、播客、文件总结\n- `weather` — 天气查询\n\nWebUI `\u002Fskills` 页可以新建、编辑、保存。保存后会触发 `loop.refresh_runtime_context()` 重建 system prompt。\n\n\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftable>\n\n---\n\n## 💬 CLI \u002F WebUI 斜杠命令\n\nCLI 与 WebUI Composer 共用一批斜杠命令；部分工作台页面命令只在 WebUI 生效：\n\n| 命令 | 说明 |\n|---|---|\n| `\u002Fhelp` | 列出所有命令 |\n| `\u002Fstatus` | 当前 provider \u002F model \u002F token \u002F 工具与技能数 |\n| `\u002Finit` | CLI 内打开终端初始化向导 |\n| `\u002Fmodel` | 当前模型详细信息（WebUI） |\n| `\u002Ftokens` | Token 用量统计（WebUI，多维度，含 KV Cache） |\n| `\u002Ftools` | 工具列表（WebUI） |\n| `\u002Fskills` | 技能列表（WebUI） |\n| `\u002Fconfig` | CLI 中同 `\u002Finit`；WebUI 中进入可编辑配置文件 |\n| `\u002Fmemory` | 记忆状态摘要（WebUI） |\n| `\u002Fmemory-log` | 最近记忆版本快照（WebUI） |\n| `\u002Fmemory-restore \u003Cid>` | 恢复指定记忆版本（WebUI） |\n| `\u002Fplan on|off|status` | 开关或查看 Plan 模式 |\n| `\u002Fmode ask|auto|plan|status` | 切换或查看三模式权限层 |\n| `\u002Fstop` | 停止当前运行中的 turn \u002F Scheduler 任务 \u002F Watchlist 手动检查（WebUI） |\n| `\u002Fcompact` | 立即压缩未归档对话（WebUI） |\n| `\u002Fclear` | 清空当前屏幕\u002F上下文（不删 memory） |\n| `\u002Freload` | 重新拉取 bootstrap（WebUI） |\n| `\u002Fexit` | 退出 CLI |\n\nWebUI 还支持 Skill 快捷调用：`\u002F\u003Cskill-name> 任务` 会强制本轮使用指定 Skill；若 Skill 名与系统命令冲突，可用 `\u002F\u003Cskill-name>-skill 任务`。Composer 与 Chat 中的 `\u002Fskill` 前缀会用特殊颜色字体区分，正文仍按普通文字显示。\n\n---\n\n## 🌐 环境变量\n\n模型 API Key 默认从 `model_config.json` 读取。`.env` 仍可保留给你自己的工具或脚本使用，但主 Agent 不再依赖任何 `*_API_KEY` 环境变量。`emperor-agent doctor` 会检查模型条目、WebUI 构建产物、本地偏好文件和 CLI 依赖；`emperor-agent doctor --dev` 会额外运行统一质量门禁。\n\n---\n\n## 🤝 协作约定\n\n- **不要提交**：`memory\u002F`、`.team\u002F`、`templates\u002FUSER.local.md`、`model_config.json`、`emperor.local.json`、`.env`、`webui\u002Fdist\u002F`、`webui\u002Fnode_modules\u002F`、`desktop-pet\u002Fnode_modules\u002F`、任何 `*.local.md`。\n- **可以提交**：`templates\u002Finit\u002F*.md` 模板（保持通用）、`templates\u002FSOUL.md` \u002F `TOOL.md`、`assets\u002F`（像素素材）、`skills\u002F`、`webui\u002Fsrc\u002F`、`webui\u002Fpackage*.json`。\n- **提交前门禁**：优先运行 `make check`。它是当前唯一推荐的本地质量入口；不要只跑单项测试就认为可提交。\n- **资产预算**：PNG 素材允许保留像素风识别度，但新增或替换单个资产超过 2.5MB 时必须说明原因；纹理类资源应优先放进 `webui\u002Fsrc\u002Fassets\u002Ftextures\u002F` 并用相对 import\u002FURL 引用，避免 Vite 解析 warning。\n- 新增 provider：在 `agent\u002Fproviders\u002Fregistry.py` 加 `ProviderSpec`，需要新 backend 时在 `factory.py` 加分支并新增 provider 实现。\n- 新增工具：在 `agent\u002Ftools\u002F` 实现 `Tool` 子类，到 `agent\u002Floop.py` 注册。\n- 新增会话控制能力：优先放在 `agent\u002Fcontrol\u002F`；权限审批策略放在 `agent\u002Fpermissions\u002F`；同步 `agent\u002Frunner.py` 暂停\u002F恢复语义、`agent\u002Fweb\u002Froutes\u002F*` API \u002F WS、`webui\u002Fsrc\u002Ftypes.ts` 与 chat 组件。\n- 新增 WebUI 行为事件：优先通过 `agent\u002Fruntime\u002Fevents.py` 构造 payload，经 `RuntimeEventStore` 持久化；前端同步 `webui\u002Fsrc\u002Fruntime\u002F*` 与 `useRuntime.ts`。\n- 新增外部平台适配：优先实现 `agent\u002Fexternal\u002FExternalAdapter` 并接入 `ExternalBridgeService`；必须汇入唯一主线，不得新增多会话、会话列表或 `session_id` 路由。\n- 新增 MCP 服务器：在 `mcp_config.json` 中配置即可，无需改代码。\n- 新增子代理：先在 `agent\u002Fsubagents\u002Fregistry.py` 增加内置 spec 和工具白名单，再在 `templates\u002Fsubagents\u002F{name}.md` 加身份模板；子代理列表以 registry 为事实来源，`researcher` \u002F `general` 这类兼容别名不再拥有独立模板。\n- 新增 WebUI 图标：把 PNG 放进 `assets\u002F\u003Ccategory>\u002F`，在 `webui\u002Fsrc\u002Fassets.ts` 加引用，重新 `npm run build`。\n\n---\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fbrand\u002Flogo-mark.png\" alt=\"令\" width=\"56\" \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Csub>\"大内总管 · 一条主线，边想边回。\"\u003C\u002Fsub>\n\u003C\u002Fp>\n","Emperor Agent 是一个本地运行的个人 Python 智能体，支持多提供商的大规模语言模型（LLM），具有流式聊天、工具集成、技能管理和记忆功能。该项目采用 Python 语言开发，并结合 Vue 3 构建前端界面，使用 Tailwind CSS 进行样式设计，通过 WebSockets 实现前后端通信。其核心特色在于提供了一个三层记忆系统和子代理派遣机制，能够将复杂的任务分解并协调执行。此外，它还具备详细的令牌使用情况跟踪能力。Emperor Agent 非常适合需要构建个性化、可扩展的人工智能助手场景，无论是开发者希望深入研究 AI 应用，还是企业用户想要定制化智能服务解决方案，都能从中受益。",2,"2026-06-11 03:59:14","CREATED_QUERY"]