feat: converge dynamic plugins onto canonical homeagent-sdk

- Separate built-in plugin interface from external plugin interface
- Route dynamic plugin loading through homeagent-sdk/sdk using reflection
- Turn internal/sdk into an enhanced wrapper over canonical SDK types
- Vendor SDK repo snapshot under third_party/homeagent-sdk for stable builds
- Keep internal constructors/adapters for memory, knowledge, llm, settings
- Align dynamic QQ loading with canonical SDK chain
This commit is contained in:
root
2026-07-06 19:27:51 +08:00
parent 753167bee4
commit 8103481254
37 changed files with 4505 additions and 420 deletions

View File

@ -0,0 +1,84 @@
# files 插件讲解
文件系统操作插件,提供文件的读写编辑和目录浏览能力。
## 工具清单
| 工具 | 功能 | 源码 |
|------|------|------|
| `files_read` | 读取文件内容,支持 offset/limit 分段 | `handleRead` |
| `files_write` | 写入文件,支持 4 种模式 | `handleWrite` |
| `files_edit` | 精确字符串替换编辑 | `handleEdit` |
| `files_ls` | 列出目录内容 | `handleLs` |
## 核心设计
### 沙箱路径隔离
`resolvePath()` 方法将用户传入的路径解析为沙箱内的绝对路径。关键逻辑:
```go
// 相对路径以沙箱根目录为基准拼接
if !filepath.IsAbs(userPath) {
userPath = filepath.Join(p.filesDir, userPath)
}
// 检查是否越界
base := filepath.Clean(p.filesDir)
if base != "/" && !strings.HasPrefix(abs, base+string(filepath.Separator)) && abs != base {
return "", fmt.Errorf("path outside sandbox")
}
```
当沙箱根设为 `/` 时放行所有路径;设为特定目录时拒绝访问外部。配置项 `plugin.files.dir` 控制此值。
### 分段读取
`files_read` 支持 `offset`(行号,1-indexed)和 `limit`(行数上限),用于大文件分段查看:
```go
// plugin.go:handleRead
lines := strings.Split(text, "\n")
offset := 0 // 从 args["offset"] 解析,1-indexed 转 0-indexed
limit := totalLines - offset
// ...
end := offset + limit
selected := lines[offset:end]
```
如果未读完会在末尾追加提示 `[Showing lines X-Y of Z. Use offset=N to continue.]`。
### 四种写入模式
`files_write` 通过 `mode` 参数区分:
- **overwrite**(默认):`os.WriteFile` 覆盖写入,自动创建父目录
- **append**:`os.OpenFile` 以 `O_APPEND|O_CREATE|O_WRONLY` 打开,追加内容
- **insert**:将文件按行分割,在指定行号前插入新内容,再写回
- **create**:先检查文件是否已存在,存在则报错,不存在才创建
### 精确编辑
`files_edit` 接收 `edits` 数组,每个元素有 `old` 和 `new`。要求每个 `old` 在原文中**恰好出现一次**,防止 LLM 误替换:
```go
count := strings.Count(content, oldText)
if count == 0 { /* 报错未找到 */ }
if count > 1 { /* 报错存在多处匹配 */ }
content = strings.Replace(content, oldText, newText, 1)
```
### 目录列表
`files_ls` 按字母序排序,目录加 `/` 后缀,同时显示文件大小。默认上限 500 条。
## 配置项
| Key | 默认值 | 说明 |
|-----|--------|------|
| `plugin.files.dir` | `/` | 文件操作沙箱根目录 |
## 注意事项
- 所有路径操作前都经过 `resolvePath` 沙箱检查
- 错误结果统一用 `errorResult()` 返回 `{isError: true, content: msg}` 格式,LLM 可据此判断
- `files_write` 的 insert/append 模式不检查文件是否存在(不存在则报错),overwrite/create 模式自动创建父目录