diff --git a/.astrion/memory/agent_hooks_design.md b/.astrion/memory/agent_hooks_design.md deleted file mode 100644 index b1b1d12c..00000000 --- a/.astrion/memory/agent_hooks_design.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -name: agent_hooks_design -description: 当需要继续讨论或实现本项目的 hooks / 生命周期钩子功能时,应该索引本记忆 ---- - -# Hooks / 生命周期钩子功能设计讨论(进行中) - -## 调研结论 - -### 主流实现 -- **Claude Code**:最完整,20+ 事件、5 种 handler(command/http/mcp_tool/prompt/agent)、多层配置(全局/项目/本地/组织/插件/SDK)。 -- **OpenAI Codex**:11 个事件、command 回调为主、有信任审查机制和企业托管 hooks。 -- **Cursor**:`.cursor/hooks.json`,Beta,command 回调,项目/用户/企业三级。 -- **GitHub Copilot**:`.github/hooks/*.json`,command/http/prompt 三种回调,Cloud Agent 也支持。 -- **Windsurf/Cascade**:`.windsurf/hooks.json`,exit 2 阻断,12 个事件。 -- **Aider / Continue.dev / Kimi Code / Devin**:无原生 hooks 或资料有限。 - -### 共性模式 -``` -Agent 事件发生 - ↓ -stdin 发送 JSON 事件载荷 → Hook 脚本 / HTTP 端点 - ↓ -返回决策(allow/deny/ask/modify/injectMessage) - ↓ -Agent 根据决策继续执行 -``` - -## 用户(项目 owner)对 hooks 的理解 - -> 钩子 = 发生一件事件之前/中/后 → 自动触发另一个事件。用 JSON 格式写条件和行为,分为全局/项目级,放在数据存储目录下/工作区内。 - -用户认可的核心:**事件驱动 + JSON 配置 + 全局/项目两级 + before/after 拦截**。 - -## 已达成的设计共识 - -### 1. 配置位置 -- 全局:`~/.astrion/astrion/config/hooks.json` -- 项目级:`/.astrion/hooks.json` -- 未来可扩展:`.astrion/hooks.local.json`、企业策略级。 -- 多层配置**合并执行**,不是覆盖(更安全)。 - -### 2. 优先事件(MVP 范围建议) -- `PreToolUse`:工具执行前拦截/修改 -- `PostToolUse`:工具执行后触发后续动作 -- `Stop`:模型/turn 结束时注入反馈 -- `PreCompact`:上下文压缩前备份 - -### 3. 优先回调类型 -- MVP 只做 `command`(shell/python/任意可执行文件)。 -- 后续可扩展 `http`、`prompt`、`agent`、`mcp_tool`。 - -### 4. 通信协议(对齐 Claude Code/Codex) -- stdin 输入 JSON 事件载荷 -- stdout 返回 JSON 决策 -- exit code:0=通过,1=非阻断错误,2=阻断 - -### 5. JSON 结构草案 - -```json -{ - "version": 1, - "disabled": false, - "hooks": { - "PreToolUse": [ - { - "name": "block-dangerous-rm", - "matcher": "run_command", - "if": "command contains 'rm -rf /'", - "enabled": true, - "max_repeats": null, - "hooks": [ - { - "type": "command", - "command": "python3 ~/.astrion/astrion/hooks/block_dangerous.py", - "timeout": 5, - "statusMessage": "检查危险命令...", - "fail_policy": "continue", - "on_error": "log" - } - ] - } - ], - "Stop": [ - { - "name": "remind-check-completion", - "max_repeats": 3, - "hooks": [ - { - "type": "command", - "command": "python3 ~/.astrion/astrion/hooks/remind_check.py", - "timeout": 5 - } - ] - } - ] - } -} -``` - -## 已讨论的具体例子 - -### 例子 1:拦截危险命令 -- 事件:`PreToolUse` -- 条件:`matcher=run_command` + `if=command contains 'rm -rf /'` -- 行为:调用脚本,返回 `permissionDecision: deny` - -### 例子 2:edit_file 单行全替换触发审核 -- 事件:`PreToolUse` -- 条件:`matcher=edit_file` + `old_string` 只有一行 + `replace_all=true` -- 行为:返回 `permissionDecision: ask` - -### 例子 3:Stop 时自动插入 user 消息,最多 3 次 -- 事件:`Stop` -- 行为:返回 `injectMessage: {role: "user", content: "请仔细检查是否真的完成了所有工作"}` -- "最多 3 次"可用 `max_repeats` 或由脚本自己维护状态文件。 - -## 尚未决定的决策点 - -1. **`if` 条件表达式语法**: - - 选项 A:简单规则 DSL(如 `command contains '...'`) - - 选项 B:结构化 JSON(如 `{"field": "...", "op": "contains", "value": "..."}`) - - 选项 C:交给 hook 脚本自己判断,`if` 只保留工具名匹配 - -2. **"最多 N 次"由谁负责**: - - 框架负责 `max_repeats`(简单,但状态语义固定) - - 脚本自己维护状态文件(灵活,框架更轻量) - -3. **注入消息语义**: - - `injectMessage.user`:会触发模型再回复一轮 - - `addContext.system`:只是给下一轮加提示,不立刻触发回复 - -4. **hook 失败时的主流程策略**: - - 默认 `continue`(用户配置错误不瘫痪智能体) - - 或提供 `fail_policy: block` 让管理员强制策略 - -5. **工具名匹配**: - - 是否同时支持内部工具名(`run_command`/`edit_file`)和 Claude Code 风格别名(`Bash`/`Edit|Write`) - -## 推荐后续路线 - -1. 先定 JSON schema 和 `if` 语法。 -2. MVP 实现 `command` 回调 + `PreToolUse`/`PostToolUse`/`Stop`。 -3. 新增 `modules/hook_manager.py`。 -4. 在 `core/main_terminal_parts/tools_execution.py` 和 `core/web_terminal.py` 插入 hook 调用。 -5. 确保 API task 模式也走 hooks。 -6. 补文档 `docs/hooks.md`。 - -## 参考资料位置 - -- 调研报告:`research/claudecode_hooks/claudecode_hooks.md` -- 调研报告:`research/codex_hooks/codex_hooks.md` -- 调研报告:`research/other_agents_hooks/other_agents_hooks.md` diff --git a/.astrion/memory/ai_avatar_demo.md b/.astrion/memory/ai_avatar_demo.md deleted file mode 100644 index 5fe4af6f..00000000 --- a/.astrion/memory/ai_avatar_demo.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: ai_avatar_demo -description: 当需要了解或继续开发/修改 agents 项目的 AI 形象头像 demo 时,应该索引本记忆 ---- - ---- -name: ai_avatar_demo -description: 当需要了解或继续开发/修改 agents 项目的 AI 形象头像 demo 时,应该索引本记忆 ---- - -# AI 形象头像 Demo - -## 文件位置 -`/Users/jojo/Desktop/agents/正在修复中/agents/demo/ai-avatar/index.html` - -## 设计目标 -为项目设计一个独特、动态的 AI 形象,替代之前从网上找的通用小机器人 SVG。 - -## 当前设计 - -### 视觉构成 -- **外框**:flat-top 圆角正六边形(上下边水平,底部是一条横线),中心 `(100,100)`,外接圆半径 80,圆角半径 14。 -- **内部符号**:由两条等粗白色线段组成: - - 空闲态:两条竖线作为眼睛,长度 22,间距 32,位于 `y ≈ 84`(面部上半区域略偏上)。 - - 工作态:`>` 终端提示符(无下横线),尖端朝右,开角 60°,单条线长度 34。 - -### 状态与动画 -- **空闲**:六边形停到最近的 60° 正位,显示眼睛,眼睛跟随鼠标移动。 -- **工作**:六边形开始持续旋转,显示 `>` 符号。 -- 状态切换时内部符号立即变形,不等待外框停稳;外框则从当前角度平滑过渡到目标角度或开始旋转。 -- 内部符号在空闲/工作之间通过**同两条 ``** 进行 morph: - - `transform`(位置 + 旋转)使用 CSS `transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1)`。 - - `x2`(线段长度)使用同节奏(0.55s,相同 cubic-bezier)的 `requestAnimationFrame` 插值,避免长度跳变。 -- 提供一个独立的"停止追踪 / 开始追踪"按钮:停止追踪时眼睛平滑回到默认居中位置。 - -### 技术实现 -- **纯 SVG**:外框用 ``,眼睛 / `>` 用 ``,整体用 `` + ``。 -- **动画方式**: - - 六边形旋转:CSS `@keyframes spin` + CSS `transition`。 - - 符号变形:CSS `transition` 改变 `transform`;JS `requestAnimationFrame` 插值改变 `x2`。 - - 眼睛追踪:JS `requestAnimationFrame` 实时更新 SVG `` 的 `transform`。 -- **无 Canvas / WebGL / GIF / Lottie / 图片序列帧**。 -- 整体缩放不会导致比例或长度失调(矢量 SVG)。 - -## 设计约束 - -### 不可改动的核心动画 -`demo/ai-avatar/index.html` 中 **空闲(idle)** 与 **工作(work)** 两个核心状态的切换动画不可改动: - -- 六边形外框:空闲时从当前角度滑到最近的 60° 正位后停住;工作时从当前角度开始持续旋转。 -- 内部图标:空闲为眼睛,工作为 `>` 符号,二者通过同两条 `` 元素进行 morphing 变形。 -- 变形节奏统一为 0.55s cubic-bezier(0.22, 1, 0.36, 1): - - `transform` 由 CSS transition 处理。 - - `x2` 长度由 JS `requestAnimationFrame` 插值处理(CSS transition 不直接支持 SVG `x2` 属性)。 - -### 设计原则 -- 只允许 SVG 的**微动 / 线条 / 点**之间的**优雅细微动化**。 -- 禁止花哨特效:脉冲波纹、光标闪烁、发光、强烈缩放、大幅度弹跳等。 -- 新增工作状态时,应保持通用切换容器,避免为每个 SVG 单独适配位置。 -- 内部图标线条应比外框稍细,保持视觉层次感。 - -## 关键调整历史 -- 外框从 pointy-top 改为 flat-top(横线在底部)。 -- 边角从锐利直线改为 Q 贝塞尔圆角。 -- 状态含义反转:空闲 = 静止 + 眼睛 + 追踪;工作 = 旋转 + `>`。 -- 眼睛长度缩短,位置上移至 `y=84`。 -- 鼠标追踪范围水平/垂直均为 ±10px。 -- 2026-07-01:为 `x2` 长度变化增加与 `transform` 同节奏的 JS 插值动画,解决工作/休息切换时长度跳变问题。 diff --git a/.astrion/memory/app_initialization_flow.md b/.astrion/memory/app_initialization_flow.md deleted file mode 100644 index 7c9cee38..00000000 --- a/.astrion/memory/app_initialization_flow.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: app_initialization_flow -description: 当需要适配桌面 App 初始化流程或理解项目首次启动配置时,应该索引本记忆 ---- - -## 项目初始化流程(首次启动 / 桌面 App 适配必读) - -### 关键文件 -- `_bootstrap.sh` — 共享引导(定位项目根、准备 Python venv + Node 依赖) -- `setup.sh` — 首次初始化入口:调 `_bootstrap.sh` 后运行 `python -m scripts.setup` -- `scripts/setup.py` — **交互式配置向导**(终端),5 步生成 `.env` + `custom_models.json` - -### setup.py 向导步骤 -1. **运行模式** — `host`(单机本地沙箱)/ `web`(多用户 Docker),决定 `TERMINAL_SANDBOX_MODE` -2. **Web 监听** — 端口 (`WEB_SERVER_PORT`) + 地址 (`WEB_SERVER_HOST`),host 模式默认 127.0.0.1 -3. **数据目录** — `~/.astrion/astrion//`,可自定义(设 `ASTRION_DATA_ROOT`) -4. **管理员账户** — `AGENT_ADMIN_USERNAME` + `AGENT_ADMIN_PASSWORD_HASH`(werkzeug hash) -5. **模型配置** — 写入部署级 `custom_models.json`(~/.astrion/astrion/config/):model_name / url / apikey / model_id / thinking 支持 -6. **自动生成** — `WEB_SECRET_KEY` + `API_TOKEN_SECRET`(secrets.token_hex(32)) - -### 设计要点 -- 向导**刻意不 import config**,以免在 `.env` 写之前过早锁定路径 -- 以 `.env.example` 为模板,仅替换被接管键,其余原样保留(含注释) -- `.env` 写完后 chmod 600 - -### 桌面 App 适配要点 -- 桌面 App 应固定 `TERMINAL_SANDBOX_MODE=host` -- 端口应自动找可用端口 -- 模型配置可在 Web UI 中后续修改,但首次启动至少需要一个模型 -- `_deploy_config_dir()` 推导逻辑必须与 `config/paths.py` 保持一致 diff --git a/.astrion/memory/astrion_naming.md b/.astrion/memory/astrion_naming.md deleted file mode 100644 index 21034c95..00000000 --- a/.astrion/memory/astrion_naming.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -name: astrion_naming -description: 当需要了解或引用本项目的品牌名称、产品名 Astrion 的含义与来源时,应该索引本记忆 ---- - -# 项目名称 / 产品名:Astrion - -## 决策记录 -- **暂定名称**:Astrion -- **决策时间**:2026-07-07 -- **命名方向**:人格化 + 宇宙现象/天体 + 科技意象 -- **当前状态**:暂定,后续如需变更请同步更新本记忆、AGENTS.md 与 claude.md - -## 名称含义 - -**构词** -- **Astr-**:来自希腊语 *astron*,意为“星星 / 天体 / 宇宙” -- **-ion**:科技命名中常见的粒子/状态后缀(如 photon、electron、neutron、ion) - -**字面意象** -> 星之子、星际粒子、来自宇宙尘埃的微小发光体。 - -## 表达特点 - -| 特点 | 解读 | -|------|------| -| 科技感 | `-ion` 后缀自带物理/科技联想 | -| 宇宙感 | `Astr-` 词根直接指向星空 | -| 轻盈感 | 粒子暗示微小、快速、自由穿行 | -| 能量感 | ion(离子)带电,暗示活跃、驱动、连接 | -| 可人格化 | 三音节 `/ˈæstriən/`,发音接近 Orion、Damion,适合作为“他/她” | -| 中性 | 不偏男性或女性,适用面广 | - -## 品牌叙事参考 - -1. **连接者**:像星际间传递信号的粒子,把人类意图传递到工具、文件、终端、子智能体之间。 -2. **从混沌中组织秩序**:宇宙尘埃在引力下聚集成恒星;Astrion 把零散任务聚合成可执行结果。 -3. **渺小但明亮**:单个粒子微不足道,但携带能量、能引发连锁反应——对应 Agent 虽小,却能撬动复杂系统。 - -## 中文参考 - -- 全称音译:**阿斯特里恩** -- 简称候选:**星子**(对应 astron + particle,更顺口) - -## 同步位置 - -- 项目规范:`AGENTS.md` -- Claude Code 指南:`claude.md` diff --git a/.astrion/memory/background_notification_pool.md b/.astrion/memory/background_notification_pool.md deleted file mode 100644 index 2b6b1aae..00000000 --- a/.astrion/memory/background_notification_pool.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -name: background_notification_pool -description: 当需要讨论或修复后台子智能体/后台 run_command 完成通知机制时,应该索引本记忆 ---- - -# 后台子智能体 / 后台 run_command 通知机制问题(待修复) - -## 问题描述 - -后台子智能体和后台 `run_command` 在“主智能体停止输出后完成”的场景下,通知机制存在串行化问题: - -- 若同时有多个后台任务在运行,它们不会随完成逐个通知。 -- 而是在所有后台任务全部完成后,按时间顺序一条一条地发送 user 消息。 -- 且每发送一条 user 消息就要触发一次“工作 → 停止 → 再工作”的循环,导致通知被拆成多次停止输出。 - -## 当前机制 - -运行期间(inline)和停止输出后(polling)是两条链路: - -### 1. 运行期间(inline,当前正常) - -- 位置:`server/chat_flow_tool_loop.py` 中 `execute_tool_calls` 末尾。 -- 调用: - - `process_sub_agent_updates(..., inline=True)` - - `process_background_command_updates(..., inline=True)` -- 行为:每次工具循环结束时,把**当前所有已完成且未通知**的任务一次性插入 `messages`,不触发新任务。 -- 结论:运行期间的批量通知已经是“池化”的,暂无问题。 - -### 2. 停止输出后(polling,问题所在) - -- 位置:`server/chat_flow_task_main.py` 中 `handle_task_with_sender` 结尾。 -- 调用: - - `poll_sub_agent_completion(...)` - - `poll_background_command_completion(...)` -- 行为: - - 两个轮询器各自由 `socketio.start_background_task(...)` 启动。 - - 每轮循环只处理**一个**完成的通知。 - - 处理完一个通知后,创建一个新的 chat task,然后 `return`。 - - 下一个通知必须等待这个新 chat task 跑完,并再次走到 `handle_task_with_sender` 结尾重新 spawn 轮询器时才会发出。 - -## 根因 - -后台任务管理器本身已经扮演了“后台池 + 通知池”的角色(任务完成但 `notified=False` 就是待通知状态)。 - -但轮询器是 **one-shot** 的:每次只消费一条通知就退出,导致通知被串行化到多次“工作 → 停止 → 再工作”循环中。 - -## 讨论中的设计方案 - -统一为“通知池轮询器”: - -1. 合并 `poll_sub_agent_completion` 和 `poll_background_command_completion` 为一个统一的 `poll_completion_notifications`。 -2. 每次轮询时,同时从以下两处取出**所有**待通知项: - - `sub_agent_manager.poll_updates()` - - `background_command_manager.poll_updates(conversation_id=...)` -3. 把所有待通知项作为 user 消息插入对话历史(倾向保留为多条消息,而非合并成一条)。 -4. 只创建 **一个**后续 chat task 来触发继续工作。 -5. 轮询器返回,由这个新任务在结束时继续下一轮轮询。 - -### 期望行为示例 - -- 3 个后台子智能体运行中,主智能体停止输出。 -- 第 1 个完成 → 通知池有 1 条 → 插入 1 条 user 消息 → 触发 1 个新任务。 -- 新任务处理期间,第 2、3 个完成 → 进入通知池。 -- 新任务停止输出 → 轮询器发现通知池还有 2 条 → 一次性插入 2 条 user 消息 → 再触发 1 个新任务。 -- 不再有后台任务时,轮询器直接退出。 - -## 涉及文件 - -| 文件 | 角色 | -|---|---| -| `server/chat_flow_task_support.py` | 新增统一轮询器 `poll_completion_notifications`;可废弃旧的两个轮询函数。 | -| `server/chat_flow_task_main.py` | 在 `handle_task_with_sender` 结尾,把分别 spawn 两个轮询器的逻辑替换为 spawn 一个统一轮询器。 | -| `modules/sub_agent/manager.py` | 提供 `poll_updates()`,基于 `notified` 标志表示待通知状态。 | -| `modules/background_command_manager.py` | 提供 `poll_updates()` 和 `has_pending_for_conversation()`。 | - -## 待决策点 - -- 是否坚持把通知拆成多条 user 消息插入历史(推荐),还是合并为一条。 -- 统一轮询器是否做成“持久单例”还是保持 one-shot batch(当前讨论倾向 one-shot batch,由新任务继续轮询)。 - -## 状态 - -已实施(2026-06-24,方案 A + one-shot batch)。 - -### 落地实现 - -- `server/chat_flow_task_support.py` 未改;新增逻辑集中在 `server/chat_flow_task_main.py`。 -- 新增统一轮询器 `poll_completion_notifications`,替换并删除旧的 `poll_sub_agent_completion` / `poll_background_command_completion`。 -- 新增 `_collect_pending_completion_notices`:每轮一次性从子智能体 + 后台命令两路取出所有待通知项(取出即就地标记 `notified` / `_announced_sub_agent_tasks`),按 `updated_at` 跨两路排序。 -- 新增 `_has_pending_completion_work`:判断是否还有运行中/待通知的后台工作,决定轮询是否继续。 -- 新增 `_build_sub_agent_notice_text`:抽出子智能体通知文本构建。 -- `handle_task_with_sender` 结尾:保留 `sub_agent_waiting` 事件探测,但把「分别 spawn 两个轮询器」改为只 spawn **一个** `run_completion_poll`。**这也顺带消除了两个轮询器同时 `create_chat_task` 撞 `create_chat_task` 单工作区互斥(`models.py` RuntimeError)的隐患。** - -### 方案 A 的「多条 + 单 task」如何落地 - -`create_chat_task` 只收单条 message,`handle_task_with_sender` 开头无条件 `add_conversation`。因此: - -- 一批取出 N 条:前 N-1 条为「前置通知」,最后 1 条作为后续 chat task 的触发消息。 -- `_dispatch_completion_user_notice` 新增 `preceding_notices` 参数: - - 通过 `_persist_and_echo_preceding_notice` 把前 N-1 条**写入对话历史**(`add_conversation`,落盘)+ **socketio 回显**(在线客户端即时可见),`starts_work=False`(渲染为完成态气泡,不起独立计时器)。 - - 把 `preceding_user_notices` 塞进 `session_data`,传给后续任务。 -- `server/tasks/models.py` 的 `_run_chat_task`:在补发触发 user_message 事件**之前**,先按顺序把 `preceding_user_notices` 逐条 `_append_event("user_message", ...)` 注入任务事件流——保证 REST 轮询客户端按时间顺序看到所有完成通知。 -- 触发消息(最后 1 条)走原有 `auto_user_message_event` 链路,正常 `starts_work=True` 启动一轮工作。 - -### 验证 - -- `python3 -m py_compile server/chat_flow_task_main.py server/tasks/models.py` 通过(注意本机 pycache 目录无写权限,需 `PYTHONPYCACHEPREFIX=$TMPDIR/pycache`)。 -- `python3 -m unittest test.test_server_refactor_smoke` 6 用例通过。 -- 前端未改:复用既有 `user_message` / `sub_agent_notice` 渲染链路(`messaging.ts` handleUserMessage + socketio `user_message`),dedup 与单通知场景一致。 diff --git a/.astrion/memory/chat_block_scroll_anchor.md b/.astrion/memory/chat_block_scroll_anchor.md deleted file mode 100644 index 6dcd4fa6..00000000 --- a/.astrion/memory/chat_block_scroll_anchor.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: chat_block_scroll_anchor -description: 当需要优化聊天区域块展开/折叠滚动行为,或排查向上滚动后瞬间跳回问题时,应该索引本记忆 ---- - -# 聊天区域块展开/折叠滚动行为优化记录 - -## 背景 -聊天区域默认使用 `vue-stick-to-bottom` 保持底部锁定。块(thinking/tool)展开/折叠时,`stick-to-bottom` 的弹簧追底动画和 CSS 高度过渡不同步,会产生「向下展开一点、向上滚动一点」的顿挫感。 - -## 已尝试方案 - -### 1. 块展开锚定(useBlockExpansionAnchor) -- 在 `static/src/composables/useBlockExpansionAnchor.ts` 中实现 -- 块展开/折叠的 CSS 过渡期间(约 260–300ms),用 `requestAnimationFrame` 持续覆盖 `scrollTop` -- 目标:让被操作块的底边或顶边保持在原来视口位置 -- 方向判断: - - 用**底边**判断比顶边更准确。高块即使顶边位于上半,底边仍可能在下半;用顶边判断会把高块误判为 `down` 模式 - - `up` 模式:底边不动,头部向上展开 - - `down` 模式:顶边不动,内容向下展开 -- 方向在单次展开-收起周期内记忆(`preferredModes`),保证收起是展开的倒放;收起动画结束后清除记录 - -### 2. 运行期间策略 -- 运行中的 thinking/tool 块(带 `.processing`/`.running`/`.thinking-animation`)保持旧逻辑,由 `stick-to-bottom` 追底,避免被输入栏遮挡 -- 非运行中的块(包括运行期间的历史块)走锚定逻辑 -- 已实现 commit:`f43b5ea7 fix(chat): 运行期间恢复旧滚动逻辑,堆叠块更多按钮也接入锚定` - -### 3. 堆叠块「更多」按钮 -- `StackedBlocks.vue` 在 toggleMore 时 emit `more-toggle`,`ChatArea.vue` 监听后对 more-block 元素启动锚定 -- 注意:事件 payload 里的展开状态应使用 `payload.expanded`,避免 `ReferenceError` - -## 未解决的向上滚动跳回问题 - -### 现象 -- 在块较多的对话中,**向上滚动**后页面会瞬间跳回滚动前的位置 -- 向下滚动不会出现 -- 禁用 `anchorBlockElement` 调用、禁用 wheel-up 时的 `stopScroll()` 后问题依然存在 - -### 调试结论 -- 不是 `anchorBlockElement` 直接导致 -- 不是 `stopScroll()` 直接导致 -- `heightDelta` 可能为负(内容高度减小),但 large-growth 检测未触发 -- `escapedFromLock` 已为 `true`,`stick-state-change` 未出现异常变化 -- 原因未最终定位,可能与 `vue-stick-to-bottom` 内部状态、浏览器原生滚动行为或内容高度变化后的布局抖动有关 - -## 当前代码状态 -- 当前 HEAD:`f43b5ea7 fix(chat): 运行期间恢复旧滚动逻辑,堆叠块更多按钮也接入锚定` -- 工作区干净,所有调试和临时修改已撤回,stash 已清空 -- 后续若继续优化,建议从 `f43b5ea7` 开始,并优先定位向上滚动跳回的根本原因 diff --git a/.astrion/memory/cloud_server_disk_usage.md b/.astrion/memory/cloud_server_disk_usage.md deleted file mode 100644 index f10c48b4..00000000 --- a/.astrion/memory/cloud_server_disk_usage.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -name: cloud_server_disk_usage -description: 当需要了解云服务器磁盘使用现状、规划Docker镜像构建空间、或决定清理哪些文件释放空间时,应该索引本记忆 ---- - -# 云服务器磁盘使用现状 - -> 记录时间:2026-06-25 -> 服务器:59.110.19.30(Ubuntu 24.04.2 LTS, x86_64) - -## 总体情况 - -```text -Filesystem Size Used Avail Use% -/dev/vda3 49G 43G 4.0G 92% -``` - -- 总磁盘:约 49 GB -- 已用:约 43 GB(92%) -- 可用:约 4 GB - -**结论:磁盘非常紧张,不足以直接本地构建方案 B 的 2.9GB Docker 镜像。** - -## 占用大户(按大小排序) - -| 路径 | 大小 | 说明 | -|------|------|------| -| `/var/lib/docker` | **18 GB** | Docker 镜像、容器、volume | -| `/var/log/journal` | **4.1 GB** | systemd journal 日志 | -| `/opt/agent` | **5.0 GB** | agents 项目源码、运行时、备份、镜像 | -| `/opt/openwebui` | **3.6 GB** | OpenWebUI 部署目录 | -| `/var/lib/filebrowser` | **3.0 GB** | filebrowser 数据 | -| `/swapfile` | **2.1 GB** | swap 文件 | -| `/home/git/data` | **856 MB** | gitea 数据 | -| `/home/admin/go` | **770 MB** | Go 相关文件/缓存 | -| `/var/www` | **1.4 GB** | nginx web 目录 | -| `/var/lib/clamav` | **1.2 GB** | 病毒库 | -| `/usr` | **4.2 GB** | 系统软件 | - -## Docker 详细占用 - -```text -Images: 4 5.284 GB - - ghcr.io/open-webui/open-webui 4.32 GB - - my-agent-shell amd64 913 MB - - my-agent-shell latest 913 MB(与 amd64 同 image id) - - python 3.11-slim 124 MB - - ghcr.io/corentinth/it-tools latest 56.2 MB - -Volumes: 1 7.966 GB - - open-webui: 7.966 GB - -Containers: 3 - - agent-term-jojo-default my-agent-shell:latest Up - - openwebui-new open-webui image Up (healthy) - - it-tools it-tools image Exited (7 months ago) - -Build Cache: 10 / 2.182 kB(可忽略) -``` - -## `/opt/agent` 详细占用 - -| 路径 | 大小 | 说明 | -|------|------|------| -| `/opt/agent/backup` | **1.5 GB** | 6 个压缩备份包(runtime_backup_* ×3, users_data_backup_* ×3) | -| `/opt/agent/dockerimage` | **894 MB** | `my-agent-shell_amd64.tar` 旧镜像 tar | -| `/opt/agent/agents` | **883 MB** | 当前源码目录 | -| `/opt/agent/agents_old_20260614` | **540 MB** | 旧版本源码备份 | -| `/opt/agent/runtime` | **520 MB** | 运行时数据(对话、用户工作区等) | -| `/opt/agent/voice_models` | **230 MB** | 语音模型文件 | -| `/opt/agent/agents_old_20260612_012026` | **190 MB** | 旧版本源码备份 | -| `/opt/agent/agents_old_20260614_190527` | **190 MB** | 旧版本源码备份 | -| `/opt/agent/venv` | **96 MB** | Python 虚拟环境 | -| `/opt/agent/agents.zip` | **42 MB** | 源码 zip 包 | - -## 建议清理清单 - -### 低风险(约可释放 6.5 GB) - -| 操作 | 可释放 | 命令/路径 | -|------|--------|-----------| -| 清理 systemd journal 旧日志 | ~4 GB | `journalctl --vacuum-time=7d` 或 `--vacuum-size=500M` | -| 删除 `/opt/agent/backup` 旧备份,只留最新各 1 个 | ~1 GB | 手动删除旧的 `runtime_backup_*` 和 `users_data_backup_*` | -| 删除 `/opt/agent/dockerimage/my-agent-shell_amd64.tar` | 894 MB | `rm /opt/agent/dockerimage/my-agent-shell_amd64.tar` | -| 删除 `/opt/agent/agents_old_*` 旧版本目录 | ~920 MB | `rm -rf /opt/agent/agents_old_*` | -| 删除 `/opt/agent/agents.zip` | 42 MB | `rm /opt/agent/agents.zip` | -| 删除已退出容器 `it-tools` | ~514 kB | `docker rm it-tools` | - -### 中风险(需先确认是否仍在使用) - -| 操作 | 可释放 | 说明 | -|------|--------|------| -| 清理 `/var/lib/filebrowser` | 3 GB | 需确认 filebrowser 中是否还有需要保留的文件 | -| 清理 `/home/admin/go` 缓存 | ~770 MB | `go clean -cache` | -| 删除 `/var/lib/clamav` | 1.2 GB | 如果系统不使用 clamav 扫描 | - -### 不建议动 - -- `/opt/agent/runtime`:用户运行时数据(对话、项目文件等) -- `/opt/agent/agents`:当前项目源码 -- `/opt/openwebui`:如果 OpenWebUI 服务仍在使用 -- `/home/git/data`:gitea 数据 -- Docker volume `open-webui`:OpenWebUI 用户数据 - -## 关键命令(下次快速查看) - -```bash -# 总体磁盘 -ssh 59.110.19.30 -df -h - -# 根目录一级占用 -sudo du -sh /* 2>/dev/null | sort -h | tail -20 - -# /var 明细 -sudo du -sh /var/lib/* /var/log/* 2>/dev/null | sort -h | tail -20 - -# /opt/agent 明细 -sudo du -sh /opt/agent/* 2>/dev/null | sort -h | tail -20 - -# Docker 占用 -docker system df -docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}' -docker volume ls - -# 清理 journal -sudo journalctl --vacuum-time=7d - -# 清理 Docker -# docker image prune -a # 谨慎:会删除所有未使用镜像 -# docker volume prune # 谨慎:会删除未使用 volume -``` - -## 与本次 Docker 改造的关系 - -- 当前 `my-agent-shell` 镜像:913 MB -- 方案 B 目标镜像:约 2.9 GB(本地 arm64 实测) -- 构建过程需下载 586 MB apt 包,解压后约 2.2 GB -- 当前可用空间仅 4 GB,**不足以安全构建方案 B** -- 建议先执行低风险清理释放 6-8 GB,再考虑构建 -- 云端构建建议分阶段:先只加 LibreOffice + 字体 + Node + Python 必要包(约 1.5 GB 镜像),验证稳定后再决定是否加 chromium/ffmpeg - -## 注意事项 - -- 服务器架构为 x86_64,本地为 arm64,**禁止将本地构建的镜像上传到云端** -- 云端网络环境较差,大文件下载可能超时 -- 云端内存 2 GB,构建/运行大镜像需警惕 OOM -- 清理前务必确认文件是否仍在使用,尤其是 `/var/lib/filebrowser` 和 `/opt/openwebui` diff --git a/.astrion/memory/cloud_server_ssh_and_directories.md b/.astrion/memory/cloud_server_ssh_and_directories.md deleted file mode 100644 index 99f5d271..00000000 --- a/.astrion/memory/cloud_server_ssh_and_directories.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: cloud_server_ssh_and_directories -description: 当需要SSH登录云服务器排查部署问题、查看日志、或操作服务器文件时,应该索引本记忆 ---- - -# 云服务器 SSH 与目录指南 - -## SSH 登录 - -```bash -ssh 59.110.19.30 -# 使用与本地相同的 id_ed25519 密钥 -``` - -## 关键目录 - -| 用途 | 路径 | -|------|------| -| **源码根目录** | `/opt/agent/agents/` | -| **运行时数据根** | `/opt/agent/runtime/`(`ASTRION_DATA_ROOT`) | -| **部署级配置** | `/opt/agent/runtime/config/` | -| **子智能体模型配置** | `/opt/agent/runtime/config/sub_agent_models.json` | -| **子智能体代码** | `/opt/agent/agents/modules/sub_agent/`(Python 主进程内协程) | -| **虚拟环境** | `/opt/agent/venv/` | - -## 用户数据 - -``` -/opt/agent/runtime/web/users/<用户名>/projects/<项目名>/ -├── project/ # 用户工作区文件 -└── data/ - └── conversations/ - └── default/ - └── conv_xxx.json # 对话记录 -``` - -## 子智能体相关 - -| 用途 | 路径 | -|------|------| -| **子智能体任务目录** | `/opt/agent/runtime/web/data/sub_agent_tasks//` | -| **任务文件** | `task.txt`、`system_prompt.txt` | -| **子智能体交付目录** | 在用户 project 下的 `sub_agent_results/` | - -## 服务管理 - -| 操作 | 命令/路径 | -|------|----------| -| **启动服务** | `bash /opt/agent/start_webapp_new.sh`(端口 8091) | -| **更新服务** | `bash /opt/agent/refresh_agents.sh`(git pull + 重启 + nginx reload) | -| **数据迁移脚本** | `python3 /opt/agent/agents/scripts/migrate_runtime_data.py`(执行前务必 `--dry-run`) | -| **Nginx 配置** | `/etc/nginx/sites-enabled/agent.cyjai.com` | -| **本地部署脚本** | `deploy_agents.sh`(git push → npm build → scp dist/ → 触发远程 refresh) | - -## 日志 - -| 用途 | 路径 | -|------|------| -| **Web 日志** | `/opt/agent/runtime/web/logs/` | -| **主机调试日志** | `/opt/agent/runtime/web/logs/host_workspace_debug.log` | - -## 常见排查流程 - -1. SSH 登录服务器 -2. 查看对话 JSON:`cat /opt/agent/runtime/web/users//projects//data/conversations/default/conv_xxx.json` -3. 查看子智能体任务:`ls /opt/agent/runtime/web/data/sub_agent_tasks/` -4. 查看日志:`tail -100 /opt/agent/runtime/web/logs/host_workspace_debug.log` -5. 查看子智能体任务文件:`ls /opt/agent/runtime/web/data/sub_agent_tasks//` - -## 已知坑 - -- **前端构建不能服务器执行**:2G 内存会 OOM,必须本地 build 后 scp 上传 `static/dist/` -- **venv 符号链接问题**:从 macOS 复制来的 venv,启动脚本 `fix_venv()` 会自动检测重建 -- **子智能体现为 Python 协程**:`easyagent/` 目录已废弃,不再作为子智能体入口 -- **数据迁移后路径变更**:运行态数据根的环境变量由 `AGENTS_DATA_ROOT` 改为 `ASTRION_DATA_ROOT`,工作区内部目录由 `.agents/` 改为 `.astrion/` diff --git a/.astrion/memory/config_system_and_deployment.md b/.astrion/memory/config_system_and_deployment.md deleted file mode 100644 index c4d8067f..00000000 --- a/.astrion/memory/config_system_and_deployment.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: config_system_and_deployment -description: 当修改配置加载逻辑、服务器部署流程、skill 归档路径或理解 settings.json 结构时,应该索引本记忆 ---- - -## Skill 归档路径约定(2026-07-09 更新) - -- **内置 skill 库**:`AGENT_SKILLS_DIR`(源码树 `agentskills/`),只放通用、自带、适用于项目的 skill。 -- **用户自定义 skill 归档目录**:`CUSTOM_SKILLS_DIR = //agentskills` - - host 模式:`~/.astrion/astrion/host/agentskills/`(全局,不按用户拆分) - - web/docker 模式:`~/.astrion/astrion/web/agentskills/`(可与 per-user `personal/agentskills` 并存) -- **create_skill 行为**:统一通过 `infer_private_skills_dir(data_dir)` 解析目标目录,host 模式不再特殊处理为源码树 `agentskills/`。 -- **实验/测试 skill**:如 `test` 应从源码树 `agentskills/` 移出,归档到 `CUSTOM_SKILLS_DIR`,不作为内置 skill 发布。 diff --git a/.astrion/memory/conversation_model_persistence.md b/.astrion/memory/conversation_model_persistence.md deleted file mode 100644 index 71395e36..00000000 --- a/.astrion/memory/conversation_model_persistence.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: conversation_model_persistence -description: 当加载/恢复对话时发现模型未恢复、新建对话后旧对话模型被覆盖、或修改 /api/model、_apply_workspace_personalization_preferences 时,应该索引本记忆 ---- - ---- -name: conversation_model_persistence -description: 当加载/恢复对话时发现模型未恢复、新建对话后旧对话模型被覆盖、或修改 /api/model、_apply_workspace_personalization_preferences 时,应该索引本记忆 ---- - -# 对话模型持久化约定(对话级隔离架构版) - -## 核心规则 - -- 每个对话的 `model_key` 保存在对话文件的 `metadata.model_key`。 -- **对话级 terminal(`_bound_conversation_id` 非空)的模型由绑定加载权威恢复**(`WebTerminal._ensure_conversation` 用 `restore_model=True`),其它任何逻辑不得覆盖它。 -- **工作区级 terminal(未绑定)刻意 `restore_model=False`** 自动加载最近对话:避免 /new 页面显示旧对话模型。 - -## 四条关键防线(2026-07-20 修复,commit 1289c31d) - -1. **`core/web_terminal.py::_ensure_conversation`**:绑定路径 `load_conversation(bound_id, restore_model=True)`。工作区自动加载路径保持 `restore_model=False`。 -2. **`server/chat/settings.py::/api/model`**:仅当请求显式携带 `conversation_id` 且与 terminal `current_conversation_id` 一致时才 `save_conversation`;无 cid(/new 页面)不持久化——工作区级 terminal 的 current 可能是陈旧对话,盲存会把模型写错对话或用陈旧 history 覆盖新消息。 -3. **前端 `static/src/app/methods/ui/model.ts::handleModelSelect`**:POST /api/model 必须带 `conversation_id: this.currentConversationId`。 -4. **`server/context.py::_apply_workspace_personalization_preferences`**:对 `_bound_conversation_id` 非空的 terminal 跳过 session 模型恢复与 default_model 应用——否则 session 里的全局模型会把对话级 terminal 恢复好的模型再盖掉(重启后回变默认的直接元凶)。 - -## 会话级语义 - -- `session["model_key"]` 是全局"用户最后选择的模型":用于工作区级 terminal 恢复与 /new 继承,**不代表任何具体对话的模型**。 -- 任务创建时前端随任务传 `model_key`(`send.ts`),`server/tasks/models.py` 在任务起点 `terminal.set_model(rec.model_key)`——前端是任务模型的权威来源。 -- `set_model()` 可能因图片/视频不兼容或模型被禁用抛异常,加载场景应捕获降级。 -- 管理员强制禁用模型的切换(context.py 406/549 行附近)对对话级 terminal 仍然生效,属正当覆盖。 - -## 验证脚本 - -- `_experiments/verify_model_fix.py`:p1 全周期(带cid切换→落盘、不带cid不污染)、p2 重启后保持;另需验证 `GET /api/status?conversation_id=X` 返回对话保存模型。 diff --git a/.astrion/memory/docker_toolbox_enhancement_plan.md b/.astrion/memory/docker_toolbox_enhancement_plan.md deleted file mode 100644 index de96b57d..00000000 --- a/.astrion/memory/docker_toolbox_enhancement_plan.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -name: docker_toolbox_enhancement_plan -description: 当需要继续推进 my-agent-shell 容器镜像增强改造、查看本地实验结果、或在云端构建新版镜像时,应该索引本记忆 ---- - -# my-agent-shell 容器镜像增强改造计划与本地实验进度 - -> 记录时间:2026-06-25 -> 相关记忆:`cloud_server_disk_usage`(服务器磁盘现状) - -## 背景 - -当前运行在云端的 `my-agent-shell` 容器(镜像 `my-agent-shell:latest`,基于 `python:3.11-slim`)依赖不足: - -- docx/pptx skill 缺少 `defusedxml` -- pptx skill 推荐的 `markitdown` 未安装 -- 完全缺少 LibreOffice / soffice -- 完全缺少 pandoc、pdftoppm(poppler-utils) -- 完全缺少 Node.js / npm -- 完全缺少中文字体(`fc-list` 为 0) - -导致用户常见的 Office 转换、图片处理、OCR、网页截图等需求无法跑通。 - -## 改造方案 - -### 方案 B(已选定) - -在最小方案基础上扩展常见业务能力: - -**系统 apt 包:** -- Office:libreoffice、pandoc、poppler-utils -- 图片/视频:imagemagick、ffmpeg -- OCR:tesseract-ocr、tesseract-ocr-chi-sim、tesseract-ocr-chi-tra -- 网页:chromium -- 字体:fonts-noto-cjk、fonts-noto-color-emoji、fonts-liberation -- 开发工具:jq、ripgrep、fd-find、tree - -**Python pip 包(加入 `docker/toolbox-requirements.txt`):** -- defusedxml -- markitdown[docx] -- pytesseract -- pdf2image - -**Node 全局包:** -- docx -- pptxgenjs - -**特殊处理:** -- 用 NodeSource 安装 Node 20 LTS,避免 Debian 源版本过老 -- 修改 ImageMagick policy.xml 允许读写 PDF -- 设置 `NODE_PATH=/usr/lib/node_modules` - -## 已修改文件 - -### 1. `docker/terminal.Dockerfile` - -主要变更: -- apt 安装段加入 libreoffice、pandoc、poppler-utils、imagemagick、ffmpeg、tesseract、chromium、中文字体、开发工具 -- 增加 ImageMagick PDF 策略调整 RUN 层 -- 增加 NodeSource Node 20 安装 -- 增加 `npm install -g docx pptxgenjs` -- 末尾添加 `ENV NODE_PATH="/usr/lib/node_modules"` - -### 2. `docker/toolbox-requirements.txt` - -新增: -- `defusedxml` -- `markitdown[docx]` -- `pytesseract` -- `pdf2image` - -## 本地实验进度 - -### 实验环境 - -- 本地 macOS 15.6.1, arm64 -- Docker Desktop -- 构建命令: - ```bash - cd /Users/jojo/Desktop/agents/正在修复中/agents - docker build -f docker/terminal.Dockerfile -t my-agent-shell:local . - ``` - -### 实验结果 - -| 指标 | 结果 | -|------|------| -| 构建成功 | ✅ 是 | -| 新镜像大小 | **2.9 GB**(原镜像 913 MB) | -| 镜像增加 | 约 **2 GB** | -| 空闲内存 | ~10 MB | -| 单任务峰值内存 | ~40 MB | -| 并发 5×soffice + 1×chromium 峰值内存 | **~237 MB** | - -### 功能验证(全部通过) - -| 测试项 | 结果 | -|--------|------| -| `soffice --headless --convert-to pdf` docx→pdf | ✅ | -| `pandoc docx -o md` | ✅ | -| `pdftoppm` pdf→png | ✅ | -| `markitdown` 读取 docx/pptx | ✅ | -| `python-docx` / `python-pptx` | ✅ | -| Node `docx-js` 生成 docx | ✅ | -| Node `pptxgenjs` 生成 pptx | ✅ | -| ImageMagick 图片裁剪 | ✅ | -| matplotlib 生成中文字体表格图片 | ✅ | -| tesseract OCR 中英文 | ✅(可运行,中文识别准确度一般) | -| chromium 网页截图 | ✅ | -| `pdf2image` PDF 转图片 | ✅ | -| docx/pptx skill 脚本 import | ✅ | - -### 遇到的问题与解决 - -1. **markitdown 读 docx 报 MissingDependencyException** - - 原因:默认 `markitdown` 不包含 docx converter 的 `mammoth` 依赖 - - 解决:改成 `markitdown[docx]` - -2. **Node 全局安装的 `docx`/`pptxgenjs` require 不到** - - 原因:node 默认不搜索 `/usr/lib/node_modules` - - 解决:Dockerfile 添加 `ENV NODE_PATH="/usr/lib/node_modules"` - -3. **chromium 运行时有 dbus 报错** - - 不影响截图结果,属于 headless 环境下的常见警告 - -4. **ImageMagick 默认禁止 PDF 处理** - - 解决:修改 `/etc/ImageMagick-6/policy.xml` 中 PDF 的 rights 为 `read|write` - -5. **onnxruntime 启动警告** - - 警告:`Unknown CPU vendor. cpuinfo_vendor value: 0` - - 不影响功能,arm64 容器中的常见提示 - -## 尚未完成的工作 - -1. **云端构建验证** - - 当前只在本地 arm64 构建验证 - - 需要在云端 x86_64 上实际构建 - - 云端磁盘紧张(只剩 4GB),需先清理或分阶段构建 - -2. **精简镜像体积(可选)** - - 2.9GB 较大,可考虑去掉 chromium/ffmpeg 做成"最小方案 B"(约 1.5GB) - - 或保留完整方案 B,但需确保云端有足够空间 - -3. **react-icons / react / react-dom / sharp** - - pptxgenjs skill 文档中提到图标生成需要这几个包 - - 当前未预装,需要时可在运行时安装或后续加入镜像 - - `sharp` 涉及原生编译,构建时间和失败风险较高 - -4. **更详细的内存压力测试** - - 当前只做了简单并发测试 - - 未测试大文件转换、长时间运行场景 - -5. **ImageMagick 安全策略复核** - - 当前为支持 PDF 放宽了限制,需确认是否符合安全要求 - -## 下一步建议 - -### 推荐路径:分阶段上云端 - -1. **阶段一:精简必要包** - - 去掉 chromium、ffmpeg、imagemagick、tesseract - - 保留:libreoffice、pandoc、poppler-utils、node、npm、docx、pptxgenjs、字体、defusedxml、markitdown[docx] - - 目标镜像约 1.3-1.5GB - - 先验证 Office skill 能跑通 - -2. **阶段二:按需加回媒体能力** - - 根据实际使用频率,决定是否加回 imagemagick、ffmpeg、chromium、tesseract - -3. **阶段三:优化构建缓存** - - 考虑把 apt 层拆成多个 RUN,便于失败后重试 - - 云端构建时加 `--progress=plain` 便于排查 - -## 关键命令 - -```bash -# 本地构建 - cd /Users/jojo/Desktop/agents/正在修复中/agents - docker build -f docker/terminal.Dockerfile -t my-agent-shell:local . - -# 本地测试容器 - docker run -d --name agent-test-local my-agent-shell:local tail -f /dev/null - -# 验证基础依赖 - docker exec agent-test-local bash -c "which soffice pandoc pdftoppm node npm convert ffmpeg chromium tesseract jq rg fdfind tree" - -# 验证 Python 包 - docker exec agent-test-local bash -c "/opt/agent-venv/bin/python -c 'import defusedxml, markitdown, pytesseract, pdf2image'" - -# 验证 Node 包 - docker exec agent-test-local bash -c "node -e 'require(\"docx\"); require(\"pptxgenjs\"); console.log(\"OK\")'" - -# 查看镜像大小 - docker images my-agent-shell:local - -# 监控运行时内存 - docker stats agent-test-local --no-stream -``` - -## 重要约束 - -- **本地 arm64,云端 x86_64,禁止把本地构建的镜像上传到云端** -- 云端磁盘只剩 4GB,必须清理或分阶段构建 -- 云端内存 2GB,构建/运行需警惕 OOM -- 云端网络差,大文件下载可能超时 - -## 相关文件 - -- `docker/terminal.Dockerfile` -- `docker/toolbox-requirements.txt` -- 项目记忆:`cloud_server_disk_usage`(服务器磁盘现状) diff --git a/.astrion/memory/frontend_debug_log_to_file.md b/.astrion/memory/frontend_debug_log_to_file.md deleted file mode 100644 index 1a2efac0..00000000 --- a/.astrion/memory/frontend_debug_log_to_file.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -name: frontend_debug_log_to_file -description: 当需要把前端日志输出到后端文件(如调试目标模式、轮询状态等)时,应该索引本记忆 ---- - -# 前端日志输出到文件 - -## 背景 - -项目已内置一套轻量级的前端 → 后端日志上报机制,用于把浏览器端的状态变化/调试信息持久化到服务器日志文件,便于复现和排查问题。 - -## 基础设施 - -### 后端端点 - -- 文件:`server/app_legacy.py` -- 端点:`POST /api/client_debug_log` -- 写入函数:`log_goal_mode_debug_entry(data: Dict[str, Any])` -- 日志文件:`{LOGS_DIR}/goal_mode_debug.log` - - 宿主机模式示例:`/Users/jojo/.astrion/astrion/host/logs/goal_mode_debug.log` - -### 前端函数 - -- 文件:`static/src/app/methods/common.ts` -- 函数:`goalModeDebugLog(event: string, payload: Record = {})` -- 默认关闭,避免日常刷屏 - -## 启用方式 - -### 方式一:浏览器控制台临时开启(无需改代码) - -```js -localStorage.setItem('goalModeDebug', '1') -// 刷新页面后生效 -``` - -### 方式二:代码里强制开启 - -在 `common.ts` 中修改 `isGoalModeDebugEnabled()`,让它默认返回 `true`: - -```ts -function isGoalModeDebugEnabled() { - // ... - return true; // 强制开启 -} -``` - -### 方式三:运行时开关 - -```js -window.__GOAL_MODE_DEBUG__ = true -``` - -## 使用方式 - -在前端需要记录状态的位置调用: - -```ts -import { goalModeDebugLog } from './common'; - -goalModeDebugLog('event_name', { - conversationId: this.currentConversationId, - someState: this.someState, - // 其他需要记录的字段 -}); -``` - -调用后会: -1. 在浏览器控制台输出 `[GOAL_MODE_DEBUG]` 前缀日志 -2. 通过 `fetch('/api/client_debug_log', ...)` 上报到后端 -3. 后端写入 `goal_mode_debug.log` - -## 注意事项 - -- 日志默认关闭,只有显式开启后才会输出 -- `fetch` 调用使用 `void` 前缀,不会阻塞业务流程 -- 上报失败会被静默捕获,不影响主流程 -- 如需长期保留某类日志,应把调用代码保留在目标文件中;临时调试后建议清理调用点,只保留 `common.ts` 和后端端点基础设施 - -## 历史应用 - -- 曾用于排查"第一个对话开启目标模式后停止,目标状态被错误继承到下一个对话"的问题 -- 修复代码位于 `server/tasks/api.py`: - - `/api/tasks//cancel` 取消任务时同步停止工作区目标状态 - - `POST /api/tasks` 创建非目标模式任务时清理残留的活动目标状态 diff --git a/.astrion/memory/ocr_client_initialization.md b/.astrion/memory/ocr_client_initialization.md deleted file mode 100644 index 79b9ad3d..00000000 --- a/.astrion/memory/ocr_client_initialization.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: ocr_client_initialization -description: 当 OCRClient/OpenAI 初始化报错、WebTerminal 创建失败、或升级 openai 包时,应该索引本记忆 ---- - -# OCRClient 初始化与 OpenAI 包兼容性 - -## 问题 - -`modules/ocr_client.py` 在 `__init__` 中直接创建 `OpenAI(api_key=OCR_API_KEY)`。 - -- `openai` 包较新版本(>= 2.40)在 `api_key` 为空时会抛出 `openai.OpenAIError: Missing credentials` -- 服务器 settings.json 通常未配置 `OCR_API_KEY` -- 这会导致 `WebTerminal`/`MainTerminal` 初始化失败,所有依赖 terminal 的 API 都返回 500 - -## 修复 - -在 `OCRClient.__init__` 中,仅在 `OCR_API_KEY` 存在时才创建 `OpenAI` 客户端: - -```python -self.client = None -if OCR_API_KEY: - self.client = OpenAI( - api_key=OCR_API_KEY, - base_url=base_url or None, - http_client=self.http_client, - ) -``` - -在 `vlm_analyze` 中同时检查 `self.client`: - -```python -if not self.client or not OCR_API_KEY or not OCR_API_BASE_URL or not self.model: - return {"success": False, "error": "VLM 配置缺失,请设置 OCR_API_BASE_URL / OCR_API_KEY / OCR_MODEL_ID", "warnings": warnings} -``` - -## 教训 - -- 升级依赖包后要检查破坏性变更 -- 可选功能(OCR/VLM)的客户端初始化不应阻塞主终端启动 -- 服务器环境缺少某个 API key 时,应优雅降级而不是直接崩溃 diff --git a/.astrion/memory/opencode_key_replacement_script.md b/.astrion/memory/opencode_key_replacement_script.md deleted file mode 100644 index 6dc91467..00000000 --- a/.astrion/memory/opencode_key_replacement_script.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -name: opencode_key_replacement_script -description: 当需要批量替换 ~/.astrion/astrion、~/.astrion/astrion-clone 和服务器 /opt/agent/runtime 中的 OpenCode API key 时,应该索引本记忆 ---- - ---- -name: opencode_key_replacement_script -description: 当需要批量替换 ~/.astrion/astrion、~/.astrion/astrion-clone 和服务器 /opt/agent/runtime 中的 OpenCode API key 时,应该索引本记忆 ---- - -# OpenCode API key 批量替换脚本 - -## 脚本位置 - -项目根目录下的 `replace_opencode_keys.py`。 - -## 作用 - -一次性批量替换以下目录中所有 OpenCode 相关的 API key: - -- 本地:`/Users/jojo/.astrion/astrion` -- 本地:`/Users/jojo/.astrion/astrion-clone` -- 服务器:`/opt/agent/runtime/`(或服务器上 `ASTRION_DATA_ROOT` 指向的目录) - -## 扫描范围 - -仅扫描配置文件,自动跳过运行时数据: - -- `settings.json` -- `config/*.json` -- 跳过 `data/`、`logs/`、对话历史等目录 -- 跳过已有的 `.bak_` / `.bak` 备份文件 - -## 替换规则 - -脚本会查找已知的旧 OpenCode key 值(被多个 OpenCode Go 相关环境变量共用的同一串 key),并将其替换为新的 OpenCode key。 - -实际会改到的键名通常包括: - -- `settings.json` 中 `env_vars` 下的: - - `API_KEY_OPENCODE` - - `AGENT_API_KEY` - - `AGENT_THINKING_API_KEY` - - `AGENT_TITLE_API_KEY` - - 指向 `opencode.ai` 的 `API_KEY_KIMI`、`API_KEY_DEEPSEEK`、`API_KEY_QWEN`、`API_KEY_MINIMAX` 等 -- `config/sub_agent_models.json` 中 `url` 为 `https://opencode.ai/zen/go/v1` 的模型条目的 `apikey` - -`config/custom_models.json` 中一般使用 `${API_KEY_OPENCODE}` 变量引用,key 本身存在 `settings.json`,脚本不会直接修改它。 - -## 用法 - -```bash -# 只预览,不写入(循环轮换模式) -python3 replace_opencode_keys.py --dry-run - -# 正式执行循环轮换(默认:key1→key2, key2→key3, key3→key1) -python3 replace_opencode_keys.py - -# 单次指定替换(覆盖默认循环模式) -python3 replace_opencode_keys.py --old-key <旧key> --new-key <新key> -``` - -## 服务器替换流程 - -1. 确保脚本已上传至 `/opt/agent/agents/replace_opencode_keys.py` -2. 在服务器上修改脚本,把 `Path("/opt/agent/runtime")` 加入 `TARGET_DIRS` -3. 先执行 `--dry-run` 预览影响范围 -4. 去掉 `--dry-run` 正式执行 -5. 执行 `bash /opt/agent/refresh_agents.sh` 重启 webapp 服务使配置生效 -6. 删除生成的 `.bak_时间戳` 备份文件 - -## 服务器执行方式建议 - -- **推荐**:通过 `terminal_session` 打开一个 SSH 会话,在会话里直接运行 `python3 replace_opencode_keys.py ...` -- **次选**:使用服务器虚拟环境的完整路径,例如 `/opt/agent/venv/bin/python3 replace_opencode_keys.py ...` -- **注意**:在旧版本或未重启前,通过 `run_command` 直接执行 `ssh host "python3 ..."` 时,`run_command` 可能把命令中的 `python3` 重写为本地 macOS 的 Python 绝对路径,导致远程服务器找不到解释器。该问题已在 `modules/terminal_ops/run.py` 中修复:当命令通过 `ssh` 在远程执行时,跳过 `python3`/`pip` 重写。 - -## 注意事项 - -- 执行前会自动在同级目录生成 `.bak_时间戳` 备份。 -- 如果在沙箱/受限执行环境下运行,原目录不可写时脚本会把备份和修改后的文件写到项目工作区的 `opencode_key_replacement_backup/` 目录下,需要手动复制覆盖。 -- 建议执行后删除生成的 `.bak_时间戳` 备份文件。 -- 服务器上配置文件修改后,需要重启 webapp 服务才能生效。 - -## 历史记录 - -- 2026-06-19:使用脚本将本地两个目录的配置从主 key 替换为备用 key,并删除备份。 -- 2026-06-19:将服务器 `/opt/agent/runtime/` 下的 `settings.json` 和 `config/sub_agent_models.json` 也从主 key 替换为备用 key,并删除备份。 -- 2026-06-24:将服务器 `/opt/agent/runtime/` 下的 `settings.json`(9 处)和 `config/sub_agent_models.json`(1 处)从备用 key 替换回主 key,随后执行 `bash /opt/agent/refresh_agents.sh` 重启 webapp 服务,并删除备份。期间发现 `run_command` 执行 `ssh host "python3 ..."` 会错误重写 `python3` 为本地 macOS 路径,已修复 `modules/terminal_ops/run.py`。 -- 2026-06-25:重构脚本为三 key 循环轮换模式(key1/key2/key3,去掉主/备用命名)。将本地 `~/.astrion/astrion` 和 `~/.astrion/astrion-clone` 的配置从 key2 替换为 key3(4 文件 20 处)。服务器尚未操作,当前仍为 key1。 -- 2026-06-25:将本地 `~/.astrion/astrion` 和 `~/.astrion/astrion-clone` 的配置从 key2 替换为 key3(4 文件 20 处),删除备份。服务器仍为 key1。 - -## 相关 key 记录 - -| 说明 | Key | -|---|---| -| key1 | `sk-zNU867TGGXNKxoM0H6Pg6Jq4PYUPUOPDgMlzrOl3SS6jECRMWK5nNi6YMSsHzoZ` | -| key2 | `sk-sgt4ERgERPx2axU18PSfmenHv9zNDD3y8M5yr0p4ryhcNSm04CZsCxDHftJ9AkFD` | -| key3 | `sk-Jhvi76vJdCEQzvEdl8cAS5MIwBxBS4iTrJYSru2zCAXVnDVQpiZIvrcn9hBwkdZ4` | -| legacy(已停用,等同 key1 轮换到 key2) | `sk-j0NdTvEDoqqkKCHEAFXXYneEsuXEiOXNCgLeZARJmfYUBf2Riym7KPFWq1amkB6z` | diff --git a/.astrion/memory/pywebview_macos_packaging.md b/.astrion/memory/pywebview_macos_packaging.md deleted file mode 100644 index f2bd15bf..00000000 --- a/.astrion/memory/pywebview_macos_packaging.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -name: pywebview_macos_packaging -description: 当打包 PyWebview macOS App、调试 "The string did not match" 错误、修改 _packaging/ 目录时,应该索引本记忆 ---- - -# PyWebview macOS 打包项目记忆 - -## 目标 -将 Vue 3 + Python Flask 项目打包为 macOS 桌面 App(PyWebview),不使用 Electron/Tauri 或原生 macOS 对话框。 - -## 核心文件 -| 文件 | 作用 | -|------|------| -| `_packaging/app.py` | PyWebview 主入口:检测 `.env` → 直接启动 / setup页面 | -| `_packaging/setup.html` | 首次设置页面(4步:工作区→模型→管理员→确认),classic暖奶油主题 | -| `_packaging/app.spec` | PyInstaller 打包配置 | -| `_packaging/dist/AI-Agent-System.app` | 打包产物 | -| `_packaging/AI-Agent-System-v8.dmg` | 最后一个可用DMG(527MB) | - -## 关键修复历史 - -### 1. DMG 禁止标志 -- **原因**:DMG 内同时存在 `AI-Agent-System/`(onedir目录)和 `AI-Agent-System.app` -- **修复**:DMG 只放 `.app` + `Applications` 符号链接 -- **hdiutil 创建需完全访问权限**:当前受限制,需切换到完全访问权限模式 - -### 2. OpenSSL 符号冲突 -- **原因**:`_ssl.cpython-311-darwin.so` 加载 psycopg2 自带的旧版 `libcrypto.3.dylib` -- **修复**:用 Homebrew OpenSSL 3.6.2 的 dylib 替换 bundle 中 `__dot__dylibs` 目录 - -### 3. `.env` 加载路径 -- **修复**:`config/__init__.py` 的 `_load_dotenv()` 改为 frozen 时读 `~/.astrion/astrion/host/.env` - -### 4. engineio Invalid async_mode -- **原因**:PyInstaller frozen 环境缺少 `engineio.async_drivers.threading` -- **修复**:`app.spec` hiddenimports 添加 `'engineio.async_drivers.threading'` - -### 5. PyWebview 6.x private_mode 🚨 **关键问题** -- **现象**:Playwright 浏览器中正常(0 errors),PyWebview App 中所有 API 返回 HTML 导致 "The string did not match the expected pattern" -- **根因**:`webview.start()` 默认 `private_mode=True`,WKWebView 隐私模式下 cookie 不工作,session 丢失 -- **修复**:`webview.start(private_mode=False)` - -### 6. App Transport Security -- **修复**:`app.spec` Info.plist 添加 `NSAppTransportSecurity: { NSAllowsLocalNetworking: True }` - -### 7. 环境变量污染 -- **问题**:旧环境变量 `AGENTS_HOST_HOME=~/.agents/host-clone` 导致数据目录错误(现已被 `ASTRION_DATA_ROOT` 替代) -- **注意**:`~/.astrion/astrion-clone` 是本对话副本数据,不要动 -- **修复**:`_start_flask` 导入前清除 `ASTRION_DATA_ROOT`/`DATA_DIR`/`LOGS_DIR` 等旧环境变量污染 - -### 8. JSON 错误处理器 -- **修复**:`_start_flask` 中注册全局 error handler,确保任何异常都返回 JSON 而非 HTML - -## 当前状态(2026-06-12) - -### ✅ 已确认正常 -- Flask 后端在浏览器中完全正常(curl/Playwright 测试 0 errors) -- 登录、对话列表、数据加载均正常 -- API 返回纯 JSON - -### ❌ 仍存在问题 -- **PyWebview App 中仍然无数据**,前端报 "The string did not match the expected pattern" -- 已排除:backend 崩溃、engineio 错误、private_mode、ATS、环境变量污染 -- **问题定位**:在 PyWebview 的 WKWebView 内部,fetch API 请求被某种机制拦截/修改,导致返回非 JSON 响应 - -### 🔍 下一步排查方向 -1. 在 PyWebview 中启用远程调试(`webview.start(debug=True)`)查看 WKWebView console -2. 检查 PyWebview 6.x 的 `request_sent`/`response_received` 事件是否拦截了请求 -3. 检查 WKWebView 对 `127.0.0.1` 的 same-origin 策略是否有特殊处理 -4. 尝试用 `localhost` 替代 `127.0.0.1` -5. 考虑降级 PyWebview 到 5.x 或尝试其他打包方案(Tauri/Electron) - -## 打包命令 -```bash -# Python 必须是 Homebrew Python 3.11(Xcode Python 3.9 会 SIGKILL) -cd _packaging -pyinstaller --noconfirm --distpath ./dist --workpath ./build app.spec - -# 安装到 /Applications -cp -R dist/AI-Agent-System.app /Applications/ -codesign --force --deep -s - /Applications/AI-Agent-System.app -xattr -cr /Applications/AI-Agent-System.app - -# 创建 DMG(需完全访问权限) -hdiutil create -srcfolder dist/AI-Agent-System.app -volname "AI_Agent_System" -ov -format UDZO AI-Agent-System.dmg -``` - -## 用户要求 -- 禁止原生 macOS/浏览器组件 -- setup 页面需与项目设计风格统一 -- 只读一个 `.env`(数据存储目录 ~/.astrion/astrion/host/.env) -- 等待时间不要设置太长 -- `~/.astrion/astrion-clone` 是本对话副本数据,不要乱动 -- `~/.astrion/astrion/host` 是正常运行时数据 diff --git a/.astrion/memory/remaining_long_files_to_split.md b/.astrion/memory/remaining_long_files_to_split.md deleted file mode 100644 index 660a7ee4..00000000 --- a/.astrion/memory/remaining_long_files_to_split.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -name: remaining_long_files_to_split -description: 当需要继续拆分项目中的过长单个文件时,应该索引本记忆 ---- - -# 仍需拆分的长文件清单 - -> 更新于 2026-06-21。 -> 已完成的拆分:`server/chat/`、`server/status/`、`server/tasks/`、`utils/context_manager/`、`utils/conversation_manager/`、`utils/api_client/`、`utils/tool_result_formatter/`、`modules/file_manager/`、`modules/persistent_terminal/`、`modules/terminal_ops/`、`modules/mcp_client_manager/`、`core/main_terminal_parts/context/`、`core/main_terminal_parts/tools_definition/`、前端 `static/src/app/methods/{ui,message,upload,taskPolling,conversation}/`。 -> `static/src/components/chat/monitor/MonitorDirector.ts` 按用户要求不参与拆分。 - -下列文件仍是单个文件且超过约 500 行,**但 500 行只是参考线,不是硬性指标**。是否拆分取决于可维护性——优先拆**业务逻辑重、复用价值高、边界清晰**的文件;边界模糊、耦合紧密或改动频率低的文件,保持现状更划算。Vue SFC 文件因模板-样式-脚本耦合较深,拆分风险较高,建议先抽离 composable / 子组件,而非直接切分 `.vue`。 - -## 后端 Python(高优先级) - -| 文件 | 行数 | 建议拆分方向 | -|------|------|--------------| -| `core/main_terminal_parts/tools_execution.py` | ~1911 | 按执行阶段拆:准备 / 工具调度 / 结果处理 / 错误恢复 / 流式输出 | -| `server/chat_flow_task_main.py` | ~1739 | 拆为消息处理、guidance 队列、流式响应、任务生命周期、历史保存等 mixin | -| `server/conversation.py` | ~1981 | 拆为对话 CRUD、回顾(review)、压缩(compression)、待办联动等模块 | -| `server/chat_flow_tool_loop.py` | ~1391 | 按循环阶段拆:参数检查、工具调用、结果合并、循环终止 | -| `server/app_legacy.py` | ~1431 | 它是 legacy 入口,改动频繁则优先拆为注册/中间件/路由聚合 | - -## 后端 Python(中优先级) - -| 文件 | 行数 | 建议拆分方向 | -|------|------|--------------| -| `core/main_terminal.py` | ~661 | 拆 init / 状态 / 主循环 / 事件处理 mixin | -| `core/web_terminal.py` | ~710 | 拆轮询任务 / Socket.IO / REST 接口 / 前端状态同步 | -| `core/main_terminal_parts/commands.py` | ~877 | 拆命令解析、分发、内置命令处理 | -| `server/context.py` | ~725 | 拆 terminal/workspace 资源获取、会话装饰器、错误处理 | -| `server/admin.py` | ~728 | 按管理领域拆:用户/模型/策略/日志/系统配置 | -| `server/api_v1.py` | ~704 | 按资源拆子 blueprint | -| `modules/background_command_manager.py` | ~727 | 拆生命周期、输出流、持久化 | -| `modules/personalization_manager.py` | ~805 | 拆读取/合并/保存/校验 | -| `modules/terminal_manager.py` | ~756 | 拆会话池、命令执行、输出缓存 | -| `modules/user_manager.py` | ~779 | 拆认证、工作区管理、host/web 双路径 | -| `modules/versioning_manager.py` | ~842 | 拆版本记录、快照、回滚、清理 | - -## 前端 TypeScript / Vue(高优先级) - -| 文件 | 行数 | 建议拆分方向 | -|------|------|--------------| -| `static/src/components/input/InputComposer.vue` | ~2824 | **高风险**:先抽离 composable(输入状态、快捷键、粘贴处理)、子组件(附件列表、工具按钮),保留 `.vue` 外壳 | -| `static/src/components/personalization/PersonalizationDrawer.vue` | ~3547 | **高风险**:按设置分组拆成子组件 / tab panels,抽离表单校验 composable | -| `static/src/components/chat/ChatArea.vue` | ~1728 | 抽离消息列表、滚动行为、空状态、错误状态为子组件或 composable | -| `static/src/composables/useLegacySocket.ts` | ~1885 | 按事件类型拆为多个 handler 模块 | -| `static/src/admin/AdminDashboardApp.vue` | ~1959 | 按 admin 子页面拆为独立组件 | -| `static/src/admin/PolicyApp.vue` | ~1601 | 按策略维度拆为子表单 / 子组件 | -| `static/src/app/bootstrap.ts` | ~1339 | 按初始化阶段拆:配置加载、store 初始化、路由恢复、终端重建 | - -## 前端 TypeScript / Vue(中优先级) - -| 文件 | 行数 | 建议拆分方向 | -|------|------|--------------| -| `static/src/components/chat/actions/ToolAction.vue` | ~1532 | 拆工具状态展示、参数/结果渲染、审批交互 | -| `static/src/components/chat/actions/toolRenderers.ts` | ~1044 | 按工具类型拆 renderer | -| `static/src/App.vue` | ~882 | 抽离布局壳、侧边栏、全局弹窗 | -| `static/src/stores/personalization.ts` | ~924 | 按功能域拆 actions | -| `static/src/stores/task.ts` | ~632 | 若继续增长可拆任务/审批/队列 | -| `static/src/stores/tutorial.ts` | ~749 | 拆 tutorial 步骤、状态、触发逻辑 | -| `static/src/stores/monitor.ts` | ~677 | 目前可接受,继续增长再拆 | - -## CLI - -| 文件 | 行数 | 建议拆分方向 | -|------|------|--------------| -| `cli/src/App.tsx` | ~704 | 拆 views / screens / hooks | - -## 拆分原则(与 AGENTS.md 一致) - -- 优先用 **mixin / 子包** 模式,保持 `from xxx import Xxx` 兼容入口。 -- 拆 Flask blueprint 时,`__init__.py` 创建唯一 blueprint,子模块从包导入并保留装饰器。 -- 拆分后检查**跨模块共享函数/常量/类属性**,必要时抽到 `common.py` 或显式导入。 -- 前端 methods 拆完后,入口 `index.ts` 必须用 `...methodGroup` 平铺,避免嵌套对象导致 `this.xxx` 不存在。 -- 每次拆分后必须: - ```bash - python3 -m py_compile <对应包>/*.py - python -m unittest test.test_server_refactor_smoke - npm run build --silent 2>&1 | tail -n 5 - ``` diff --git a/.astrion/memory/runtime_state_loading_concurrency_redesign.md b/.astrion/memory/runtime_state_loading_concurrency_redesign.md deleted file mode 100644 index c55a2a6e..00000000 --- a/.astrion/memory/runtime_state_loading_concurrency_redesign.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: runtime_state_loading_concurrency_redesign -description: 当继续讨论/实现 conversation 级 WebTerminal 隔离(单对话单 terminal、同工作区多对话并行)、24h TTL 回收、子智能体按需加载、git worktree 时,应该索引本记忆 ---- - -# 运行状态对账 + Conversation 级 WebTerminal 隔离(全部已完成) - -> 2026-07-20 更新。完整实现记录见 `docs/conversation_level_terminal_design.md` §7(含文件级改动清单与验证结果)。 - -## 状态 -- 议题 1(运行状态 REST 对账):✅ 完成并验证。事件为主 + 2.5s 对账纠偏(`GET /api/conversations//running-status`,前端 `probe.ts` 单循环)。 -- 议题 4(conversation 级 WebTerminal 隔离):✅ 2026-07-20 完成并端到端验证。议题 2(子智能体按需加载)、议题 3(worktree)暂缓。 - -## 议题 4 核心架构(勿重新讨论,用户已拍板) -- terminal key = `username::workspace_id::conversation_id`(`server/context.py _make_terminal_key`);**container key 保持工作区级**(docker 一工作区一容器)。 -- `get_user_resources(..., conversation_id=None)` 可选参数;无对话上下文的 API(对话列表/api_v1/skills/conversation.py/socket connect)保持工作区级服务实例(shell 惰性)。 -- WebTerminal `_bound_conversation_id`,`_ensure_conversation` 加载绑定对话不 fallback;`last_activity_at` + 24h TTL 回收器(`reap_idle_conversation_terminals`,env `CONVERSATION_TERMINAL_TTL_SECONDS`)。 -- chat 互斥:同工作区 → 同对话(`tasks/models.py create_chat_task`,`_norm_cid` 比较)。 -- TerminalManager per 对话(每对话 max 3 终端、对话间分离);广播回调包装注入 conversation_id(`_wrap_callback_with_conversation_id`),前端 TerminalPanel `acceptTerminalBroadcast` 过滤 7 事件。 -- 前端 `currentWorkspaceHasRunningTask` 已改当前对话判定(名字未改,遗留更名)。 -- **行为变化**:SubAgentManager 按 `owner_conversation_id` 过滤恢复,重启后多智能体任务延迟到对话激活时恢复(工作区级 manager 不再恢复)。 - -## 验证资产 -- `_experiments/verify_conversation_level_terminal.py`(9 断言)、`_experiments/verify_running_status_api.py`。 -- 实测铁证:双对话并行 running、同对话 409、A 运行中 B 页面零锁定、终端 A/B 列表隔离、/api/terminals max_allowed=3。 - -## 边界修复轮次(commit 序列) -- `a4eb6888` 议题1+4 主体 → `1289c31d` 模型持久化四防线 → `6b5b1732` 二轮边界(压缩路由走对话级/回收器竞态防护/空cid补建对话)→ `e8bbaccf` idle 多智能体不算运行(对齐 socket 语义,防 REST 幽灵运行+回收器永久阻塞;idle 实例靠 restore_running_tasks 磁盘恢复兜底)。 -- 详见 `docs/conversation_level_terminal_design.md` §8/§9。 - -## 环境教训(2026-07-20 实测踩坑) -- **8092 端口是用户另一检出的服务**(cwd=`/Users/jojo/Desktop/外置/agents`),不是本仓库;打接口前必须 `lsof -nP -p | grep cwd` 确认进程归属。 -- 沙箱会拦 `kill`/`ps`(Operation not permitted),新服务在 socketio.run 绑端口时被杀;测服务相关改动优先进程内直调(如 `_experiments/test_reaper_unit.py` 19 断言)。 -- host 模式多实例共写同一 debug_stream.log 且启动即截断;import server.app 的测试脚本必须先设 `ASTRION_DATA_ROOT` 到 /tmp 隔离。 -- 短 TTL 回收实测(CONVERSATION_TERMINAL_TTL_SECONDS=120)尚未在真实服务验证,待用户下次自然重启服务后补测(单元测试已覆盖逻辑)。 - -## 遗留 -- 测试对话 conv_20260720_001402_595/857(语音工作区,A 内有 test-shell-a);张雪峰对话 conv_20260718_005231_752 有测试消息;conv_20260720_032131_953/032132_014(TTL 测试误建,在共享数据里,可删)。 -- 既有 bug 未修:assistant reasoning_content 前端渲染两遍(历史加载路径)。 -- 服务验证用 host-login 后必须重新 GET /api/csrf-token(login 会 force_new token);启动服务不要用 `| head` 管道(Broken pipe 杀任务)。 diff --git a/.astrion/memory/server_deployment_venv.md b/.astrion/memory/server_deployment_venv.md deleted file mode 100644 index c06f81ff..00000000 --- a/.astrion/memory/server_deployment_venv.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: server_deployment_venv -description: 当服务器部署时遇到 venv/pip 损坏、Python 路径错误,或需要更新部署脚本时,应该索引本记忆 ---- - -# 服务器 venv 部署注意事项 - -## 背景 - -服务器 `/opt/agent/venv` 最初是从 macOS 复制到 Linux(Ubuntu 24.04)的,导致: - -- `python3` 等符号链接指向 macOS 路径(`/Library/Developer/...`) -- `pip`、`flask` 等脚本的 shebang 指向 macOS 路径或错误路径 -- 结果:`python3` 可能能用,但 `pip` 无法执行 - -## 修复方法 - -### 1. 重建 venv(推荐) - -在服务器上用 Linux 系统 Python 重建: - -```bash -/usr/bin/python3.12 -m venv --clear /opt/agent/venv -/opt/agent/venv/bin/pip install -r /opt/agent/agents/requirements.txt -``` - -### 2. 修改后的部署脚本 - -#### `/opt/agent/start_webapp_new.sh` - -`fix_venv()` 同时检查 `python3` 和 `pip` 是否可用: - -```bash -fix_venv() { - if [ -x "$VENV_DIR/bin/python3" ] && "$VENV_DIR/bin/python3" --version &>/dev/null && \ - [ -x "$VENV_DIR/bin/pip" ] && "$VENV_DIR/bin/pip" --version &>/dev/null; then - return 0 - fi - # ... 重建 venv ... -} -``` - -#### `/opt/agent/refresh_agents.sh` - -安装依赖前验证 `pip` 是否可执行,不可执行则自动重建 venv: - -```bash -VENV_DIR="/opt/agent/venv" -if [ -x "$VENV_DIR/bin/pip" ] && "$VENV_DIR/bin/pip" --version &>/dev/null; then - "$VENV_DIR/bin/pip" install -r "$AGENTS_DIR/requirements.txt" -q 2>&1 | tail -3 -else - log "venv pip 不可用,重建虚拟环境..." - sys_python=$(command -v python3.12 2>/dev/null || command -v python3 2>/dev/null || command -v python 2>/dev/null || true) - if [ -z "$sys_python" ]; then - log "错误: 找不到系统 Python" - exit 1 - fi - "$sys_python" -m venv --clear "$VENV_DIR" - "$VENV_DIR/bin/pip" install -r "$AGENTS_DIR/requirements.txt" -q 2>&1 | tail -3 - log "虚拟环境重建完成" -fi -``` - -## 注意事项 - -- 服务器内存只有 2G,前端构建不在服务器执行,由本地 `deploy_agents.sh` scp 上传 `static/dist/` -- 以后部署不要再从 macOS 复制 venv 到服务器 -- 如果 pip 包版本升级引入破坏性变更(如 `openai>=2.40` 强制要求 `api_key`),需要同步修复代码 diff --git a/.astrion/memory/server_frontend_build_oom.md b/.astrion/memory/server_frontend_build_oom.md deleted file mode 100644 index 55a99b70..00000000 --- a/.astrion/memory/server_frontend_build_oom.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: server_frontend_build_oom -description: 当服务器执行前端构建失败、OOM、或需要部署前端时,应该索引本记忆 ---- - -# 服务器前端构建 OOM - -## 问题 -服务器(2G 内存 Linux)执行 `npm run build` 时因内存不足导致 OOM,进程被 kill,服务器可能 SSH 超时无响应。 - -## 解决方案 -**不在服务器上构建前端。** 在本地(macOS,充足内存)构建,然后 scp 上传 `static/dist/`。 - -## 操作步骤 -```bash -# 本地构建 -cd agents && npm run build - -# 打包上传 -tar czf /tmp/static_dist.tar.gz static/dist/ -scp /tmp/static_dist.tar.gz root@59.110.19.30:/tmp/ -ssh root@59.110.19.30 "cd /opt/agent/agents && tar xzf /tmp/static_dist.tar.gz" -``` - -## 已落实到脚本 -- `deploy_agents.sh`(本地):自动本地 build + 上传 -- `refresh_agents.sh`(服务器):已移除 npm build 步骤,注释说明原因 diff --git a/.astrion/memory/slash_menu_highlight_animation.md b/.astrion/memory/slash_menu_highlight_animation.md deleted file mode 100644 index bff66a34..00000000 --- a/.astrion/memory/slash_menu_highlight_animation.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: slash_menu_highlight_animation -description: 当修改/菜单选中高亮动画时,应该索引本记忆 ---- - -## / 菜单选中高亮动画 — 设计教训 - -### 最终方案 -- 提取 `skill-slash-menu-wrapper` 外壳层,高亮条(`.skill-slash-menu__highlight`)放在 wrapper 内但**滚动容器外**,用 `position: absolute` 相对于 wrapper 定位 -- 用一个 `requestAnimationFrame` 循环**同时驱动 scrollTop 和 slashHighlightTop**,相同 easing(easeOutCubic)、相同时间(180ms),帧帧同步 -- 不使用 CSS transition、scroll-behavior: smooth、scroll 事件 - -### 踩过的坑 -1. **高亮条必须在滚动容器外**:放在内部会随内容滚动,钉死在中间的效果无法实现 -2. **CSS transition + scroll-behavior: smooth 无法精确同步**:easing 曲线和时间窗口不完全匹配,导致运动快慢不一致 -3. **scroll 事件有盲区**:当 scrollTop 不变(边界、所有项可见)时不触发,导致高亮条卡死 -4. **底部 5px 偏差**:`maxScroll` 不是 step(43px) 的整数倍,用 `visibleRows` 推算永远无法精确对齐。正确做法:`highlightTop = gap + index * step - targetScroll`,公式本身就保证了精确对齐 - -### 中间区域"固定"原理 -高亮条和列表以相同距离(step)、相同时间(180ms)、相同 easing 反向运动,视觉上高亮条钉在屏幕中间不动,只有列表在滚。到达滚动极限时,高亮条才开始实际移动。 - -### 已选中项 hover 不叠加 -`.skill-slash-item--active:hover` 设为 `background: transparent`,避免高亮条 + hover 两层背景叠加。 diff --git a/.astrion/memory/sub_agent_network_permission.md b/.astrion/memory/sub_agent_network_permission.md deleted file mode 100644 index b602a19f..00000000 --- a/.astrion/memory/sub_agent_network_permission.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: sub_agent_network_permission -description: 当需要理解或调整子智能体在受限网络下的行为时,应该索引本记忆 ---- - -# 子智能体进程网络权限设计 - -## 问题 -当用户把「网络权限」设置为受限(`HOST_SANDBOX_NETWORK_PERMISSION=restricted`)时,创建子智能体会失败,子智能体进程内报 `fetch failed (ENOTFOUND)`。原因是子智能体进程本身也被加上了与工具命令相同的宿主机沙箱,导致它无法解析模型 API 域名、无法调用模型接口。 - -## 解决方案 -子智能体进程使用独立的网络权限配置 `SUB_AGENT_HOST_NETWORK_PERMISSION`(默认 `full`),与工具级 `HOST_SANDBOX_NETWORK_PERMISSION` 解耦。 - -- 子智能体本身需要调用模型 API,因此默认拥有完整网络,避免在受限网络下直接无法启动。 -- 主进程对普通 `run_command` / `terminal_input` 等命令单独施加 `HOST_SANDBOX_NETWORK_PERMISSION` 沙箱,这部分限制不受影响。 -- 子智能体内部执行的 `run_command` 目前不额外加沙箱(macOS 的 `sandbox-exec` 不支持嵌套沙箱,无法在同一个子智能体进程内再套一层)。 - -## 配置项 -- `config/sub_agent.py` 新增 `SUB_AGENT_HOST_NETWORK_PERMISSION`,可通过环境变量覆盖,默认 `full`。 -- `modules/sub_agent_manager.py` 在构建宿主机沙箱启动计划时使用该配置,而不是直接使用 `HOST_SANDBOX_NETWORK_PERMISSION`。 - -## 注意事项 -- 若未来需要在子智能体内部也对命令做网络限制,必须采用非嵌套方案(例如由主进程提供本地代理/执行服务,或让子智能体通过 localhost 调用主进程代理后再外出)。不能简单在子智能体里再次调用 `sandbox-exec`。 -- 在 Docker/容器模式下,网络由容器自身策略控制,上述配置不影响容器行为。 diff --git a/.astrion/memory/sub_agent_python_rewrite.md b/.astrion/memory/sub_agent_python_rewrite.md deleted file mode 100644 index f42799f2..00000000 --- a/.astrion/memory/sub_agent_python_rewrite.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -name: sub_agent_python_rewrite -description: 当需要了解子智能体当前实现、修改子智能体代码、或排查子智能体相关问题时,应该索引本记忆 ---- - -# 子智能体现状(Python 主进程内协程) - -## 核心变更 - -子智能体已经从 **Node.js 子进程**(`easyagent/src/batch/index.js`)彻底改为 **Python 主进程内协程**。 - -## 代码位置 - -所有子智能体专用代码集中在 `modules/sub_agent/` 包内: - -``` -modules/sub_agent/ -├── __init__.py # 导出 SubAgentManager -├── manager.py # 任务调度、创建、终止、状态查询 -├── task.py # LLM 循环、模型调用、工具执行驱动 -├── toolkit.py # 8 个工具定义、工具结果格式化、模型配置解析 -├── prompts.py # 用户消息和系统提示词构建 -├── tools.py # search_workspace、read_mediafile 本地实现 -├── state.py # 状态加载、保存、刷新、超时、僵尸清理 Mixin -├── stats.py # 统计摘要与结果消息组装 Mixin -└── creation.py # 创建参数校验、任务 ID 生成、交付目录解析 Mixin -``` - -## 执行机制 - -- 子智能体作为 `asyncio.Task` 在独立的后台事件循环线程中运行(避免与 Flask-SocketIO threading 模式冲突)。 -- 所有实际工具调用都通过 `WebTerminal.handle_tool_call()` 执行,因此**自然复用主进程的宿主机沙箱 / Docker 容器链路**。 -- 子智能体自身不再被整体沙箱限制网络(因为它需要调用模型 API),网络调用走 `utils.api_client.DeepSeekClient`。 -- 权限固定为 `unrestricted`,但工具执行本身受主进程沙箱/容器/审批链路约束。 - -## 模型配置 - -- 子智能体模型独立配置,不跟主模型合并。 -- 配置文件:`~/.astrion/astrion/config/sub_agent_models.json`(由 `SUB_AGENT_MODELS_CONFIG_FILE` 指定)。 -- 默认读取该文件中的 `default_model` 和 `models` 列表。 - -## 可用工具(8 个) - -1. `read_file` - 读取/搜索/抽取文件 -2. `write_file` - 创建或覆盖文件(支持 append) -3. `edit_file` - 精确字符串替换,支持 `replacements` 数组和 `replace_all` -4. `run_command` - 执行命令(走主进程沙箱/容器) -5. `web_search` - 网络搜索 -6. `extract_webpage` - 网页内容提取 -7. `search_workspace` - 工作区搜索 -8. `read_mediafile` - 读取图片/视频文件(base64 返回) - -## easyagent/ 目录状态 - -- `easyagent/` 是旧的 Node.js 子智能体实现,目前**暂时保留但已不再使用**。 -- 主项目 Python 代码没有任何地方引用 `easyagent/`。 -- 是否删除待后续决策。 - -## 入口点 - -- 主项目创建子智能体:`modules.sub_agent.manager.SubAgentManager.create_sub_agent()` -- `core/main_terminal.py` 中通过 `SubAgentManager.set_terminal(self)` 注入 WebTerminal 引用。 - -## 回归测试 - -- 测试脚本:`test/test_sub_agent_regression.py` -- 覆盖:write_file、edit_file(多处替换 + replace_all)、search_workspace、read_mediafile、terminate - -## 已删除/清理的内容 - -- `config/sub_agent.py` 中已删除 `SUB_AGENT_HOST_NETWORK_PERMISSION` 临时配置。 -- `easyagent/src/batch/index.js` 已删除(旧子进程入口)。 -- `AGENTS.md` 和 `CLAUDE.md` 中关于子智能体的描述已更新为当前实现。