归档当前功能基线并完善工作区 Agent

This commit is contained in:
2026-07-16 16:23:43 +08:00
parent d8950e7648
commit d0d1de4d00
61 changed files with 10619 additions and 1196 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -109,6 +109,8 @@ Qt 版本:
## 本地文件操作
当前源码仍保留安全受限的本地文件操作 v1。后续将由本地工作区 Agent 替代;替代完成前,以下用例仍作为回归项保留。
| 用例 | 步骤 | 预期结果 | 结论 |
| --- | --- | --- | --- |
| 读取文本 | 输入读取文本文件请求并选择 txt/md/log | 显示内容预览,最多读取前 64KB | TODO |
@@ -122,6 +124,56 @@ Qt 版本:
| 符号链接拒绝 | 选择符号链接或链接目录内路径 | 提示不允许操作符号链接路径 | TODO |
| zip 延期 | 输入打包/压缩请求 | 提示 zip 打包暂不启用 | TODO |
## 本地工作区 Agent
v1a.1/v1b/v1b.5 已接入工作区只读分析、打开定位和独立 Agent 窗口;创建、修改、移动、删除到回收站仍属后续阶段。正式施工计划见 `docs/agent/DesktopPet_本地工作区Agent_施工计划.md`
| 用例 | 步骤 | 预期结果 | 结论 |
| --- | --- | --- | --- |
| 选择工作区 | 选择普通项目目录 | 显示当前工作区并扫描索引 | TODO |
| 系统目录拒绝 | 选择 `C:/Windows``Program Files` | 拒绝作为工作区 | TODO |
| 扫描跳过目录 | 工作区含 `.git/build/node_modules/release_packages` | 扫描跳过这些目录,结果数量受限制 | TODO |
| 搜索文件名 | 输入 `搜索 README` | 返回最多 30 个相对路径结果 | TODO |
| 独立 Agent 窗口 | 输入 `搜索 README` | 独立窗口展示上一轮结果,可选中结果;左侧结果/文本命中/上下文、中间对话、右侧详情分区清晰 | TODO |
| 窗口非客户区 | 打开独立 Agent 窗口 | 没有白色系统标题栏,标题区与整体深色 UI 协调,顶部可拖动移动窗口 | TODO |
| 搜索文本 | 输入 `搜索包含 class 的文件` | 返回路径、行号和片段,跳过二进制和敏感文件 | TODO |
| 紧凑文本搜索 | 输入 `含class的文件``包含 class 的文件``找一下含 class 的文件` | 走 Workspace 文本搜索,不弹旧文本文件选择框,关键词提取为 `class` | TODO |
| 文本命中窗口 | 输入 `搜索包含 class 的文件` | 独立窗口展示文本命中和关联文件列表,选中文本命中时右侧详情同步 | TODO |
| 读取文本文件 | 输入 `读取 main.cpp` | 读取工作区内文本文件,超大文件截断 | TODO |
| 窗口读取 | 搜索后在独立窗口选中结果并点击 `读取` | 读取选中文本文件,目录项读取按钮禁用 | TODO |
| 敏感文件拒绝 | 读取 `.env``*.pem``credentials.json` | 默认拒绝或强确认,不展示敏感内容 | TODO |
| 项目结构摘要 | 输入 `分析这个项目` | 显示本地结构摘要;AI 可用且空闲时补简短分析 | TODO |
| 文件总结 | 读取文件后输入 `总结这个文件` | 显示本地内容,AI 可用且空闲时补简短总结 | TODO |
| 文件分析同义词 | 读取 `main.cpp` 后输入 `分析这个文件``解释这个文件``看看这个文件` | 走 Workspace 最近读取文件分析,不弹旧文本文件选择框 | TODO |
| 文件名分析 | 输入 `分析 main.cpp``解释 README``main.cpp 是干嘛的` | 走 Workspace 文件读取和 AI 分析流程,不落入普通 AI 对话 | TODO |
| 总结无上下文 | 未读取工作区文件时输入 `总结这个文件` | 提示先读取一个工作区文件或输入具体文件名,不弹旧文件选择框 | TODO |
| 读取文件泛请求 | 输入 `读取文件` | 仍走旧 fileops 文本文件选择流程 | TODO |
| 上下文引用 | 先搜索 README,再输入 `读取第一个` | 正确引用上一轮结果;上下文过期则要求重新选择 | TODO |
| 打开文件 | 先搜索 README,再输入 `打开第一个` | 弹出确认框,确认后用系统默认程序打开;取消不打开 | TODO |
| 窗口打开 | 搜索后在独立窗口选中结果并点击 `打开` | 文件弹确认后打开;目录直接打开 | TODO |
| 打开目录 | 输入 `打开 docs``打开这个项目里的 docs` | 打开工作区内目录 | TODO |
| 显示位置 | 输入 `显示 main.cpp 所在位置``打开所在文件夹` | 打开工作区内文件所在目录 | TODO |
| 窗口显示位置 | 搜索后在独立窗口选中结果并点击 `位置` | 打开选中项所在目录 | TODO |
| 右键入口 | 右键桌宠选择 `Agent 工作区` | 独立 Agent 窗口打开;未选择工作区时显示空状态,不强制选择目录 | TODO |
| 托盘入口 | 托盘菜单选择 `Agent 工作区` | 独立 Agent 窗口打开;桌宠隐藏时也可直接打开窗口 | TODO |
| 默认快捷键 | 按 `Alt+A` | 呼出独立 Agent 窗口 | TODO |
| Agent 设置 | 设置页 `Agent` 页关闭快捷键或修改快捷键后保存 | 快捷键立即按新配置生效,非法/冲突快捷键有明确提示 | TODO |
| 自动显示设置 | 关闭 `工作区命令完成后自动显示 Agent 窗口` 后输入 `搜索 README` | 搜索正常完成,但独立窗口不自动弹出;手动入口仍可打开 | TODO |
| 打开敏感/危险文件拒绝 | 尝试打开 `.exe/.ps1/.lnk/.env/*.pem/credentials.json` | 明确拒绝,不调用系统打开 | TODO |
| 打开路径越界拒绝 | 尝试打开工作区外、系统目录、符号链接路径 | 明确拒绝 | TODO |
| 创建误分发修复 | 输入 `创建 test.txt` | 提示当前不支持创建/写入,不弹读取文本文件选择框 | TODO |
| 写入误分发修复 | 输入 `新建文件``写入 README``修改 main.cpp``修复 main.cpp``优化 main.cpp` | 明确拒绝当前阶段写操作,不弹读取文本文件选择框 | TODO |
| 危险能力拒绝 | 请求运行脚本、执行命令、自动安装依赖、自动 git commit | 明确拒绝,不调用系统命令 | TODO |
后续阶段验收项:
| 用例 | 步骤 | 预期结果 | 结论 |
| --- | --- | --- | --- |
| 创建文件 | 创建 `test.txt` | 展示计划,确认后创建;目标存在拒绝 | TODO |
| 修改文件 | 修改普通文本文件 | 展示计划,确认后写入;写入前生成备份 | TODO |
| 移动/重命名 | 移动或重命名单文件 | 展示计划,确认后执行;工作区外和覆盖目标拒绝 | TODO |
| 删除到回收站 | 删除普通文件 | 强确认后移动到回收站;永久删除和批量删除拒绝 | TODO |
## 联网模式
| 用例 | 步骤 | 预期结果 | 结论 |
@@ -193,6 +245,7 @@ UI 状态:
| 角色 | 设置页角色页和 `CharacterPackageRepository` | 导入先验证再复制;切换保存后立即生效;用户角色可删除;任意角色可导出;可打开用户角色目录 | 内置角色不可删除/覆盖;导出目标存在需二次确认;损坏角色包不复制 | 目标目录权限失败、用户取消、角色包资源缺失 | 当前角色管理补全完成,角色市场/分享延期 |
| 历史 | 设置页聊天页和 `ConversationManager/ConversationStore` | 新消息写入 `timestamp/provider/model`;旧 role/content 历史兼容读取;支持关键词、Provider、模型筛选;Markdown/JSON 导出 | 本地历史关闭时只显示内存记录;损坏历史备份后忽略;导出路径不可写时提示错误 | 超大历史由保存上限和筛选 UI 控制;导出文件由用户选择路径 | 历史管理补全完成,不导出 API Key,不记录完整聊天日志 |
| 文件操作 | `CommandDispatcher -> PetWindow::handleFileOperationChatMessage() -> FileOperationManager` | 用户通过系统对话框选择路径;读取文本、列目录、复制、备份、重命名;写操作先展示计划并二次确认 | 删除/覆盖/移动/脚本/命令/截图保存/zip 直接拒绝;系统目录、符号链接、目标冲突、非法文件名均拒绝 | 文件选择对话框是安全边界;系统目录根据常见路径和环境变量识别 | v1 安全能力完成,zip 打包延期,不引入重依赖 |
| 联网模式 | `ChatInputDialog -> PetWindow::submitWebChatMessage() -> WebChatManager` | 输入框联网开关开启后,支持 OpenAI 官方 Web Search 和 Gemini Google Search grounding;不支持 DeepSeek/Custom 伪联网 | AI 未配置、Provider 不支持、超时、网络失败、无来源均明确提示;Web 忙时不启动第二个请求 | 不再维护旧搜索源聚合或 SearXNG 旧配置 | 联网模式完成,结构化搜索 API/自建后端延期 |
| 本地工作区 Agent | `CommandDispatcher -> PetWindow::handleWorkspaceChatMessage() -> WorkspaceController -> WorkspaceAgent/WorkspaceTool -> WorkspaceAgentWindow` | v1b.5 用户选择工作区后,扫描索引、搜索文件/文本、读取文本文件、生成本地摘要,AI 可用时补简短分析,并支持打开普通文件、打开文件夹、显示所在位置、独立 Agent 窗口、菜单入口和全局快捷键 | 工作区外路径、系统目录、符号链接、危险后缀、敏感文件、命令执行均拒绝;打开文件前确认;AI 忙时不阻塞本地结果 | 路径归一化、符号链接逃逸、敏感文件判断、意图误判、全局快捷键冲突是核心风险;写操作仍未进入当前阶段 | v1a.1/v1b/v1b.5 独立窗口化已落地;低风险写操作计划留到后续 |
| 联网模式 | `ChatInputDialog -> PetWindow::submitWebChatMessage() -> WebChatManager` | 输入框联网开关开启后,支持 OpenAI 官方 Web Search 和 Gemini Google Search grounding;记住开关状态时用户切换会立即保存 | AI 未配置、Provider 不支持、超时、网络失败、无来源均明确提示;Web 忙时不启动第二个请求 | 不再维护旧搜索源聚合或 SearXNG 旧配置 | 联网模式完成,结构化搜索 API/自建后端延期 |
| 应用启动 | `CommandDispatcher -> PetWindow::handleLaunchAppChatMessage() -> AppLaunchManager` | 用户输入打开/启动应用后,按登记别名、开始菜单、App Paths、手选 exe 顺序解析,启动前二次确认 | 配置关闭、未找到应用、用户取消、非 exe/lnk、脚本/安装包、路径不存在均不启动;手选记住失败只提示,不影响已确认启动 | 启动成功由系统返回值判断,目标应用自身异常不由桌宠控制;开始菜单和注册表发现仅 Windows 优先 | v1 完成,不支持脚本、命令行参数、管理员权限、跨平台发现 |
| 发布/性能 | `tools/package_release.ps1``docs/performance_stability_check.md` | 发布脚本复制 exe、Qt runtime、角色/图标/音效资源、README、LICENSE;性能文档覆盖静置、AI、提醒、天气、联网模式、文件操作 | `windeployqt`、Inno Setup、安装/卸载验证需用户手动执行并记录 | 未经手动构建和实机安装不能确认二进制运行 | 验收入口已补齐,本轮不运行 CMake 或构建命令 |
+31
View File
@@ -0,0 +1,31 @@
# QtDesktopPet 文档索引
本文档用于区分当前有效文档和历史归档文档,避免后续开发继续引用过时路线。
## 当前有效文档
| 文档 | 用途 |
| --- | --- |
| `QtDesktopPet_后续功能规划与结构审查.md` | 当前功能状态、下一阶段路线和结构边界 |
| `QtDesktopPet_测试清单与验收标准.md` | 功能测试清单、回归验收标准和静态推演记录 |
| `performance_stability_check.md` | 性能采样、稳定性检查和发布验证入口 |
| `agent/DesktopPet_本地工作区Agent_施工计划.md` | 本地工作区 Agent 的正式施工计划 |
## 归档文档
历史文档统一放在 `docs/archive/`。这些文档保留为开发上下文,不再作为当前施工依据。
| 文档 | 归档原因 |
| --- | --- |
| `archive/implementation_plan.md` | 早期分阶段实施计划,当前大部分阶段已完成或被后续模块替代 |
| `archive/Qt_DesktopPet_开发文档.md` | 初版开发总纲,当前产品能力已超出第一版范围 |
| `archive/QtDesktopPet_后续功能规划与结构审查_旧版.md` | 旧后续规划过长,包含大量已完成阶段和旧文件操作路线 |
| `archive/DesktopPet 本地工作区 Agent 开发文档_草案.md` | 本地工作区 Agent 初稿,已整理为正式施工计划 |
## 维护规则
- 新功能进入开发前,先更新当前路线文档和对应测试清单。
- 功能完成后,同步更新 README、测试清单和性能检查项。
- 历史文档不再直接修改,除非补充归档说明。
- GitHub 公开导出不包含 `docs/`;开发仓库保留完整文档。
- 在构建卡住问题解决前,文档中的验证步骤仍以用户手动构建为准,不由 Codex 调用 CMake 或构建命令。
@@ -0,0 +1,869 @@
# DesktopPet 本地工作区 Agent 施工计划
## 1. 定位
本地工作区 Agent 是下一阶段核心功能。它不是继续维护旧版“文件操作 v1”,而是新增一个独立的工作区系统:
> 用户选择一个本地工作区目录后,桌宠可以读取、搜索、分析该目录内的文件,并在用户确认后创建、修改、复制、移动、重命名或删除到回收站。
第一阶段优先实现文件级工具,不做 Shell 命令执行,不做脚本执行,不做自动安装依赖。
## 2. 总原则
- 不允许 AI 直接获得全盘文件权限。
- 不允许 AI 自由执行系统命令。
- 不允许永久删除文件。
- 不允许绕过用户确认修改本地文件。
- 不允许默认读取敏感文件。
- 所有写操作必须先生成计划,展示路径、动作和风险,用户确认后才能执行。
- 所有文件路径必须限制在用户选择的工作区内。
- `PetWindow` 只负责 UI 入口、展示和确认,不承载复杂业务逻辑。
- 新模块先新增、后替换旧 `src/fileops/`
## 3. 分期目标
当前执行状态:
- v1a.1/v1b/v1b.5 已落地到 `src/workspace/``src/ui/WorkspaceAgentWindow.*`
- 当前不持久化工作区路径,不执行写操作,不替换旧 `src/fileops/`
- v1b 已支持打开工作区内普通文件、打开文件夹、显示文件所在位置。
- v1b.5 已提供独立 Agent 工作区窗口、右键/托盘入口和默认 `Alt+A` 呼出快捷键;窗口包含左侧结果导航、中间任务对话、右侧详情/上下文和 v1c 预留变更区。
### v1a:只读工作区
目标:
- 选择工作区。
- 显示当前工作区。
- 扫描工作区文件树。
- 搜索文件名。
- 搜索文本内容。
- 读取文本文件。
- 分析项目结构。
- 总结文件内容。
验收:
- 工作区外路径被拒绝。
- 系统目录被拒绝。
- `.git/build/node_modules/dist/release_packages` 等目录被跳过。
- 敏感文件默认拒绝读取。
- 二进制文件拒绝读取。
### v1b:打开和定位
状态:已落地。
目标:
- 打开工作区内普通文件。
- 打开工作区内文件夹。
- 显示文件所在位置。
验收:
- 危险后缀拒绝打开。
- `.lnk` 不直接打开。
- 工作区外路径拒绝。
- 打开文件必须二次确认,打开文件夹作为低风险操作直接执行。
### v1c:低风险写入
进入 v1c 前已完成:
- `创建 test.txt / 新建文件 / 写入 README / 修改 main.cpp` 明确拒绝,不再误进入旧 fileops 读取文件流程。
- 独立 Agent 窗口可以分区展示上一轮结果、文本命中、上下文、选中项详情和最近读取文件。
- 窗口支持对选中结果执行读取、打开和显示位置。
- 窗口可通过桌宠右键菜单、托盘菜单和可配置全局快捷键呼出。
目标:
- 创建文本文件。
- 创建文件夹。
- 复制单个文件。
- 同目录重命名文件。
验收:
- 所有写操作必须确认。
- 目标文件或目录已存在时拒绝覆盖。
- 文件名非法时拒绝。
- 危险后缀拒绝创建或写入。
### v1d:高风险写入
目标:
- 修改已有文本文件。
- 移动单个文件。
- 删除文件到回收站。
- 删除文件夹到回收站。
验收:
- 修改前必须备份。
- 移动和删除必须强确认。
- 删除只进入回收站。
- 批量删除第一阶段拒绝。
- 失败时不能破坏原文件。
### v1e:替换旧文件操作
目标:
- `FileOperation` 意图迁移为 `Workspace`
- `PetWindow::handleFileOperationChatMessage()` 过渡为调用 `WorkspaceController`
- 新模块覆盖旧能力后删除旧 `src/fileops/`
验收:
-`FileOperationManager` 有效引用。
- CMake 不再登记旧 fileops 文件。
- README 和测试文档改为“本地工作区 Agent”。
## 4. 新目录结构
建议新增:
```text
src/workspace/
├── WorkspaceTypes.h
├── WorkspaceSession.h
├── WorkspaceSession.cpp
├── WorkspaceIndex.h
├── WorkspaceIndex.cpp
├── WorkspaceScanner.h
├── WorkspaceScanner.cpp
├── WorkspaceAccessPolicy.h
├── WorkspaceAccessPolicy.cpp
├── WorkspaceTool.h
├── WorkspaceToolRegistry.h
├── WorkspaceToolRegistry.cpp
├── WorkspaceAgent.h
├── WorkspaceAgent.cpp
├── WorkspaceController.h
├── WorkspaceController.cpp
├── WorkspaceHistory.h
├── WorkspaceHistory.cpp
└── tools/
├── ListFilesTool.h
├── ListFilesTool.cpp
├── ReadFileTool.h
├── ReadFileTool.cpp
├── SearchFilesTool.h
├── SearchFilesTool.cpp
├── SearchTextTool.h
├── SearchTextTool.cpp
├── OpenFileTool.h
├── OpenFileTool.cpp
├── CreateFileTool.h
├── CreateFileTool.cpp
├── CreateDirectoryTool.h
├── CreateDirectoryTool.cpp
├── WriteFileTool.h
├── WriteFileTool.cpp
├── CopyFileTool.h
├── CopyFileTool.cpp
├── MoveFileTool.h
├── MoveFileTool.cpp
├── RenameFileTool.h
├── RenameFileTool.cpp
├── DeleteToTrashTool.h
└── DeleteToTrashTool.cpp
```
第一阶段继续保持 `CMAKE_AUTOMOC OFF`,新增业务类不使用 `Q_OBJECT`
## 5. 核心类型
```cpp
enum class WorkspacePermissionMode
{
ReadOnly,
ConfirmBeforeWrite,
Advanced
};
```
第一阶段默认 `ConfirmBeforeWrite`,但所有写操作仍必须确认。`Advanced` 只预留,不启用自动写入。
```cpp
enum class WorkspaceToolRisk
{
ReadOnly,
Low,
Medium,
High,
Dangerous
};
```
风险定义:
- `ReadOnly`:列目录、搜索、读取普通文本文件。
- `Low`:打开文件夹、显示文件所在位置。
- `Medium`:打开普通文件、创建文件、创建文件夹、复制文件。
- `High`:修改文件、移动文件、重命名文件、删除到回收站。
- `Dangerous`:永久删除、执行命令、运行脚本、修改系统目录。
第一阶段遇到 `Dangerous` 直接拒绝。
```cpp
struct WorkspaceFileInfo
{
QString relativePath;
QString absolutePath;
QString fileName;
QString suffix;
qint64 sizeBytes = 0;
QDateTime lastModified;
bool isDirectory = false;
bool isTextFile = false;
bool isHidden = false;
};
```
```cpp
enum class WorkspaceActionType
{
Unknown,
ListFiles,
SearchFiles,
SearchText,
ReadFile,
OpenFile,
OpenDirectory,
RevealInExplorer,
CreateFile,
CreateDirectory,
WriteFile,
CopyFile,
MoveFile,
RenameFile,
DeleteToTrash
};
```
```cpp
struct WorkspaceActionPlan
{
QString id;
WorkspaceActionType type = WorkspaceActionType::Unknown;
WorkspaceToolRisk risk = WorkspaceToolRisk::ReadOnly;
QString title;
QString description;
QString sourcePath;
QString targetPath;
QString relativeSourcePath;
QString relativeTargetPath;
QString content;
QString searchKeyword;
QStringList warnings;
bool requiresConfirmation = false;
bool allowOverwrite = false;
bool confirmed = false;
};
```
```cpp
struct WorkspaceToolResult
{
bool success = false;
QString message;
QString errorMessage;
QString outputText;
QString targetPath;
QVector<WorkspaceFileInfo> files;
};
```
## 6. 安全策略
`WorkspaceAccessPolicy` 是最重要的模块,所有工具必须经过它校验。
职责:
- 路径标准化。
- 判断路径是否在工作区内。
- 阻止 `../` 跳出工作区。
- 阻止符号链接跳出工作区。
- 阻止系统目录。
- 阻止危险文件类型。
- 阻止敏感文件默认读取。
- 限制写入行为。
- 限制删除行为。
建议接口:
```cpp
class WorkspaceAccessPolicy
{
public:
explicit WorkspaceAccessPolicy(const QString &workspaceRoot);
QString workspaceRoot() const;
QString normalizePath(const QString &path) const;
QString toAbsolutePath(const QString &relativeOrAbsolutePath) const;
QString toRelativePath(const QString &absolutePath) const;
bool isInsideWorkspace(const QString &path, QString *errorMessage = nullptr) const;
bool hasSymlinkSegment(const QString &path, QString *errorMessage = nullptr) const;
bool isSystemPath(const QString &path) const;
bool isDangerousSuffix(const QString &path) const;
bool isSensitiveFile(const QString &path) const;
bool isSupportedTextFile(const QString &path) const;
bool canReadFile(const QString &path, QString *errorMessage = nullptr) const;
bool canReadDirectory(const QString &path, QString *errorMessage = nullptr) const;
bool canCreateFile(const QString &path, QString *errorMessage = nullptr) const;
bool canCreateDirectory(const QString &path, QString *errorMessage = nullptr) const;
bool canWriteFile(const QString &path, QString *errorMessage = nullptr) const;
bool canMoveFile(const QString &sourcePath, const QString &targetPath, QString *errorMessage = nullptr) const;
bool canDeleteToTrash(const QString &path, QString *errorMessage = nullptr) const;
};
```
禁止危险后缀:
```text
.exe .bat .cmd .ps1 .vbs .msi .scr .reg .com .pif .lnk
```
敏感文件默认拒绝:
```text
.env
*.pem
*.key
id_rsa
id_ed25519
*.pfx
*.p12
credentials.json
secrets.json
token.json
```
文件名包含以下关键词也视为敏感:
```text
secret
token
password
credential
private
apikey
api_key
```
## 7. 扫描和索引
`WorkspaceScanner` 第一版使用 `QDir` + 手动目录栈扫描工作区,后续如需取消、进度回调或更细粒度过滤,再考虑替换为 `QDirIterator` 或异步扫描。
默认限制:
- 最大递归深度:8。
- 最大文件数量:5000。
- 跳过隐藏目录。
- 跳过常见产物目录。
跳过目录:
```text
.git
.vs
.idea
build
cmake-build-debug
cmake-build-release
node_modules
dist
release
release_packages
logs
```
`WorkspaceIndex` 第一版只做内存索引:
```cpp
class WorkspaceIndex
{
public:
void clear();
void setFiles(const QVector<WorkspaceFileInfo> &files);
QVector<WorkspaceFileInfo> allFiles() const;
QVector<WorkspaceFileInfo> findByName(const QString &keyword, int maxResults) const;
QVector<WorkspaceFileInfo> findBySuffix(const QStringList &suffixes, int maxResults) const;
QVector<WorkspaceFileInfo> recentFiles(int maxResults) const;
};
```
## 8. 工具清单
### 8.1 只读工具
`ListFilesTool`
- 列出工作区内目录。
- 最多显示 200 项。
- 不递归或浅递归。
`ReadFileTool`
- 只读文本文件。
- 最大读取 128KB。
- 超出截断。
- 敏感文件默认拒绝。
- 不读二进制文件。
支持后缀:
```text
.cpp .h .hpp .c .cc .py .js .ts .json .md .txt .ini .xml .yaml .yml .cmake .pro .css .html
```
`SearchFilesTool`
- 只搜索当前工作区索引。
- 最多返回 30 个结果。
- 按相关性和最近修改时间排序。
`SearchTextTool`
- 最多扫描 1000 个文本文件。
- 单文件最大读取 128KB。
- 最多返回 50 条匹配。
- 每条显示文件路径、行号、片段。
### 8.2 打开工具
`OpenFileTool`
- 用系统默认程序打开文件。
- 只能打开工作区内普通文件。
- 危险后缀拒绝。
- 敏感文件需要确认或第一版拒绝。
`OpenDirectoryTool`
- 打开工作区内目录。
- 工作区外路径拒绝。
`RevealInExplorerTool`
- 第一版可以先打开所在文件夹。
- 如果后续使用 `explorer.exe /select`,必须固定程序路径和参数,不接受聊天文本命令。
### 8.3 写入工具
`CreateFileTool`
- 只能在工作区内创建。
- 目标不能已存在。
- 文件名必须合法。
- 危险后缀拒绝。
- 必须确认。
`CreateDirectoryTool`
- 只能在工作区内创建。
- 目标不能已存在。
- 必须确认。
`WriteFileTool`
- 修改已有文本文件前自动备份。
- 禁止覆盖二进制文件。
- 禁止写敏感文件。
- 禁止写危险后缀。
- 必须确认。
备份目录:
```text
<workspaceRoot>/.desktoppet/backups/
```
备份命名:
```text
relative_path.replace("/", "__") + "." + yyyyMMdd-HHmmss + ".bak"
```
`CopyFileTool`
- 只复制单文件。
- 源和目标都必须在工作区内。
- 不覆盖。
- 必须确认。
`MoveFileTool`
- 只移动单文件。
- 源和目标都必须在工作区内。
- 不覆盖。
- 必须确认。
`RenameFileTool`
- 只允许同目录重命名。
- 文件名不能包含路径分隔符。
- 不覆盖。
- 必须确认。
`DeleteToTrashTool`
- 只移动到系统回收站。
- 不永久删除。
- 单文件删除第一阶段可支持。
- 文件夹删除需要强确认。
- 批量删除第一阶段拒绝。
## 9. WorkspaceAgent
第一版采用半自动 Agent,不做复杂工具循环。
推荐流程:
```text
用户输入
WorkspaceController 判断工作区任务
程序根据关键词和索引检索相关文件
读取少量相关文件
把文件树摘要、相关文件内容、用户问题交给模型
模型返回分析结果或修改建议
涉及写操作时生成 WorkspaceActionPlan
用户确认
执行工具
```
工作区专用系统提示词:
```text
你是 QtDesktopPet 的本地工作区助手。
你只能基于用户授权的工作区文件进行分析。
你不能假设自己看过未读取的文件。
你不能要求访问工作区外路径。
你不能执行系统命令。
你不能生成危险操作指令。
涉及创建、修改、移动、重命名、删除文件时,必须先生成清晰的操作计划,等待用户确认。
涉及敏感文件、密钥、凭证时,必须提醒风险。
回答代码问题时要引用具体文件路径。
当信息不足时,说明还需要读取哪些文件。
```
后续可升级为工具循环,但第一版不做。
## 10. WorkspaceController
建议接口:
```cpp
class WorkspaceController
{
public:
explicit WorkspaceController();
bool hasWorkspace() const;
QString workspaceRoot() const;
bool openWorkspace(const QString &rootPath, QString *errorMessage = nullptr);
void closeWorkspace();
WorkspaceToolResult scanWorkspace();
WorkspaceToolResult handleMessage(const QString &message);
bool hasPendingPlan() const;
WorkspaceActionPlan pendingPlan() const;
WorkspaceToolResult confirmPendingPlan();
void cancelPendingPlan();
private:
WorkspaceSession m_session;
WorkspaceAgent m_agent;
std::optional<WorkspaceActionPlan> m_pendingPlan;
};
```
原则:
- `handleMessage()` 不直接执行高风险写操作。
- 需要确认时保存到 `m_pendingPlan`
- UI 调用 `confirmPendingPlan()` 后才执行。
- 所有错误通过 `WorkspaceToolResult` 返回,不在工具层弹窗。
## 11. UI 接入
聊天入口支持:
```text
打开工作区
选择工作区
当前工作区
扫描工作区
分析这个项目
搜索当前工作区
读取 main.cpp
打开第一个
```
如果没有选择工作区,提示用户选择目录。
气泡只显示短状态:
```text
已打开工作区。
正在扫描文件。
找到了 12 个相关文件。
需要你确认修改计划。
操作已完成。
```
复杂结果不全部塞进气泡,当前通过独立 Agent 窗口展示。
当前独立 Agent 窗口可显示:
- 当前工作区路径。
- 文件树。
- 搜索结果。
- 已读取文件。
- AI 分析结果。
- 待确认操作计划。
- 执行结果。
- 最近操作历史。
## 12. 意图分发
后续建议新增:
```cpp
enum class UserIntentType
{
Chat,
Reminder,
Weather,
Workspace,
FileOperation,
LaunchApp
};
```
意图原则:
- `Reminder` 保持最高优先级。
- 明确天气问答走 `Weather`
- 明确工作区、代码、项目分析走 `Workspace`
- 当前阶段,未明确“工作区/项目/代码”且没有工作区文件名特征的普通文件读写请求仍可走旧 `FileOperation`
- 明确应用名启动走 `LaunchApp`
- 其余走 `Chat` 或输入框联网模式。
不能用宽关键词简单吞掉所有请求:
- “打开微信”应走 `LaunchApp`
- “打开文件夹”这种泛请求不走 `Workspace`;“打开 docs / 打开这个项目里的 docs”走 `Workspace`
- “搜索 Qt 最新版本”不应走工作区搜索。
- “搜索这个项目里的 main”应走工作区搜索。
## 13. 迁移步骤
1. 新增 `src/workspace/`,不改旧 `src/fileops/`
2. CMake 登记 workspace 新文件。
3. `PetWindow` 增加 `WorkspaceController` 成员。
4. 新增工作区打开、扫描、搜索、读取入口。
5. v1b 支持打开和定位;写操作计划和确认框从后续阶段开始。
6. 新模块覆盖旧文件操作能力后,`handleFileOperationChatMessage()` 转发到 `WorkspaceController`
7. 确认无旧引用后删除 `src/fileops/`
8. 更新 CMake、README、测试文档。
删除旧文件前必须执行:
```text
rg "FileOperationManager|FileSandbox|FileBackupManager|src/fileops|FileOperation"
```
确认没有有效代码引用后再删。
## 14. 日志要求
允许记录:
- 打开工作区路径。
- 扫描文件数量。
- 执行的工具名称。
- 操作是否成功。
- 错误摘要。
禁止记录:
- API Key。
- Authorization Header。
- 完整敏感文件内容。
- `.env` 内容。
- 密钥文件内容。
- 用户完整隐私文本。
使用项目已有 `Logger`,不要新增零散调试输出。
## 15. 测试清单
### 工作区选择
- 选择普通项目目录。
- 选择空目录。
- 选择不存在目录。
- 选择 `C:/Windows`,应拒绝。
- 选择 `Program Files`,应拒绝。
- 选择中文路径。
- 选择包含空格的路径。
### 扫描
- 扫描普通 Qt 项目。
- 扫描包含 build 目录的项目。
- 扫描包含 `.git` 的项目。
- 扫描大量文件目录。
- 扫描中文文件名。
- 扫描深层目录。
要求:
- 不崩溃。
- 不扫系统目录。
- 不进入跳过目录。
- 结果数量受限制。
### 搜索文件
- 搜索 `main`
- 搜索 `.cpp`
- 搜索 `README`
- 搜索不存在文件。
- 搜索中文文件名。
要求:
- 最多返回 30 条。
- 显示相对路径。
- 结果可用于后续“打开第几个”。
### 搜索文本
- 搜索 `class`
- 搜索 `include`
- 搜索中文关键词。
- 搜索不存在关键词。
- 搜索超大文件。
要求:
- 显示路径、行号、片段。
- 跳过二进制文件。
- 跳过超大文件。
- 不读取敏感文件。
### 读取文件
- 读取 `.cpp`
- 读取 `.md`
- 读取 `.json`
- 读取图片应拒绝。
- 读取 `.exe` 应拒绝。
- 读取 `.env` 应拒绝或强确认。
- 读取工作区外文件应拒绝。
### 创建文件
- 在工作区创建 `test.txt`
- 创建已存在文件应拒绝。
- 创建非法文件名应拒绝。
- 创建 `.bat` 应拒绝。
- 创建到工作区外应拒绝。
### 修改文件
- 修改普通 `.txt`
- 修改 `.cpp`
- 修改前生成备份。
- 修改二进制文件应拒绝。
- 修改敏感文件应拒绝。
要求:
- 必须确认。
- 必须备份。
- 失败不能破坏原文件。
### 移动和重命名
- 移动普通文件。
- 移动到已存在目标应拒绝。
- 重命名普通文件。
- 重命名为非法文件名应拒绝。
- 移动到工作区外应拒绝。
### 删除到回收站
- 删除普通文件到回收站。
- 删除文件夹到回收站。
- 删除工作区外文件应拒绝。
- 永久删除请求应拒绝。
- 批量删除请求第一阶段应拒绝。
### 对话上下文
```text
用户:搜索 README
用户:打开第一个
用户:搜索 fileops
用户:读取第二个
用户:找一下包含 FileOperationManager 的文件
用户:总结这些文件的作用
```
要求:
- 系统能记住上一轮搜索结果。
- “第一个”“第二个”能正确解析。
- 上下文过期后要求用户重新选择。
## 16. 静态推演要求
每完成一个阶段,需要记录:
- 入口路径。
- 业务模块调用链。
- 路径校验点。
- 用户确认点。
- 失败回滚策略。
- 不支持能力的拒绝话术。
- 对提醒、天气、联网模式、应用启动的回归影响。
## 17. 最终验收标准
本轮重构完成后至少达到:
1. 用户可以选择一个本地项目目录作为工作区。
2. 程序可以扫描工作区并建立文件索引。
3. 用户可以通过对话搜索文件。
4. 用户可以通过对话读取和分析文本文件。
5. 用户可以要求分析项目结构。
6. 用户可以创建文本文件和文件夹。
7. 用户可以移动、重命名、删除到回收站。
8. 所有写操作必须展示计划并等待确认。
9. 所有路径必须限制在工作区内。
10. 不允许永久删除。
11. 不允许执行脚本或命令。
12. 不允许默认读取敏感文件。
13. 旧文件操作功能被新 Workspace 系统替代。
14. 项目可以由用户手动构建通过。
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -58,6 +58,7 @@ reports/perf/
| 联网模式 | 使用支持联网的 OpenAI/Gemini Provider 连续发起 5 次联网对话,并覆盖 DeepSeek/Custom 不支持提示 | `tools/perf_sample.ps1 -DurationSeconds 300` | 请求异步执行,来源展示不阻塞 UI;不支持 Provider 不发起伪联网;日志不记录完整用户问题或 API Key | TODO | TODO | 输入框打开联网开关;不再测试旧多搜索源/SearXNG 路径 |
| 应用启动 | 登记一个测试 exe,聊天触发启动并取消/确认各一次 | 手工观察 + 采样 | 启动前始终二次确认;取消时不启动;确认后 UI 可继续响应;不执行脚本或聊天参数 | TODO | TODO | 使用无副作用测试程序,例如记事本或临时测试 exe |
| 本地文件操作 | 读取文本、列目录、复制、备份、重命名 | 手工观察 + 采样 | 写操作有确认;不覆盖、不删除、不访问系统目录;操作后 UI 可继续响应 | TODO | TODO | 使用临时测试目录,不操作真实重要文件 |
| 本地工作区 Agent v1b.5 | 选择临时项目工作区,扫描、搜索文件、搜索文本、读取文件、总结文件、打开普通文件、显示所在位置,并使用独立 Agent 窗口、右键/托盘入口和 `Alt+A` 快捷键操作选中结果 | 手工观察 + `tools/perf_sample.ps1 -DurationSeconds 600` | 扫描和搜索结果数量受限制;独立窗口刷新不阻塞 UI;快捷键注册失败有提示且菜单入口可用;打开文件前二次确认;系统目录、符号链接、危险后缀、敏感文件和命令执行均拒绝;日志不记录敏感文件内容;内存无持续异常增长 | TODO | TODO | 不做创建、修改、移动、删除;使用临时测试仓库,不操作真实重要项目 |
| 配置损坏兜底 | 备份后分别破坏 app 配置、AI 配置或本地聊天记录再启动 | 启动后采样 3 分钟 | 程序恢复默认配置或忽略损坏历史,并生成带时间戳的 broken 备份,不覆盖旧备份 | TODO | TODO | 使用备份副本测试 |
| 角色包损坏兜底 | 使用临时复制的损坏角色包测试 | 启动后采样 3 分钟 | 程序不崩溃,回退 preview 或默认显示 | TODO | TODO | 不直接破坏仓库内默认角色包 |