163c5f70b3
chore(sdk-mirror): 同步 vendored SDK 到 SDK main(browser_search 现代 Bing 版式解析修复)
...
镜像追平 SDK main(`ebd700e`):
- browser 示例:browser_search 三处根因修复(www.bing.com 302 → cn.bing.com;
标题取 h2 > a 而非块内第一个 <a>;摘要兼容 p.b_lineclamp*)并解开 /ck/a 跳转包装,
解析不出结果时显式报错;附真实响应夹具与 5 项单测;版本 2.4.0 → 2.4.1。
注:SDK 仓新增的 example/deepsearch(联网检索 + SearXNG 生命周期托管)与
example/vikunja(任务管理)按本仓策略(.gitignore 忽略 example/ 下未跟踪文件)不入镜像。
2026-09-12 23:39:10 +08:00
d1662e77cf
chore(sdk-mirror): 同步 vendored SDK 到 SDK main(qq 插件重写、browser 会话超时必填、路牌 1.3.0)
...
核心仓 `third_party/homeagent-sdk` 是 SDK 仓的 vendored 副本,内容以 SDK 仓 main 为准
(本仓 `.gitignore` 忽略 example/ 下的未跟踪文件,已跟踪的镜像文件用 `git add -u` 更新)。
本次把镜像追平到 SDK main(`8c10b7e`):
- qq 插件重写(+498 行):`output_send` 回声/自激路径的根因修复与去重;
- browser 示例:交互式会话 `timeout` 由默认 10m 改为**必填**(附参数校验与测试);
- `meta.Version` 1.2.0 → **1.3.0**(SDK v1.2.0 已定版,SDK main 按纪律推进到下一个中版本)。
2026-09-12 20:21:01 +08:00
5e10f012cb
docs(license): 核心仓采用 AGPL-3.0-only,并写进包内与安装器
...
本仓此前**没有任何许可文件**,README 里也没有许可声明,而 rpm 元数据里甚至写着
`--license "Proprietary"`(与我们实际的分发意图相反)。
## 选择了什么
`LICENSE`:GNU Affero 通用公共许可证第 3 版官方全文(gnu.org 正本,
661 行 / 34523 字节,
sha256 0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0)。
选 AGPL-3.0-only 的理由:GPL 家族里**传染性最强**的一档,并且不允许选后续版本。
它比 GPL-3.0 多出 §13(Remote Network Interaction)——通过网络提供服务时也要向
使用者提供源码。这正是「最严格」在 GPL 家族里的落点。
依赖许可已核对为全部宽松且兼容:go-sqlite3 / gojieba / gopher-lua / bubbletea /
bubbles / lipgloss / yaml.v3(MIT)、golang.org/x/{sys,text}(BSD-3)、
Chinese-CLIP 产物(Apache-2.0,与 GPLv3+/AGPLv3 双向兼容)、ONNX Runtime(MIT)。
没有 GPL-2.0-only 这类与 AGPL 不兼容的依赖。
## 落在哪些地方
- `README.md` / `README_EN.md`:新增「许可 / License」章节,写明 §13 的含义、
插件因**静态链接 SDK 源码**而成为衍生作品须同许可发布、以及随包第三方组件清单
- `deploy/packaging/package-linux.sh`:
· 新增 `stage_license()`,**四个变体(full/server/client/tar)全带**
`/usr/share/doc/homeagent/{LICENSE,copyright}`(copyright 为 DEP-5 机器可读格式,
含第三方条目)
· fpm 的 `--license "Proprietary"` → `"AGPL-3.0-only"`
- `deploy/packaging/installer.nsi`:新增 MUI 许可页
(`..\..\LICENSE`,NSIS 以 .nsi 所在目录解析相对路径)
- `third_party/homeagent-sdk/LICENSE`:vendored SDK 的许可一并入库 —— 本仓
`.gitignore` 有意不镜像 SDK 的 README/tools/package/example,但依赖的许可
应当随依赖可见
## 待办(下一步)
README 的「项目状态」仍停在 v1.1.1,且写着已被 v1.2.0 **删除**的机制
(引用计数式 GC、`[<mime> <digest>] <描述>` 描述式索引)——单独一个提交修。
2026-09-12 09:17:03 +08:00
fbd3adabea
fix(sdk-mirror): vendored SDK 路牌回到 1.2.0(SDK 不跟 beta 发版)
...
SDK 仓 44bd915 把 main 的 meta.Version 推到 1.3.0 是错的(12cabcb 已改回 1.2.0)。
按 §七.2,beta 不伴随 SDK 发版:SDK 1.2.0 要等核心的**正式** tag 才定版打 tag
(§七.3),在那之前 1.2.0 仍是 SDK 尚未发布的中版本,路牌不得越过它。
核心 main 的 1.3.0 不受影响(1.2.0 已归 release/v1.2.x 所有),两仓在此阶段
故意不对称——这一点已写进 SDK meta.go 的注释,避免再被「对齐」回去。
本提交只同步 core 里跟踪的镜像:sdk/ 目录与 SDK 仓仍然逐字节一致。
2026-09-12 09:00:41 +08:00
1fb5700662
chore(meta): main 的版本路牌推到 1.3.0
...
按 docs/git-branching.md §2.1,main 的 meta.Version 始终是**下一个未发布中版本**。
1.2.x 线已开(release/v1.2.x 承载 1.2.0),所以 main 指向 1.3.0。
它标记「main 正在积攒 1.3 的东西」,不表示 1.3.0 已经存在——1.3.0 没有任何 tag。
1.2.0 已由 release/v1.2.x 承载;main 不能再挂着 1.2.0,否则 main 的版本号
就与某一次发布的版本号相同,违反 §2.1「main 的版本号不是任何一次发布的版本号」。
两仓同步:SDK 仓 main 也在同一时刻推到 1.3.0(commit 44bd915)——SDK 版本跟随
核心中版本(§七.1),且两仓 main 都表示下一个未发布中版本(§七.4)。
本 commit 同时把 vendored SDK 镜像(third_party/homeagent-sdk/meta/meta.go)
更新到与 SDK 仓一致。
SDKCompatibleVersion 保持 1.2.0:那是本内核**实际实现并兼容的最高 SDK 接口版本**,
与「下一个未发布中版本」是两件事(参见 e4be966 时 main 的 1.2.0 / SDK 兼容 1.1.0)。
**此 commit 不 cherry-pick 到发布分支**(§五:版本号 bump 不跨分支搬)。
2026-09-12 08:25:30 +08:00
4d2028fbef
chore(sdk): 同步 vendored SDK 镜像到 1.2.0
...
core 仓里跟踪的 SDK 镜像落后于 third_party/homeagent-sdk(那是个独立仓库):
已提交的 sdk/memory.go 仍带 MediaAttachment.Description,而 SDK 仓 b2eafdf 已把它
删掉(媒体不再以文本描述参与索引)。磁盘上的文件其实就是 SDK 仓当前的版本,
只是 core 的索引没有跟上——于是**干净检出 main 拿到的是一个与它构建所用 SDK
不一致的镜像**。
clean worktree 能编译(内核已不再引用 Description),所以这个不一致不会被构建
发现,只能靠比对发现——属于本项目一直在治的「静默不一致」。
同步内容:sdk/memory.go 与 SDK 仓 HEAD 逐字节一致。
2026-09-12 08:21:21 +08:00
d1959cbe80
feat(core): 注入行为的记忆/裁剪标志位落地 + jieba 词库内嵌 + Windows 改走 WSL
...
配套 SDK 提交:homeagent-sdk ba49dfd(公开 API 纯追加,无签名变更)。
本仓第三方的库镜像同步至该版本,以保证全新 clone 能编译。
## 1. 注入标志位(内核侧)
- 7 条注入路径(排队/中断/同步 × 纯文本/带媒体 + 旧 NoMem 变体)解析并转发
no_memory / context_policy / cleaner_name;策略在入口**校验**,
非法值报错而不是静默降级成 none(降级会让调用方以为自己声明的裁剪在生效)。
- 新增 validateContextPolicy(与 tool.register 同一套规则)与 pubSdkInjectOpts。
- input.register 不再手写字段白名单重建 ChannelDef,改为整体传递 + 补 ContextPolicy。
- io 层:applyInjectOpts 把标志位写进事件 payload,仅非零时写
(零值与旧 payload 逐字节一致,事件订阅方与旧内核都不受影响)。
- ioAdapter / procCore / internal-sdk 别名补齐六个 *Opts 实现。
## 2. 修掉「输入无条件裁剪」这个真缺陷
eventloop 此前对**每条非中断输入**都调 `context.Prune(...)`:破坏性(低相关事件被
归档移出上下文)且无法从调用点看出是谁触发的。改为 pruneOnInput/pruneDeclared:
优先级:注入点声明(payload.context_policy)> 通道声明(ChannelDef.ContextPolicy)
> 默认**不裁剪**
查询向量仍取清洗后的内容;新增 cleanInputFor 解析清洗文本,优先级为
注入点声明的 cleaner(cleaner_name)> 按 source 查到的通道 cleaner > 原文,
名字查不到时**记日志再回退**(注入是 fire-and-forget,插件看不到错误,
至少要在内核日志留下「你声明的清洗没生效」的痕迹)。
## 3. jieba 词库内嵌(修「猜 GOMODCACHE → 静默失效」)
原 jiebaDictDir() 去猜 GOMODCACHE/GOPATH/~/go/pkg/mod,部署机上通常没有 Go 模块
缓存 → GetJieba() 返回 nil → 分词/关键词提取/NLP 依存解析(进而 doc→graph 三元组
抽取)/静态词向量 tokenizer **一律静默返回空列表**,只有一行日志。本机看起来正常
只因开发机与生产机重合、恰好有那份缓存。
现在词库随二进制分发:internal/memory/jiebadict/ 5 文件约 11.6MB + go:embed,
按**内容哈希**命名缓存目录落盘(词库升级不复用旧文件),已齐全则跳过写入。
模块缓存降为兜底。homed 体积 32MB。
顺带确认(并有测试佐证):gojieba 的 Tag() 不需要 pos_dict/ 目录——
cppjieba 的 PosTagger 从主词典每行的词性列取 tag。
## 4. homed 放弃 Windows 原生,改走 WSL2
插件体系依赖「继承的 fd」+「统一共享内存区的段内偏移解引用」,Windows 既无 fd
继承语义,其句柄模型也无法表达后者;强行适配等于再维护一套平台专属 ABI
(C ABI 时代三套 ABI 并存曾导致改写型插件在某平台静默失效)。
- cmd/homed/platform_{windows,other}.go:原生 Windows 启动即拒绝并打印 WSL2 指引。
- internal/plugin/proc/shmalloc_windows.go:allocShm 直接返回「请用 WSL2」,
**不返回半可用的段**(与 shmalloc_other.go 同风格:未支持平台显式报错);
procEnvForShm 返回 nil。顺手修掉两处长期编译错误
(cryptorand→rand、h.evData→h.unified.evtData),使 GOOS=windows 至少能编译。
注:homed 本就编不出 Windows——internal/memory 依赖 cgo-only 的 gojieba。
- deploy/packaging/installer.nsi:不再安装 homed.exe/initconfig.exe,改为携带
**linux payload** 并调用新的 install-via-wsl.ps1;退出码 20/21 表示
「需先装 WSL/发行版」,走指引而非报错。
- deploy/packaging/windows/install-via-wsl.ps1(新):检测 WSL → 引导安装 →
确保 WSL2 → 送包进发行版 → 在 WSL 内按 Linux 方式安装。**复用 Linux 包与
linux/setup.sh**,不另写一套安装逻辑;落点与 deb 布局统一
(/usr/bin/homed + /usr/lib/homeagent/setup.sh)。
- deploy/packaging/linux/setup.sh:API Key 允许 HOMEAGENT_API_KEY 覆盖
(否则安装器界面显示一份、config.db 里另一份 → 登录不上)。
- deploy/packaging/build.sh:windows 目标只构建 waiter + gui,并新增
stage_linux_payload 把 Linux 包暂存给安装器;homed/initconfig 在 windows
目标下明确拒绝。
## 5. 插件调用点统一写明意图
- webui 的 OpenAI 兼容端点(固定提示词模板)→ InjectTextSyncNoMemory。
- agentcli 的 5 处纯状态通知(已启动/超时/执行结束/进程退出/读取结束)→ NoMemory;
**带输出**的 2 处(定时反馈、有新输出)刻意保留记忆并注明理由。
- timer 的定时提醒 → NoMemory(中断本来也隐含 NoMemory,这里是写明意图)。
## 6. 版本
meta.Version 仍为 1.2.0(main 是下一个未发布中版本);
SDKCompatibleVersion 1.1.0 → **1.2.0**(本内核已实现 SDK 1.2.0 全部新增方法)。
## 测试
- core:默认不裁剪(无声明/none/空)、通道 opt-in、注入点双向覆盖通道、
nil context/io 安全、cleaner 优先级与未知名回退。
- io:零值 opts 与历史 payload 逐键相同;text/中断/媒体三类注入标志位都落到
payload;旧方法仍生效。
- proc:validateContextPolicy 只接受 ""/none/prune,报错含位置与实际值;
**跨进程** e2e——testdata 插件经 io.injectText 送出三个标志位,断言它们穿过 RPC
到达内核。
- memory:模块缓存不可见时内嵌词库仍可用(分词与 POS 内容词均非空)、
落盘幂等、内容哈希稳定。
验证:go build ./... / go vet ./... / go vet -tags onnxruntime ./...
go test -short ./internal/memory/... ./internal/nlp/... ./internal/plugin/...
./internal/agent/{core,io}/... ./pkg/...
2026-09-11 20:31:50 +08:00
772a494223
feat(ctx): ContextPolicy tool 上下文策略(§13.8)
...
- SDK ToolDef 加 ContextPolicy string(默认 none / 可设 prune)
- StageAfterToolcall 后检查工具 ContextPolicy=prune → RelevanceContext.Prune
- 所有现有测试通过
2026-09-10 15:41:23 +08:00
8cd884027f
docs: 项目状态与 plan 同步到 v1.1.1,vendored SDK meta 注释对齐 SDK 仓
...
三处失同步,都会让读者拿到错的现状:
## README / README_EN
「项目状态」段的最新条目还停在 v1.0.0,而 1.1.0 与 1.1.1 都已发布。补上两条,
并把顶部特性摘要与下载段的 Windows 安装器版本号一起更新(那里写死了
`HomeAgent_v1.0.0_*_win64.exe`,照着它去 release 页面找是找不到文件的)。
## plan.md §12.1
标题还是「待用户决策后执行」,而四个决策点早已全部落定并执行完毕。改为已完成,
并记下实际演进已超出当初设想的地方(这些后来都写进了 docs/git-branching.md):
发布分支改为一个中版本一条、三级通道由 tag 区分、SDK 版本跟随核心中版本且
patch 位恒为 .0、beta 阶段不发 SDK、main 永不作发版分支。
同时修掉一处会误导追溯的陈述:§12.1 原文说 `v1.0.0` tag 指向 feature 分支中间点
`670efcd` 需要重打——那件事早已做完,现在指向 `release/v1.0.x` 上的 `9b92a04`。
## plan.md §12.5
「无 Windows 真机验证」这条仍然成立,但补一句区分:Windows NSIS 安装器从 v1.0.0
起就随每个正式版作为 release 资产发布了。**能打出包 ≠ 包里的共享内存/事件对象在
真机上能跑通**,混为一谈会让人以为这项已经关闭。
## vendored SDK meta
主仓跟踪 `third_party/homeagent-sdk/meta/meta.go`,而该文件在 SDK 仓 main 上刚补了
版本路牌的说明注释。两仓这份文件必须逐字一致——否则下次谁改了哪边说不清,
而它正是「两仓中版本对齐」这条纪律的载体。仅注释差异,无行为变化。
2026-09-06 10:56:42 +08:00
e4be9667ec
chore(meta): main 的版本路牌推到 1.2.0
...
按 docs/git-branching.md §2.1,main 的 meta.Version 始终是**下一个未发布中版本**。
1.1.x 线正在发布中(release/v1.1.x 承载 v1.1.0 / v1.1.1),所以 main 指向 1.2.0。
它标记「main 正在积攒 1.2 的东西」,不表示 1.2.0 已经存在——1.2.0 没有任何 tag。
已发布的版本号一律看对应的 release/vX.Y.x 分支与 tag。
两仓同步:SDK 仓 main 也是 1.2.0(SDK 版本跟随核心中版本,见 §七.1),
而 release/v1.1.x 上 SDK 定版 1.1.0。
**此 commit 不 cherry-pick 到发布分支**(§五:版本号 bump 不跨分支搬)。
2026-09-06 09:54:55 +08:00
687e5655bc
feat(sdk): 多模态贯通插件边界——公开接口、内核桥接与统一输入主干
...
记忆系统在 1.1.0 支持了二进制多媒体节点,但那条链路只对**内核自己**开放:
用户在 qq 发图能落进 CAS、能被记忆引用,而插件调 Commit / DocMemory().Insert
交进来的媒体一律无处安放。原因是三层都断着,且**每一层都不报错**。
## 一、公开 SDK:补上媒体的表达能力(全部新增,无签名变更)
- `Triple` += `SentenceText`、`MediaDigests`
- `Doc` += `MediaDigests`、`Attachments`;新增 `MediaAttachment`
- `TextEvent` += `Attachments`
- `DocMemoryAPI` += `InsertWithMedia`
- `IOInjector` += `InjectInputMedia` / `InjectInputMediaSync` / `InjectInterruptMedia`
- `PluginSDK` 补上一直缺失的 `SetToolBlocks` 包装(接口里有、便捷方法里没有)
`MediaAttachment` 一个类型服务两个方向:给 `Data`+`MIME` 是新内容(CAS 按字节
去重),只给 `Digest` 是引用已有内容。读路径**只回元数据不回字节**——一次检索
可能命中几十份媒体,全塞回去会把跨进程消息撑爆。
媒体注入不能搭 `SetToolBlocks` 的车:那个方法只在工具处理函数内部可用,且媒体
要等下一条 tool message 才到模型手上。插件主动发起一轮带媒体的对话、以及中断
注入,需要自己的签名,且媒体在**本轮**就送到模型。
## 二、内核桥接层:原先在静默裁字段
`internal/sdk/memory_impl.go` 此前只搬自己认识的几个字段,其余丢弃且返回 nil:
- 图记忆丢 `Confidence`/`SubjectType`/`ObjectType`/`SentenceText`,又走 `Commit`
而非 `CommitWithMedia`(不回 sentenceIDs)→ 媒体绑定链 `SentenceText → sentences
→ sentence_id → media_refs` 一步都走不通,插件即便按格式写好标记也永远挂不上;
- 知识库 `Query` 只回 ID/Title/Content,`Insert` 只写这三个;`Remove` 不解引用,
于是那些媒体永久处于「被引用」状态,GC 收不掉、磁盘只增不减
(内核的归档路径 `releaseDocMedia` 做了这一步,插件路径漏了同一步)。
规则改为:**内部结构有的字段一律透传**。标记格式处理作为包级私有辅助留在桥接
层自己手里,但必须与内核 `mediaSummaryForEvent` 字节兼容——两边要能互读对方
写下的标记。
标记插入必须在 `ds.Insert` **之前**(向量索引取 `Summary + " " + Content`,
之后补的标记检索不到),引用绑定必须在**之后**(owner_id 是 Insert 生成的 ID)。
## 三、跨进程链路:不接线就是全体外部插件编译失败
`go test` 直接把这一层拍出来了——`procIO does not implement sdk.IOInjector`。
公开接口加方法后,生成模板不跟上,**每个外部插件都编不过**,是硬失败不是软降级。
六处接线:`protocol.go` 四个 method 常量、`capability.go` 能力归属、
`corehandler.go` 四个分派分支、`proc_core.go` 委托、`proc_main.go.tmpl` 模板侧
实现、以及三个测试替身。
## 四、统一输入主干:把模态从「函数选择」降级为「字段」
`processTextInput` / `processMediaInput` 合并为 `processInput`。这个分叉是历史
产物而非设计:`processTextInput` 本来就处理媒体(`bindEventMedia` +
`mediaSummaryForEvent`,与媒体路径尾部完全相同),`process()` 只看
`stageCtx.Extra["media_blocks"]`、根本不认识 `evt.Type`。模态是输入的**属性**,
不是输入的**种类**。
媒体路径由此获得它一直缺的六项:去重、`no_memory`、通道 `Cleaner`、中断语义、
`_consolidation_` 路由、正确的 `EventRawInput`。
最后一项是个真 bug:媒体路径发布 `"content": evt.Payload`(一个 map),而
`webui/handler.go` 断言 `.(string)` → 断言失败、`content == ""`、提前返回。
**用户发的图从来没出现在 WebUI 聊天记录里。**
`media_blocks` 同时接受 `[]agentAPI.ContentBlock` 与 `[]pubsdk.ContentBlock`:
字段一致但 Go 不自动转换,只认一种的后果是另一种被静默丢弃。
## 五、模型可调用的三个工具
`memory_commit` 的 `sentence_text` **从未暴露给模型**,而它是绑定链上的必经环节;
连同 `media_digests` 一起补进 JSON schema 与工具文档。`doc_commit` 加
`media_digests`。`doc_query` 把关联媒体单独一行附在结果末尾(正文按 2000 字截断,
标记通常就在尾部)。
标记由**内核**生成而非插件/模型拼装:要求调用方知道格式,等于让一个拼写错误
静默切断引用绑定,而全链路无人报错。
## 六、WebUI 上传走真实媒体链路
图片/音频读回字节拼 data URL 注入 `media_blocks`(8MB 上限,超限退回按路径处理)。
此前只注入一句「文件已保存到 <路径>」,指望模型自己调 `files_read`——但那返回
文本,图片字节对模型永远不可见。附件类型识别扩展到 audio 并在缺 Content-Type
时按扩展名兜底(判错不只是卡片样式问题,图片被当普通文件就进不了视觉链路)。
## 测试
- `internal/sdk/memory_impl_test.go`(12 例,此前该包**没有任何测试文件**)
- `internal/agent/core/inputunify_test.go`(统一主干 + 双静态类型 + 三工具媒体)
- `third_party/homeagent-sdk/sdk/stress_test.go`(13 例并发压测)
压测抓到两处**真**竞态(不是理论风险):`PluginSDK` 的 API 字段与 `autoRestart`
无锁,而写方(内核注入 API、插件 `SetAutoRestart`)与读方(插件后台 goroutine
注入、内核 registry 读 `AutoRestart`)天然跨 goroutine。加 `apiMu` 修掉;约定
只在持锁期间取字段值,取完即释放再调用——持锁调用会把 `InjectInputSync` 这类
阻塞到 agent 回复(可达数分钟)的方法与 `SetIOInjector` 串起来,让插件重载卡死。
测试还抓出两个自身缺陷:`bindDocMedia` 把同一份媒体数两次(`AddRef` 幂等所以表
是对的,但日志说「绑定 2 个」而实际 1 条——误导后续排查),以及用单字符实体名
时 `validEntityName` 静默跳过、`Commit` 返回 nil 却什么都没写。
存量插件不需要改一行也不需要重编:新增方法由插件调用、内核实现,不调就不受影响。
17 个 example 插件源码零改动通过类型检查。
2026-09-06 09:51:31 +08:00
670efcd426
sdk: 同步 SDK 仓 meta 到 1.0.0(vendored 侧)
...
SDK 仓的 5ed8d65 在主仓这边的对应提交。主仓经 replace 引用
third_party/homeagent-sdk,其 sdk/ 与 meta/ 由主仓一并跟踪
(只有 example/ 与 tools/ 被 .gitignore 忽略)。
2026-09-02 23:02:26 +08:00
b20121f703
plugin: 删除 C ABI 通道(Part 6.2 完成,-3198 行)
...
外部插件统一走子进程 + stdio RPC,三套独立 ABI 实现收敛为单一 RPC 实现。
用户决策:彻底舍弃 .so 能力,不保留双通道回退。
## 删除清单
internal/plugin/cabi/ 1156 行(loader.go/loader.c/types.go/output_test.go)
internal/plugin/dynamic_dll_windows.go 272 行(§9.2 记录的能力退化实现)
internal/plugin/dynamic_loader_unix.go 79 行(唯一 cabi 引用点)
internal/plugin/dynamic_dll_test.go 32 行
internal/plugin/dynamic_dll_stub.go 11 行
internal/plugin/dynamic_loader_windows.go 11 行
internal/plugin/bridge_e2e_test.go (测的是 cabi 路径)
third_party/.../plugindev/templates.go 1296 行(取消跟踪,SDK 仓才是权威副本)
dynamic.go:entryCABI 通道删除,soEntry/dllEntry 常量删除。
registry.go:tryDynamic 探测顺序从 .so → .dll → .lua 变成 proc → lua。
## 旧 .so 给明确错误,不静默跳过
静默跳过会让「插件目录在但没加载」看起来像配置问题,而实际原因是需要
用新版 plugindev 重编。故保留 legacyCABIEntries 表专门用于识别残留:
plugin legacy: 检测到旧 C ABI 产物(plugin.so/.dll/.dylib)。
外部插件已改为子进程模式,请用新版 plugindev 重编产出 plugin.bin
(业务代码无需修改)
错误消息里「业务代码无需修改」这句是有测试守着的——迁移的核心承诺就是它。
## pluginmgr 安装逻辑跟进 bundle 命名
子进程模式下各平台产物统一叫 plugin.bin(进程边界即 ABI 边界),故 zip 内
按平台加后缀 plugin.bin.<goos>.<goarch>,解包时挑当前平台那一份重命名。
platformBinary 改为按 runtime.GOOS+GOARCH 生成条目名;platformBinaries 固定表
换成 isPlatformBinary 前缀判断(平台组合会增长:linux/arm64、darwin/arm64…,
按前缀判断无需维护清单)。
新增 chmod 0755:zip 保留了原权限位,但经某些工具链/传输后可能丢失,
内核加载时会因缺执行位报错。提前补上比事后让用户 chmod 更好。
## 测试
entry_dispatch_test.go 重写(12 项):
- classifyEntry 对 .so/.dll/.dylib 现在返回 unknown
- LegacyManifestFallsBackToProbe:存量插件 manifest 仍写 "plugin.so"
(17 个插件没人去改),须靠目录探测找到 plugin.bin —— 这是
「外部插件零改动」的直接后果
- LegacyCABIGivesActionableError:错误消息须含 plugindev / plugin.bin / 业务代码
- PluginEntryHash_IgnoresLegacyCABI:.so 不参与 hash(内核已不认它)
upgrade_test.go 的 .hmap 构造改用 plugin.bin。
验证:go build ./... 通过;go test ./... 全仓无失败;
go test -race ./internal/plugin/... 全绿;三平台构建通过。
Ref: docs/zh/架构迁移评估.md §3.1/§9.2、docs/zh/plugin-migration-plan.md Part 6
2026-09-02 19:26:40 +08:00
2e2602f437
docs(plan): Part 0.1/0.2 勾选 + 迁移计划进度标记
...
- plan.md 11.1 (3 checkbox)、11.3 (3 checkbox) 全部勾选
- plugin-migration-plan.md: 0.1/0.2 标记 ✅ 完成(含踩坑记录与待部署项)
2026-08-31 12:33:24 +08:00
eba300aec2
chore: 同步 vendored SDK(qq v1.2.0 + plugindev bridge 修复)
...
third_party/homeagent-sdk 同步至 SDK 仓 61f307b:
- fix(plugindev): bridge 模板补 dispatchIO.SetToolBlocks
SDK v0.9.2 给 IOInjector 加了该方法但 C ABI bridge 模板未同步,
导致任何外部插件编译失败(missing method SetToolBlocks)
- feat(qq) v1.2.0: msg_id→get_history 7 天兜底(不缓存正文)
+ qq_list_chats / qq_mark_read 会话列表(按最新消息排序 + 未读数)
+ 中断模板补 fallback 路径与私聊 user_id
.gitignore 补 HarmonyOS 构建工具链缓存(.hvigor-home/ .npm-cache/ .ohpm/),
这些由 HVIGOR_USER_HOME/ohpm 生成,非源码。
go build ./cmd/homed/ 通过。
2026-08-30 17:14:07 +08:00
14f3605f6e
chore: sdk v0.9.2 vendored副本同步 + go.mod 依赖更新
...
- third_party/homeagent-sdk/meta/meta.go: 0.9.1 → 0.9.2
- go.mod: require v0.9.2(replace 保留,指向本地 vendored 目录确保可编译性)
- CoreVersion 同步更新
2026-08-27 09:36:49 +08:00
c44ec0f210
feat(waiter): daemon模式 + localuse本机外设插件
...
waiter 新增 --daemon 后台驻留模式 (daemon.go):
- 维持 homed 连接,TUI实例经 Unix socket 接入
- 单客户端串行模型:每个TUI独占homed响应,新客户端回放缓冲(256行)
- 设备桥场景可无 homed 运行(纯设备桥驻留)
- 设备桥看护循环:WS断开自动重连(3-5s间隔)
- conn.go 新增 daemonConn 类型,dial() 优先检测 daemon
localuse 插件 (internal/plugins/localuse/):
- local_screensee / local_camerasue / local_speakeruse
- local_screensue / local_clipboardsee / local_clipboardsue / local_computeruse
- 跨平台实现(Linux/macOS/Windows),能力与 waiter device.go 对齐
- headless服务器上缺依赖工具自动返回安装提示
SDK sync: third_party SetToolBlocks 多模态类型同步
README 补充 daemon 模式使用文档
2026-08-27 09:27:15 +08:00
f0cdbdb030
feat(sdk): SettingsAPI.DataDir() 插件专属数据目录 + ai_image 本地交付
...
【SDK DataDir API】
- SettingsAPI 新增 DataDir() string:返回插件专属数据目录
<data>/plugin_data/<name>(内核保证存在),解决此前插件只能
靠 GetCore("daemon.data_dir") 手工解析的缺陷
- settingsImpl 新增 dataDir 字段 + SetDataDir;Registry buildSDK
注入(<data>/plugin_data/<name> 并 MkdirAll);main.go 接线
- cabi 新增 CORE_SETTINGS_DATA_DIR (id 51);plugindev dispatchSettings
模板补 DataDir() 实现
【ai_image 交付本地路径】
- 生成后下载临时 S3 URL 到插件数据目录,返回本地文件路径(永久不
过期),而非 1 小时过期的 S3 URL。带 UA 规避图床对无 UA 客户端拦截
(此前 agent 裸 curl 验证被拒导致误报失败)
- 返回 local_paths 字段 + 提示用 output_send(type=image) 展示
端到端:ai_image_generate → plugin_data/ai_image/*.png 有效 PNG(1024²),
经 llmsproxy→siliconflow 生成。
2026-08-26 21:40:29 +08:00
d0fc7e03c1
feat(ai_image): base_url 设置项支持自定义 OpenAI 兼容网关 v1.1.0
...
generateOpenAI 原硬编码上游为 https://api.openai.com,无法接入
本机 llmsproxy(ModelRouter) 等标准 OpenAI 兼容网关。新增:
- 设置项 base_url(string,默认空):为空保持官方直连;非空时
上游改为 {base_url}/v1/images/generations(约定不带 /v1 尾缀,
拼接时自动去重避免 /v1/v1)
- Plugin.baseURL 字段 Start 时读入;注册 ConfigDef(category
ai_image)供 WebUI 配置页展示
部署:plugindev 打包 1.1.0 → pluginmgr overwrite:true 升级安装
config_kept=true → plgreload 热加载。
配置(category ai_image):api_key=sk-gw-local-0001,
base_url=http://127.0.0.1:8081 , provider=openai,
model=Kwai-Kolors/Kolors, size=1024x1024
验收:agent 实调 ai_image_generate 出图成功——llmsproxy audit
记录 type=image src=siliconflow model=AUTO ok=true(经网关非直连),
返回临时 S3 URL 下载为有效 PNG (1024x1024)。
2026-08-26 19:46:59 +08:00
5fb15f5045
feat(a2a/acp): 会话历史查询 session.get + 客户端 session_id 透传
...
a2a:
- tasks.get 从空壳改为按 session_id 返回会话内近 N 条消息(默认10);
新增 session.get 别名同语义
- a2a_query(出站)接受 session_id 参数透传给目标 agent,
响应回显 session_id + 延续提示
- A2AParams/A2AResult 加 session_id 字段;工具描述补说明
acp:
- 新增 JSON-RPC method session/get:按 session_id 返回近 N 条消息
- acp_query(出站)接受 session_id 透传给 session/new,
响应回显 + 延续提示
- params 结构体加 limit 字段
端到端验证(回环本机):
a2a tasks.send→建会话;session.get→返回[user/agent]交替消息列表
同 session 第二轮延续上下文正确(记数字→答数字)
acp session/new + session/get 同样通过
2026-08-26 19:30:32 +08:00
14aa0c880b
fix(a2a/acp): 回复闭环 + 会话延续 + 同步注入(不再抢占打断)
...
【问题】
1. 入站请求用 InjectInterruptText 抢占打断当前对话,立即回 202 submitted,
agent 的回复 emit 到未注册的 channel(a2a/acp)→ 请求方永远拿不到回复文本,
只能干等超时。(acp 的 session.Replying 从未被填真回复 → SSE 永远 "(未收到回复)")
2. 无法指定/延续 session:a2a 无 session 概念;acp session/new 每次新建、
不接受调用方 session_id,多轮上下文断裂。
3. a2a/acp 通道未注册为输出设备 → emitResponse 的回复无落点,
output_list_channels 也不可见 → agent 困惑"回复该发到哪"。
【修复】
- 入站改用 SDK InjectInputSync 同步注入:阻塞等待 agent 处理完成,
直接把最终回复文本返回给 HTTP 请求方(不再回 202)。
这是 A2A/ACP 协议的合理形态——客户端控制超时,服务端同步返回。
- session 支持:a2a tasks.send 与 acp session/new 均接受 params.session_id,
指定则延续已有会话(拼上下文前缀),不指定则新建并返回 session_id。
单会话保留最多 10 轮历史防膨胀;30min GC 清理 2h 未用会话。
- a2a/acp 注册为输出通道(RegisterOutputChannel):回复有落点,
output_list_channels 可见,agent 可主动 output_send 推消息。
- 注入提示词明确"直接以文本回复即可,无需调 output_send"——
agent 不再把回复走 queued 入队而返回干净文本。
端到端验证:
单轮:status=completed, reply=真实回复文本(非 submitted)
多轮:同 session_id 第二轮准确复述第一轮问题(上下文生效)
a2a v1.2.0 / acp v1.1.0 安装 config_kept=true
2026-08-26 19:16:46 +08:00
41c46d131e
fix(plugins): bili output_dir 系统目录黑名单 + recoverydiag db_path 沙箱限制
...
P3 bili: output_dir 配置项此前未校验,yt-dlp 可被配置写到任意系统目录。
加系统目录黑名单(/、/etc、/usr、/var 等),命中直接拒绝执行。
P4 recoverydiag: db_path 参数 LLM 可控,可探测读取任意 sqlite 文件。
现强制限制在 data 目录内(前缀校验),越界返回提示。
两插件重打包升版(bili 1.2.0 / recoverydiag 0.2.0)安装验证
config_kept=true,内核重启加载正常。
2026-08-26 16:57:34 +08:00
75447aa7f8
fix(plugins): 全插件审查修复(qq/a2a/memo/calendar/rss/browser)
...
17 个生产插件全量审查:编译/vet 全过、无硬编码密钥、内核
executeToolCall 有 panic recover + 超时兜底。发现并修复:
P1 qq: downloadURL 裸 http.Get 无超时 → 120s client(挂起泄漏)
P2 a2a: inbound http.Server 无超时 → Read 30s/Write 120s/Idle 60s
(慢速连接占用 goroutine)
P5 memo/calendar/rss: 数据持久化直写 → atomicWriteJSON temp+rename
(崩溃截断 JSON 丢全部数据)
P6 qq: 3 处后台 goroutine(已读标记/rcon转发/下载任务)加 recover
(工具调用外 panic 会带崩 homed 进程)
P7 browser: dump-dom failback Kill 后补 wait 回收僵尸进程
已知可接受项:bili output_dir 用户可控(本机单用户)、recoverydiag
db_path 可读任意 sqlite(诊断工具固有权限,argv 传参无注入)。
全部经 plugindev 重打包 v+0.1 安装验证 config_kept=true。
2026-08-26 16:46:06 +08:00
c431af0902
fix(browser): render 改走共享后端(带登录态),v2.2.1
...
browser_render 原实现每次独立 chromium --dump-dom 冷启动:不带共享
profile 登录态、每次 ~2s 启动开销。现改为主路径经 CDP 9222 开临时
标签页渲染(Title+OuterHTML)后即关——登录态与 interactive 会话一致;
后端不可用时保持 dump-dom failback(补 30s 超时防挂死)。
返回新增 mode 字段(backend-tab / local-dump-dom)便于 agent 判断。
修复过程中清理 python 重写残留的重复函数定义。
2026-08-26 12:38:53 +08:00
bb0c274563
feat(browser): systemd 托管共享浏览器后端 + 全机 agent 标签页架构 v2.2.0
...
浏览器插件重新定位为本机所有 agent 的统一浏览器操作壳:
1. 主路径:homeagent-browser.service(systemd 托管)
- chromium --headless --remote-debugging-port=9222
--user-data-dir=<data>/browser_profiles/shared
- 独立于 homed 生命周期,崩溃自动重启,登录态持久保存
- 本机所有 agent(HomeAgent/pi/opencode/deepseekharness 等)
共享同一实例:登录一次全机可用
2. 每 agent 一个标签页(CDP Target 隔离),同 source 复用已有标签页
3. browser_start 探测链:CDP 9222 在线 → 直连;服务已装未跑 →
systemctl start 等待就绪;未安装 → 返回 need_install+guide 引导
4. 新工具 browser_install:探测 chromium 二进制 → 写 systemd 单元 →
daemon-reload + enable --now → 验证 CDP → 返回全机共享使用指南
(含其他 agent 经 connectOverCDP 接入的说明);无 root 权限时
返回手动安装命令清单
5. failback:无法联网装 chromium 的机器,重试 start 自动降级本地
spawn 临时模式(登录态不持久,仅保证功能可用)
工具链打包 v2.2.0 已部署验证:need_install 引导 / install 装服 /
start 复用与开新标签页全链路通过。
2026-08-26 12:22:15 +08:00
c945eace1a
fix(webui): 附件死锁 + qq 插件文件发送收敛到 output 通道
...
1. webui 附件死锁修复(output_send__webui 发图 60s 超时根因)
EventAgentOutput 订阅者已持 chatMu,附件分支调 addChatMsg(内部
再次 Lock)——sync.Mutex 不可重入导致 Publish 永久阻塞,工具超时、
图片永远发不出来。改为持锁状态下直接操作 chatHistory+persist。
2. qq 插件 image/file 分支收敛到 output 通道
output_send__qq 的 type=image/file 此前只透传 payload 给 CQ 码,
发本地文件必须绕道 upload_group_file 独立工具。现在与 voice 分支
同模式:本地路径拷入 NapCat 共享目录转 file:///app/files/<name> URI,
http(s)/file:// URL 保持透传。upload_group_file 保留作为群文件柜
专用入口。
验证:工具链重编译 → qq.hmap 重打包 → pluginmgr overwrite 安装
(config_kept=true)→ agent 经 output_send__qq 用本地路径发图成功,
mascot.webp 已落共享目录。
2026-08-26 11:14:03 +08:00
ae42e486de
feat(pluginmgr): 插件更新接口(upgrade/downgrade 保留配置)+ skill_install overwrite
...
内核 Registry 拆出 StopAndUnload:
- 停止并从注册表移除插件但保留 config_<name> 表
- 不触发 onRemove 回调(那是删除专用语义)
- RemovePlugin 改为追加清理配置表示清除,更新场景调 StopAndUnload
pluginmgr:
- installFromData/installFromURL/installFromPath 加 overwrite 参数
- 已存在+overwrite=true:StopAndUnload→备份旧目录→解压新包→失败回滚→
返回 action=upgraded/downgraded/reinstalled+previous_version+config_kept
- 已存在+overwrite=false:返回 error+hint(指向 overwrite 用法)
- cmpVersion 点分版本号数字比较(非字典序)
- 测试覆盖:首次安装→重装拒绝→升级保留配置→降级→失败回滚
skill_install 加 overwrite 参数:
- 同名技能存在时先卸载旧实例+删除目录再安装新包
SDK PluginMgr 接口同步加 StopAndUnload(name string) error
工具链 plugindev 已重建到 /usr/local/bin(7/29→8/25 版本)
QQ 插件诊断日志版(webhook recv 到达+isAtBot 失败日志)已打包并
通过 upgrade 接口热更新部署,配置保留验证通过。
2026-08-25 22:02:17 +08:00
2089d41c0f
sync: 同步 SDK PluginMgr API 与 dispatch 48/49/50 (vendored 副本)
2026-08-16 18:44:28 +08:00
0d963ed86f
sync: 同步 SDK meta.go CABINumMin 兼容修复(与 SDK 仓一致)
2026-08-15 17:26:33 +08:00
710b1afdf9
C ABI v2: invoke_stage 写回 + ABI 版本对齐核心版本号 (v0.9.0)
...
- C ABI invoke_stage 增加 result 输出参数,外部插件 stage 回调可将修改后的
StageContext(RawMessage/LLMText/FinalText/Response/ToolResults) 写回内核
- ABI 标识版本改为字符串 semver 与核心 Version 对齐(ABIVersion="0.9.0"),
C 层协商用派生整数 CABINum=900(major*100+minor),不再使用独立数字编码
- version_min 保证 v0.8.x(800) 旧插件向后兼容可加载
- 修复工具循环 zen 兼容补位误伤首轮 system 上下文(仅尾部为 assistant/tool 时补位)
- 更新 README 项目状态说明
2026-08-15 15:38:57 +08:00
09faa2874a
插件删除回调:onRemove 生命周期(仅卸载触发,重载不触发)
...
- 公共 SDK(third_party/homeagent-sdk):RegisterOnRemoveHandler/RunOnRemoveHandlers
(后注册先执行、幂等);内部 SDK PluginManager 接口新增 RemovePlugin
- registry.RemovePlugin:runStopHandlers → Stop → 清理 plugins/sdkRefs/instances
→ runOnRemoveHandlers → toolCleaner.UnregisterPluginTools → cfgReg.RemoveDisabledPlugin
- pluginmgr.removePlugin 先调 Registry.RemovePlugin 再删目录
- 配置清理:PluginSettings.Remove(key)(内部 SettingsAPI + settingsImpl)
- 演示:timer 插件 onRemove 删 max_duration 键;plugindev 模板同步 onRemove 示例;
SDK example/calendar cleanupData 删 events.json;manager plugins/uninstall 先停通道
2026-08-02 13:32:44 +08:00
76ef49e9a6
clawhubadapter: OpenClaw 通道插件兼容修复(gateway 生命周期桥 + channelRuntime + deliver 事件式输出)
...
- manager/main.js:makeChannelRuntime(dispatchReplyWithBufferedBlockDispatcher 入站 + deliver 出站
绑定 + typingCallbacks)、startChannels/stopChannels(gateway.startAccount fire-and-forget 生命周期
桥、listAccountIds/resolveAccount 规范签名 cfg 传参)、tools/call 通道分支无 outbound 走 deliver
事件式发送、SIGTERM 优雅停靠、console 输出重定向 stderr 防 JSON-RPC 流污染
- plugin.go:channel_input 改 InjectInputSync 同步注入取回复并经 CallTool 回发通道(修复
InjectInterruptText 无 ResponseCh 致回复静默丢弃);channel_status/channel_output 通知接入
- registry.go:channelStatus 状态缓存
- SDK:公共 IOInjector 增加 InjectInputSync(source, channel, text) string + ioAdapter 实现
- 生产验证:微信发消息 → pollLoop → dispatchReply → mock LLM 回文本 → deliver →
ilink/bot/sendmessage status=200 送达
2026-08-02 13:03:49 +08:00
1013aa0aa9
feat(sdk): add RegisterStopHandler/RunStopHandlers and wire stop cleanup into registry lifecycle
...
- SDK PluginSDK gains RegisterStopHandler(fn func()) + RunStopHandlers()
(LIFO, idempotent, cleared after running); synced to third_party copy
- Registry keeps per-plugin SDK refs (sdkRefs); StopAll/ReloadOne/
DisablePlugin run handlers before calling Stop()
- timer built-in plugin demonstrates handler-based shutdown cleanup
- z_bridge (linux/windows templates) runs handlers before plugin.Stop()
2026-08-02 10:23:40 +08:00
3a5a16c916
bump version to v0.8.0
2026-07-29 15:45:24 +08:00
a899d777c3
sdk: embed non-toolchain SDK in third_party, add NoMemory/Cleaner support
...
- Embed sdk/, example/, meta/, go.mod from homeagent-sdk (no .git)
- Core .gitignore excludes SDK toolchain: bin/, tools/, package/
- RegisterInputChannel + ChannelDef(NoMemory, Cleaner) in SDK
- IOManager input channel registry with GetInputChannelDef
- eventloop: apply channel Cleaner/NoMemory to interrupt text
- context engine: channelDefLookup applied in textForVector
- document store: ChannelCleaner param for archive functions
- All callers/adapters updated with ChannelDef{} default
2026-07-29 14:48:23 +08:00
1f1233b823
refactor: P0-P3 fixes, C1 cleanup, architecture diagrams, go.work upgrade
...
- P0-1: ProviderError type + ReportStatus for precise 401/403 detection
- P0-2: Remove -config flag from deploy/homeagent.service
- P2-1: 5s debounce on context.go Save()
- P2-2→C1: Delete output_set_channel entirely
- P2-3: Extract mediaDataURL/mediaChat helpers
- P2-4: Dedup defaultSources var
- P3: Delete dead packages (embed/tokenizer/container/snapshot)
- P3: Delete dead functions (messagesToMap, RunStageAll)
- CL: Update .gitignore, docs, Makefile, gojieba removal
- Config: Delete config/config.yaml, update docs
- Arch: Remove EmitOutputTo from emitResponse
- CL-1: go.work 1.19→1.21
- Docs: Add Mermaid architecture diagrams to README
- Docs: Add kernel-rebuild requires plugin-rebuild note to PLUGIN_DEV.md
2026-07-12 11:42:56 +08:00
dad62516a8
fix: openclaw translateAndRegister uses full tool name for LLM visibility
2026-07-10 13:55:03 +08:00
b55f1dcf0e
fix: cap distiller startup scan and switch to formal SDK module dependency
...
- replace vendored third_party/homeagent-sdk with formal module dependency
- keep canonical SDK via go.mod pinned commit
- optimize distiller startup loading to stream recent raw records only
- parse timestamps correctly and cap startup load to 5000 records
- remove quadratic string building in raw record parsing
- restore fast startup while preserving memory pipeline
2026-07-06 20:18:13 +08:00
50e5dec745
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
2026-07-06 19:27:51 +08:00