219b11a1f9
chore(release): SDK 定版 1.1.0(1.1.x 线全程共用)
...
按 核心仓 docs/git-branching.md §七.1,SDK 版本跟随核心的中版本、
patch 位恒为 .0:整条核心 1.1.x 线(1.1.0、1.1.1、…、1.1.N)共用 SDK 1.1.0。
**为什么不跟着核心 patch 走**:SDK 版本号是插件开发者的依赖声明。核心的 patch
专用于 bugfix 与漏洞修复,不碰公开接口;若每个 bugfix 都推一个 SDK 新号,
开发者要么被迫跟版、要么怀疑自己版本过时,而接口一个字都没变。
本版内容见 tag v1.1.0 的发布说明。
v1.1.0
2026-09-06 10:04:28 +08:00
da01af1ad7
chore(meta): main 的版本路牌推到 1.2.0
...
按 核心仓 docs/git-branching.md §2.1,两仓 main 的 meta.Version 都是
**下一个未发布中版本**。1.1.x 线正在发布中,所以 main 指向 1.2.0。
SDK 版本跟随核心的中版本(§七.1):release/v1.1.x 上定版 1.1.0,
整条核心 1.1.x 线共用它。
**此 commit 不 cherry-pick 到发布分支**(§五)。
2026-09-06 10:04:26 +08:00
741e284cd4
Merge branch 'feature/sdk-multimodal' — 多模态贯通插件边界
...
公开 SDK 新增媒体字段与媒体注入接口(全部新增,无签名变更),
修掉 PluginSDK 的 API 字段与 autoRestart 两处并发竞态,
工具链(proc 模板、mocksdk、方法清单断言)同步接线,README 中英双语补文档。
存量插件零改动零重编。
2026-09-06 10:04:26 +08:00
ce5bff9275
feat(sdk): 多模态贯通插件边界——媒体字段、媒体注入接口与并发修复
...
记忆系统在核心 1.1.0 支持了二进制多媒体节点,但那条链路只对**内核自己**开放:
插件把 Triple / Doc 交进来,媒体一律无处安放,且**不报错**。本版补上公开接口
侧缺失的表达能力。
## 一、类型与接口(全部新增,无签名变更)
- `Triple` += `SentenceText`、`MediaDigests`
- `Doc` += `MediaDigests`、`Attachments`;新增 `MediaAttachment`
- `TextEvent` += `Attachments`
- `DocMemoryAPI` += `InsertWithMedia`
- `IOInjector` += `InjectInputMedia` / `InjectInputMediaSync` / `InjectInterruptMedia`
- `PluginSDK` 补上一直缺失的 `SetToolBlocks` 包装(接口里有、便捷方法里没有,
插件只能自己去拿 injector)
`MediaAttachment` 一个类型服务两个方向:给 `Data`+`MIME` 是新内容(内核按字节
去重),只给 `Digest` 是引用已有内容。读路径**只回元数据不回字节**——一次检索
可能命中几十份媒体,把字节全塞回来会撑爆跨进程消息。
媒体注入为什么不能搭 `SetToolBlocks` 的车:那个方法只在工具处理函数内部可用,
且媒体要等**下一条** tool message 才到模型手上。插件主动发起一轮带媒体的对话、
以及中断注入,需要各自的签名,且媒体在**本轮**就随消息发出。
`Triple.MediaDigests` 非空而 `SentenceText` 为空时,内核会用媒体标记本身充当句子
——媒体引用挂在句子上,没有句子就无处挂起。插件只需填 digest,标记由内核拼:
要求调用方知道格式,等于让一个拼写错误静默切断引用绑定而全链路无人报错。
## 二、修掉两处并发竞态
`sdk/stress_test.go` 的 `-race` 实测报 11 处 DATA RACE,收敛到两个字段:
1. **`PluginSDK` 的 API 字段无锁**。写方是内核(加载/重载插件时依次注入
injector、memory、doc、llm…),读方是插件在 `Start()` 里起的后台 goroutine
——轮询、监听、定时器都要拿 injector 往管道注消息。生产表现是插件重载瞬间
偶发崩溃:读到半个接口值就 nil 解引用。
2. **`autoRestart` 标志无锁**。`SetAutoRestart` 的文档用法本身就是「外部连接建好
后再决定能否自动重启」,而连接建立通常在后台 goroutine;内核 registry 在另一个
goroutine 读 `AutoRestart()` 决定崩溃后重启策略。这对读写天然跨 goroutine。
加 `apiMu sync.RWMutex`。关键约定写进注释:**只在持锁期间取字段值,取完立刻
释放再调用**。持锁调用会把 `InjectInputSync`(阻塞到 agent 回复,可达数分钟)
与 `SetIOInjector` 串到一起,让插件重载卡死。
## 三、压测(sdk/stress_test.go,13 例)
SDK 是被多个 goroutine 同时使用的共享对象,单线程单测全绿不代表并发路径成立。
断言的是不变量而非吞吐:
- 媒体注入高并发不丢不串——每次调用带唯一 tag,逐条校验文本与图片 URL 配对。
「不串」是重点:若实现里出现任何共享中间状态(把 blocks 暂存到字段再读出),
高并发下会出现 A 的文本配 B 的图,而两者单独看都「成功」了;
- injector 热替换(含替换成 nil,即内核卸载 API 的真实状态);
- stop / onRemove handler 恰好一次——契约是「执行后清空,幂等」,执行两次的后果
从重复写文件到 close 已关闭 channel 直接 panic;
- `StageContext` 并发读改写无 lost update(媒体链路让 Extra 成为新热点,
而 map 并发写在 Go 里是直接 fatal,recover 接不住);
- `OwnTools` scope 不跨插件泄漏;
- 媒体类型 JSON 往返字节级一致(9 种长度,含 0/1/2/3 与 base64 分组边界)
——`[]byte` 在 JSON 里是 base64,往返不一致意味着图片静默损坏,
要到 CAS 校验 digest 时才发现,那时已无从追查;
- `omitempty` 真的生效(读路径不能出现 `"data"` 键);
- nil 依赖全部静默降级不 panic。
## 四、工具链同步
- `proc_main.go.tmpl`:`procIO` 三个媒体方法、`procDocMemory.InsertWithMedia`。
模板不跟上的后果是**每个外部插件都编不过**(接口未实现),是硬失败;
- `proc_runtime_test.go`:方法清单补 `io.injectMedia*` 与 `doc.insertWithMedia`。
漏接线时插件调 `InjectInputMedia` 会静默无效果——模板不发这个 RPC,内核也就
收不到,两边都不报错;
- `yaegi/mocksdk`:与公开 SDK 对齐。它此前漂移严重且**没有任何代码对着它编译**,
所以漂移不会被编译器抓到:`Triple` 用的是 `Predicate`,而公开 SDK 一直叫
`Relation` —— 插件在 yaegi 调试期写 `Relation:` 报未知字段,写 `Predicate:` 则
编成 plugin.bin 时报错,两边都不对。
- README 中英双语补媒体接口文档与用法示例。
## 兼容性
存量插件不需要改一行也不需要重编:新增方法由**插件调用、内核实现**,不调就不
受影响。17 个 example 插件源码零改动通过类型检查;用 SDK 0.9.2 编的旧 plugin.bin
在新内核上直接建链通过(握手校验的是 ProtocolVersion=1,不是 SDK 版本)。
媒体接口需要核心 1.1.1+(更早的核心没有对应 RPC,调用返回 unknown method)。
`CoreVersion` 保持 1.0.0:它是「SDK 能在其上运行」的下限,媒体是可选能力。
2026-09-06 10:03:36 +08:00
e256023399
docs+plugindev: README 同步子进程架构,scaffold 修 entry 与 go.sum 死路
...
三类问题,都会让新用户第一次上手就走错:
1. README 仍写 plugin.so
plugin.json 示例的 entry、entry 字段说明、.hmap 包格式三处描述的都是
已退场的 C ABI 产物。改为 plugin.bin,并补 bundle 模式下
plugin.bin.<goos>.<goarch> 的命名与安装时挑平台的行为。
2. cmd_init.go 的 entry := "plugin.so"
scaffold 出来的 plg.json 带着一个已退场的 entry 值。build 实际不看这个
值(只用它区分 Lua),但跟着模板走会误以为自己在做 C ABI 插件。
3. 生成的项目第一次 build 必定失败
go.mod 只 require 一个 gitcode 模块版本号且不生成 go.sum。gitcode 不在
proxy.golang.org 上,于是:
go build → missing go.sum entry
go mod tidy → 去公共 proxy 拉一个不存在的条目,超时
原来 cmdBuild 里那句 `go mod download <mod>` 走的正是这条死路,失败后
只打一行 warn 就继续编译,紧接着死在同一个错误上——用户看到两段无关报错。
修法分两处:
- 生成的 go.mod 直接写指向本机 SDK 的 replace(replace 到目录时 go 不
需要也不校验 go.sum)
- 新增 ensureSDKResolvable:三级策略(已有本地 replace → 探测本机 SDK
并写入 → 兜底 go mod tidy 带 -mod=mod,失败给可操作提示)。存量项目
go.mod 无 replace 时走第二级救回。
bin/ 5 个预编译二进制不再进仓库(改为 release 附件):
5 个平台各 26-28MB,每次重编都在 git 历史里再叠一份,而它们本质是可从源码
复现的产物。README 的下载说明同步改为 release 附件 URL + 从源码编译。
v1.0.0
2026-09-03 19:27:03 +08:00
092d8f4ab0
Merge branch 'feature/plugin-proc-migration' into main
...
SDK 侧配合内核子进程化迁移:plugindev 工具链产出 plugin.bin
(纯 Go 二进制,零 cgo),模板支持共享内存 stage 与事件环消费。
- entry 语义收敛到 plugin.bin,删除 C ABI 工具链
- stage 回传改 diff,只回传变更字段(修复 lost update)
- 模板支持事件环消费(evtConsumerLoop)
- Windows 共享内存适配(OpenFileMappingW / OpenEventW)
- meta.Version 升到 1.0.0,与内核对齐
公开 SDK 接口(sdk/ 目录)全程零改动——接口冻结不变量。
2026-09-03 13:54:21 +08:00
5ed8d65479
meta: 版本升到 1.0.0,删除 C ABI 时代的死常量
...
## 为何是主版本号
公开 SDK 接口(sdk/ 目录)本轮**零改动**,插件业务代码一行不用改。
但产物形态变了:plugin.so → plugin.bin。0.9.x 内核只会 dlopen `.so`,
本版工具链产出的 `plugin.bin` 在旧内核上根本不会被识别——
这是不可互操作的破坏性变化,故 CoreVersion 也升 1.0.0 作为**硬下限**
而非建议值。
## 删掉的死代码
ABIVersion / ABIVersionMin / CABINum / CABINumMin / 51 个 Core<Method>
整数 ID,全部无使用者:
grep -rn "CABINum" --include=*.go --include=*.tmpl . → 只有定义处
grep -rn "meta.Core[A-Z]" ... → 无
它们随 Part 6.2 删除 internal/plugin/cabi/ 就已失效:
- 整数 method id 平移为 method 名字符串(proc/protocol.go 的 Method* 常量)
- 版本协商改为握手帧里的 protocol 字段
留着有实际危害——下一个读这个文件的人会以为 C 层协商还在生效,
或者以为加 method 时要同步维护那张整数表。
## 协议版本与语义版本解耦
新注释写明:子进程 RPC 的 protocol 是独立小整数(当前 1),
只在帧格式或握手语义变化时升;语义版本变动频繁(修 bug、加字段)
不应牵动 wire 协议。这两者以前被 ABIVersion = CoreVersion 绑在一起,
现在分开。
验证:go build ./... 通过;git diff sdk/ 为空(接口冻结不变量)。
2026-09-02 22:40:15 +08:00
9f844123fe
plugindev: entry 语义收敛 + 删 C ABI 工具链 + Windows 共享内存适配(Part 6.1)
...
## entry 不再是通道开关 —— 外部插件零改动的关键
17 个存量插件的 plg.json 都写着 "entry": "plugin.so"。若把 entry 当通道
开关,迁移就得改 17 个文件,而「外部插件零改动」是本次迁移的硬约束。
改法:Go 插件一律产出 plugin.bin,不看 entry 值。isProcEntry 删除,
resolveBuild 去掉 proc 参数。entry 现在只剩区分 Lua(main.lua)一个用途。
实测:weather 的 plg.json 一行不改(仍写 plugin.so),plugindev build
直接产出三平台 plugin.bin。
## Windows 不再是能力退化的第三套实现(§9.2 的正解)
C ABI 时代 Windows 是独立的第三套 ABI:dynamic_dll_windows.go 的 stage
只下发 3 个字段(raw_message/user_id/phase)且完全没有写回,sanitizer
这类改写型插件在 Windows 上静默失效,且无任何运行时警告。
现在 Windows 与 Unix 共用同一份 RPC 逻辑与同一份共享段布局。平台差异
收敛到三个挂载函数:
- Unix(linux/darwin/freebsd):内核经 ExtraFiles 传继承 fd(3=StageContext
段,4=事件环段,5=eventfd/pipe)
- Windows:没有 fd 继承语义(os/exec 的 ExtraFiles 在 Windows 不支持),
改用命名内核对象——父进程 CreateFileMapping/CreateEvent 建带名字的对象,
子进程 OpenFileMappingW/OpenEventW 按同名打开。名字经环境变量传入而非
硬编码:多个 homed 实例并存时不能撞名。
Windows 绑定用 syscall.NewLazyDLL 而非 golang.org/x/sys/windows:
OpenFileMappingW/OpenEventW 未被标准库 syscall 导出,而引入 x/sys 会给
**每个插件的 go.mod** 加一个新依赖,违反「插件仅依赖公开 SDK」。
LazyDLL 属标准库,零新增依赖。
新增 evtWaiter 接口抽象等待语义:eventfd 是计数器(多事件合并成一次
唤醒),Windows Event 是二元信号。不影响正确性——消费者被唤醒后按
readSeq 追 writeSeq 批量 drain,一次唤醒能处理累积的全部事件。
模板拆成三个文件:
proc_main.go.tmpl 平台无关(RPC + 共享段布局 + stage + 事件环消费)
proc_shm_unix.go.tmpl 继承 fd 挂载
proc_shm_windows.go.tmpl 命名对象挂载
## 删除 C ABI 工具链
templates.go 1296 → 516 行:
- tmplBridge(Windows DLL bridge) -265 行
- tmplLinuxBridge(Linux c-shared) -457 行
- tmplPluginInitC(C 入口) -57 行
另删 generateBridge / detectWindowsCC(MinGW 探测)/ tmplCABIHeader /
InitData.CABIVersion+CABIHeader。
交叉编译不再需要目标平台 C 工具链——这是 -buildmode=c-shared 退场的
连带收益(§3.1)。
## 测试
15 项全过,新增 4 项守护迁移不变量:
- AllPlatformsProduceBin:6 个 GOOS/GOARCH 组合统一产出 plugin.bin
- LuaIsSeparatePath:Lua 仍走解释器路径
- UnsupportedOSErrors:不支持平台明确报错,不静默产出错误产物
- NoCABIResiduals:代码中不得再出现 c-shared / CGO_ENABLED=1 /
detectWindowsCC / tmplLinuxBridge / tmplPluginInitC(注释除外)
- IgnoresEntryForGoPlugins:isProcEntry 必须已删除
验证:go build/vet/test 全通过;三平台交叉编译产出 plugin.bin;
git diff sdk/ 为空(接口冻结)。
Ref: docs/zh/架构迁移评估.md §3.1/§9.2、docs/zh/plugin-migration-plan.md Part 6
2026-09-02 18:39:02 +08:00
ef0e58ee23
plugindev: 模板支持事件环消费(Part 5 子进程侧)
...
handleHandshake 额外挂载 fd 4(事件环段)+ fd 5(eventfd),启动
evtConsumerLoop goroutine 消费事件。
HandshakeParams 新增 evt_ring_size 字段(0 = 不支持事件环)。
events.subscribe:按类型列表在本地注册 handler,evtConsumerLoop 从
共享段读 slot 后按位索引分发。events.unsubscribe 清空全部 handler。
事件环布局常量与内核 internal/plugin/proc/evtring.go 一一对应。
验证:weather.bin 零改动编译通过;E2E 测试全通过。
Ref: docs/zh/架构迁移评估.md §3.6
2026-09-02 17:05:01 +08:00
09b64dcb53
plugindev: 支持子进程插件构建(entry=plugin.bin,零 cgo)
...
Part 3 工具链改造。插件业务代码零改动,只需把 plg.json 的 entry
从 plugin.so 换成 plugin.bin。
新增 templates/proc_main.go.tmpl(1113 行)——子进程运行时:
- 51 个 core method 的插件侧 RPC 实现(procIO/procMemory/procSettings/
procSocial/procLLM/procKnowledge/procDocMemory/procTextMemory/procPluginMgr)
- 共享段访问(fd 3 = 内核经 ExtraFiles 传入的 memfd)+ 16 字段
StageContext 编解码,布局常量与 internal/plugin/proc/shm.go 逐一对齐
- handleStageInvoke:拿锁 → 读段 → handler → **只写脏字段** → 放锁。
只读插件脏字段集为空 → 零写入 → 不可能覆盖他人改写
(对照 C ABI 副本模型实测 35.8~36.8% lost update)
- 主循环每请求独立 goroutine:handler 内会反向调用内核并等应答,
在读循环里同步处理会死锁
- plugin.start 后显式上报 AutoRestart:公开 SDK 的 SetAutoRestart 是纯
setter 无 hook,隔着进程边界内核读不到(内核侧 corehandler.go:145 已就绪)
模板选择真实 .go 源文件 + //go:embed 而非 raw string:900+ 行代码塞在
字符串里写错只能等生成插件时才炸,作为源文件可被 parser/gofmt/vet 检查。
cmd_build.go:resolveBuild(target, proc) 分派;proc 走 go build -trimpath
+ CGO_ENABLED=0,交叉编译不再需要目标平台 C 工具链。bundle 模式各平台
产物同名故 zip 内加平台后缀(plugin.bin.linux.amd64)。
proc_runtime.go 生成时清理残留 z_bridge_gen.go/z_entry.c——同目录两套
main 会编译冲突,这让 .so → .bin 切换无需人工清理。
proc_runtime_test.go 16 项静态检查,防内核/插件两侧漂移:
method 名清单、7 个内核调用、共享段常量与字段枚举顺序、stage 加锁顺序、
快照必须存序列化字符串(切片共享底层数组的坑在 11.3 已踩过)、
arena 不足须报错、日志走 stderr、版本不匹配须拒绝、零 cgo。
验证:真实 plugindev 构建 example/weather,plugin.go 逐字节未改,
产出静态链接 ELF;git diff sdk/ 为空(接口冻结)。
Ref: docs/zh/架构迁移评估.md §3、docs/zh/plugin-migration-plan.md Part 3
2026-09-02 12:13:45 +08:00
56485194df
fix(plugindev): stage 回传改 diff,只回传变更字段,修复 lost update(plan 11.3)
...
根因:go_invoke_stage 无条件回传 stageContextWritable 全部字段(含插件从内核收到
的旧快照),sanitizer(改ToolResults)+weather(只读) 并发时,只读插件把自己收到的
旧快照覆盖回清洗结果(实验13 实测丢失率 1.6~4.3%,脏数据进LLM)。
改动:
- templates.go: 新增 snapshotWritable(handler 前的序列化快照)+ changedFieldsOnly(只回传差异字段)
go_invoke_stage 改为 before 快照 → handler → diff 回传,无变更零回传
- ❗ 关键陷阱(第一版踩坑):stageContextWritable 的切片字段与 sc 共享底层数组,
handler 原地改元素时 before 快照跟着变,diff 失效——故 before 必须序列化成字符串
- stagediff_test.go: 6 用例(只读零回传/原地改切片/标量改/新response/清空切片/现网场景复刻)
⚠️ 需用新 plugindev 重编全部 17 个外部插件(bridge 模板变更)
2026-08-31 12:29:57 +08:00
61f307be1a
feat(qq): msg_id→get_history 7天兜底 + list_chats/mark_read 会话列表 v1.2.0
...
## 问题
1. NapCat get_msg 的 message_id 是 QQ 服务端临时短号,约 3 天后失效。
实测 1306 条真实 webhook 消息,141 条(10.8%)现在查回报「消息不存在」,
全是 3 天前的旧消息;1 小时内的消息可正常查回。NapCat 侧无保留时长配置项
(napcat.json / onebot11_*.json / webui.json 均无),是 QQ 协议硬限制。
模型收到 not_found 后曾编造正文(虚构 message_id + 虚构需求),
已在核心 prompt 加事实性约束,此处从插件层根治取不到正文的问题。
2. 插件与真人客户端差距大:没有会话列表、消息不按到达先后排序、无未读提醒,
模型只能靠单条中断消息被动响应,导致消息处理不及时。
## 改动
### msg_id → get_history 兜底(不缓存正文)
- 新增 msgRef{peerID,isGroup,time}:只记 msg_id → (peer, 时间) 映射,7 天 TTL
(qqMsgTTL),超 2000 条时惰性清理过期项。不缓存消息正文。
- handleGetMessage 改为包装层:命中映射且 <7 天 → getMsgFromHistoryByTime()
按 peer 拉 get_history(count=50),取时间最接近的一条,
经 msgToGetMsgResult() 包装为与 get_msg 同构的结果(附 resolved_via:history);
未命中或超 7 天 → 回退原 NapCat get_msg(重命名为 getMsgFromNapcat)。
- 超 7 天的消息由 get_history 自行处理,插件不做长期缓存。
- not_found 文案改为明确引导改用 qq_get_history / qq_list_chats。
### 会话列表(对齐真人客户端)
- 新增 chatMeta:会话名、未读数、最新一条 ≤60 字摘要(qqLastSumLen)、最新时间。
只维护最新一条摘要,不存历史。
- 新增 qq_list_chats:按最新消息时间降序返回会话列表,每项含
peer_id/type/name/unread/last_text/last_nick/last_time。
- 新增 qq_mark_read:按 group_id/user_id 清零未读;get_history 拉取某会话后
自动标已读(看过=已读,与真人客户端一致)。
- webhook 记录时机前移:策略允许的消息(群/私聊、是否 @bot 均记)都进入映射与
会话状态,@bot 只决定是否发中断——与真人客户端一致能看到全部会话。
### 中断模板
- 补 fallback 路径与私聊 user_id(原模板只给 message_id,取不到正文时
模型没有 peer 信息可用于 get_history):
「先用 get_message 取正文;若取不到(已过期),改用 get_history(...) 按会话拉取,
或用 list_chats 查看未读会话。用 output_send__qq 回复」
## 验证
用重建的 plugindev build --target linux/amd64 产出 dist/qq_linux_amd64.hmap,
经内核 plugin_install(overwrite=true) 安装(action=reinstalled, config_kept=true),
重启 homed 后内核注册 20 个 qq_* 工具(原 18 + list_chats + mark_read)。
实测 qq_list_chats(count=8) 返回按时间排序的会话,unread=6 正确累积。
注意:cgo c-shared 插件带完整 Go runtime,dlclose 后引用计数不归零,
同路径 dlopen 复用旧映像,plgreload 无法热替换 .so,换 .so 必须重启 homed。
2026-08-30 17:11:57 +08:00
59c6e1844c
fix(plugindev): bridge 模板补 dispatchIO.SetToolBlocks,修复外部插件无法编译
...
SDK v0.9.2(68497b4)给 IOInjector 加了 SetToolBlocks,但 plugindev 的
C ABI bridge 模板(tmplLinuxBridge)未同步,导致任何外部插件在当前 SDK 下
编译失败:
z_bridge_gen.go:97: cannot use dispatchIO{} as sdk.IOInjector value in
argument to base.SetIOInjector: dispatchIO does not implement
sdk.IOInjector (missing method SetToolBlocks)
补空实现满足接口。SetToolBlocks 是 Go 原生(进程内 IOManager)的多模态注入,
跨 ABI 无对应 method id 与内核桥接,故不做 callVoid 转发。
实测:example/qq 用重建后的 plugindev build --target linux/amd64 构建通过,
产出 .hmap 经 plugin_install 安装、重启后 20 个工具全部注册成功。
2026-08-30 17:11:20 +08:00
5c1574be25
bump: sdk v0.9.2 (SetToolBlocks + DataDir)
v0.9.2
2026-08-27 09:32:01 +08:00
68497b4092
feat(sdk): IOInjector.SetToolBlocks + ContentBlock/ImageURL/AudioURL 多模态类型
...
SDK 公共层新增多模态内容块类型;IOInjector 接口新增 SetToolBlocks
方法让插件工具注入 image_url/audio_url 块,内核 process.go 消费后
追加到 tool message 的 content 数组。详见 TrueAgent 仓库 multimodal。
2026-08-27 08:39:40 +08:00
130f805b6e
feat(sdk): SettingsAPI.DataDir() + plugindev dispatchSettings 补 DataDir
...
SDK SettingsAPI 新增 DataDir() 插件专属数据目录;
plugindev 工具链 dispatchSettings 模板补 DataDir 实现(callString 51)。
ai_image example 改用 DataDir + 本地交付。详见 TrueAgent 仓库。
2026-08-26 21:41:28 +08:00
cd1984e26e
feat(ai_image): base_url 设置项支持自定义 OpenAI 兼容网关 v1.1.0
...
generateOpenAI 支持配置 base_url 指向 OpenAI 兼容网关(如本机
llmsproxy),为空保持官方直连。已实测经 llmsproxy→siliconflow
(Kwai-Kolors/Kolors) 生图出有效 PNG。
2026-08-26 19:47:01 +08:00
6184736fd4
feat(examples): a2a/acp 会话历史查询 + 出站 session_id 透传
...
a2a tasks.get/session.get 返回会话近N条消息;acp 新增 session/get;
a2a_query/acp_query 接受 session_id 延续对方会话。详见 TrueAgent 仓库。
2026-08-26 19:30:34 +08:00
81bfdfce1d
fix(examples): a2a/acp 回复闭环 + 会话延续 + 同步注入
...
入站请求从 InjectInterruptText(202 submitted) 改为 InjectInputSync
同步等待回复,直接返回回复文本;支持 params.session_id 延续多轮
上下文;注册为输出通道让回复有落点。详见 TrueAgent 仓库同名 commit。
2026-08-26 19:16:49 +08:00
e3f93e254b
chore(recoverydiag): 补齐 go.mod 与 main.go(与其他插件结构一致)
2026-08-26 17:05:24 +08:00
d57c5eaf3e
fix(examples): 全插件安全审查修复(qq/a2a/memo/calendar/rss/browser/bili/recoverydiag)
...
审查发现并修复 7 项问题:
- P1 qq: downloadURL 裸 http.Get 无超时 → 120s client
- P2 a2a: inbound http.Server 零超时 → Read 30s/Write 120s/Idle 60s
- P3 bili: output_dir 配置项零校验 → 系统目录黑名单(/、/etc、/usr、/var 等)
- P4 recoverydiag: db_path LLM 可控任意 sqlite → 强制限制 data 目录内
- P5 memo/calendar/rss: os.WriteFile 直写 → atomicWriteJSON (temp+rename)
- P6 qq: 3 处后台 goroutine(已读/rcon转发/下载)加 panic recover
- P7 browser: dump-dom failback Kill 后补 wait 回收僵尸进程
recoverydiag 此前被 .gitignore 排除,但其 db_path 安全修复
属生产代码,故取消忽略并入库。
全部经 plugindev 重打包升版安装验证 config_kept=true。
2026-08-26 17:04:41 +08:00
16b4a56ee8
feat(sdk): 补齐流式增量事件常量 EventReasoningDelta/EventContentDelta
...
外置 SDK 缺失流式 delta 事件常量——外部插件无法订阅 token 级增量。
- 常量值与内核 internal/events/bus.go 完全对齐
- 向后兼容:旧插件不订阅即无影响;聚合事件仍照常发布
v0.9.1
2026-08-25 13:54:25 +08:00
2e6d037bb9
chore: ignore example/recoverydiag (本地调试工具,含生产路径)
2026-08-25 13:22:51 +08:00
cf77bf389e
chore: bump version to v0.9.1
...
- Version: 0.9.0 -> 0.9.1
- CoreVersion: 0.9.0 -> 0.9.1
与主仓库 HomeAgent v0.9.1 配套发布。SDK Go 代码自 33a79de 后无变更。
2026-08-25 13:22:18 +08:00
fc876c5554
feat: 单插件重载等 PluginMgr 能力导出到外部 SDK
...
- meta: CORE_PLUGIN_RELOAD_ONE(48) / LIST_LOADED(49) / IS_DISABLED(50)
- sdk: PluginMgrAPI 接口(ReloadOne/ListLoadedPlugins/IsPluginDisabled) +
PluginSDK.SetPluginMgrAPI/PluginMgr() 访问器
- plugindev 模板: dispatchPluginMgr 桥接注入,走 C ABI 48/49/50
2026-08-23 19:47:55 +08:00
5c5df9cfb9
demo(luademo): pre_action stage 展示写回(llm_text 追加标记)
2026-08-15 18:11:33 +08:00
c91739d670
fix(qq): parseIDList 兼容科学计数法存库的历史坏值
...
配置里 QQ 号被 WebUI 以科学计数法(2.198972886e+09)存库时,
ParseInt 解析失败导致 adminIDs 为空、老大消息不被标记。
ParseInt 失败后回退 ParseFloat, 整数值转为 int64。
v0.9.0
2026-08-15 17:24:21 +08:00
6527a40539
fix: CABINumMin 恢复为 1 兼容旧 ABI 插件(仅缺 stage 写回)
2026-08-15 16:34:25 +08:00
392f391f68
v0.9.0: C ABI v2 stage 写回 + ABI 版本对齐核心版本号
...
- meta: ABI 标识版本改为字符串 semver(ABIVersion=CoreVersion="0.9.0"),
C 层协商用派生整数 CABINum=900(major*100+minor),不再用独立数字编码
- plugindev 模板: invoke_stage 增加 result out 参数(stage 写回),
插件在 OnInput/AfterToolcall/PostAction 修改 StageContext 后回传内核
- cmd_init: CABIVersion 改用 CABINum
- example/sanitizer: 增强为全链路清洗(坏 UTF-8/U+FFFD/ANSI 转义),
挂载 OnInput/AfterToolcall/PostAction 三阶段(依赖 stage 写回能力)
2026-08-15 15:52:10 +08:00
cca9fdce9c
example: 新增 acp(ACP 代理通信)、vanblog(VanBlog 博客管理) 插件; 多插件工具调用检测与清理改进; weather 移除 onRemove/文本记忆; plugindev 精简
2026-08-15 09:03:05 +08:00
b6e30f9279
tools: plugindev 工具链支持公共 IOInjector.InjectInputSync(CORE_INJECT_INPUT_SYNC=47,z_bridge dispatchIO 桥接),重建 bin 预编译二进制;example/memo: plg.json 修复(name_en 去斜杠、去 BOM);sdk/plugin.go 注释精简
2026-08-02 16:24:59 +08:00
8e5610c494
example/memo: 待办与备忘分离(待办提醒、备忘纯记事)
...
- 待办(todo_add/todo_complete/todo_list):保留原有主动提醒能力——
stagePreAction 注入未完成条数 + periodicCheck 每 5 分钟 InjectInterruptText;
数据文件 todos.json
- 备忘(memo_create/memo_list/memo_delete):纯记事用途,不参与任何提醒
(无 stage 注入、无周期中断);数据文件 memos.json
- cleanupData 卸载时清理两个数据文件;ID 各自独立递增
2026-08-02 15:47:37 +08:00
1796395668
sdk: 公共 IOInjector 补 InjectInputSync(同步注入并等待回复)
...
与主仓 third_party/homeagent-sdk 对齐:IOInjector 接口新增
InjectInputSync(source, channel, text) string + PluginSDK 便捷方法
(通道插件请求-响应流:转发入站消息并取回 agent 回复文本)
2026-08-02 15:30:22 +08:00
f3d87ec35f
docs: 生命周期文档补全 onRemove(删除清理)说明
...
- README.md/README_EN.md:新增删除清理(onRemove)小节——语义(仅卸载
触发、重载/禁用不触发,Stop 之后执行)、内核配套清理(工具注册/disabled/
配置项定义 plugin.<name>.*/配置表 config_<name>)、示例清单与代码片段
- plugindev 模板 README.md.tmpl:新增 Lifecycle 段(RegisterStopHandler 每次
停止、RegisterOnRemoveHandler 仅卸载)
2026-08-02 15:23:22 +08:00
aee63a4f98
example: 演示插件 onRemove 清理补齐(memo/rss/weather)
...
- memo: 卸载时删除 memos.json 数据文件
- rss: 卸载时清理 .homeagent/rss 订阅数据目录
- weather: 卸载时清理 .homeagent/weather 缓存目录
- 与 calendar(events.json)一致:仅删除触发、重载不触发;
files(filesDir 为用户配置的访问根目录)、bili/qq(用户下载资产)、
ocr(临时目录函数内自清理)按语义不加入删除回调
2026-08-02 13:37:59 +08:00
fb07081929
插件删除回调(onRemove)与模板/示例同步
...
- sdk: RegisterOnRemoveHandler/RunOnRemoveHandlers(仅卸载时触发,重载不触发;
后注册先执行、幂等),与 RegisterStopHandler/RunStopHandlers 并存
- plugindev: 模板 main.go.tmpl 新增 onRemove 演示(删配置键),templates.go 同步
- example/calendar: RegisterOnRemoveHandler(p.cleanupData) 卸载时清理 events.json
- README/README_EN: 生命周期文档补充 onRemove
- example/calendar/plg.json: 版本对齐
2026-08-02 13:33:25 +08:00
db5d3133ea
docs: document pre-built plugindev binaries in bin/
2026-07-31 13:17:19 +08:00
12a8e99892
plugindev: rebuild pre-built binaries with latest toolchain (no local replace, auto go mod download)
2026-07-31 13:16:13 +08:00
62447e3952
plugindev: generate go.mod without local absolute replace; auto-download SDK module on first build
...
- init 生成的 go.mod 只 require SDK 线上版本(gitcode.com/JianFeeeee/homeagent-sdk v0.8.0),不再写本地绝对路径 replace
- build 仅在显式指定(plg.json sdk_path 或 --sdk-path)时写入 replace
- 首次构建自动执行 go mod download <sdk_module> 生成 go.sum(修复无 go.sum 构建失败)
- 修正 ensureGoMod 模块名解析(支持 require 行与 require 块)
2026-07-31 13:12:48 +08:00
bc1a005885
docs: fix plugindev install endpoint and build output; examples table; fix NewPluginFactory in bridge template
...
- README 构建与安装:输出目录为 dist/,安装端点为 pluginmgr POST /plugins(JSON 或 raw body),删除不存在的 /api/plugins/install
- 示例表:weather/luademo 新增、qq 更新 17 工具(补入 2b54814 未提交部分)
- templates.go: go_init_plugin 调用 NewPluginFactory(模板插件仅导出该函数)
2026-07-31 13:00:09 +08:00
2b54814037
examples: update weather with v0.8.0 API surface, add luademo Lua example, fix go.mod replaces
2026-07-31 10:59:13 +08:00
429fe9e1b9
plugindev: fall back to git clone when SDK archive download unavailable
2026-07-31 10:32:46 +08:00
87136057b1
plugindev: align Lua SDK mock with external plugin surface, refactor debug
v0.8.0
2026-07-31 10:28:08 +08:00
84bf100a12
example/qq: add typing indicator on private messages
...
- setInputStatus helper wrapping NapCat set_input_status API
- startTyping/stopTyping with goroutine-based typing manager
(5s interval, 30s auto-timeout)
- start typing on webhook private message receipt
- stop typing when output channel sends a response
- fix plg.json UTF-8 BOM that broke plugindev parse
2026-07-30 09:24:20 +08:00
78ef7998c2
docs: add RegisterInputChannel, ChannelDef to SDK README
2026-07-29 15:51:32 +08:00
b166697dd7
bump version to v0.8.0
2026-07-29 15:45:33 +08:00
4d01e75282
example/qq: use relative replace path, disable bundle mode
2026-07-29 15:24:43 +08:00
c5bcae9404
sdk: add RegisterInputChannel + NoMemory/Cleaner support
...
- ChannelDef struct: NoMemory bool + Cleaner func(string) string
- InputChannelRegistrar interface + RegisterInputChannel method
- RegisterOutputChannel now takes ChannelDef parameter
- QQ example registers input channel with NoMemory:true + Cleaner
2026-07-29 14:46:15 +08:00
c7c66b8d39
docs: 修正 plugindev 工具链描述 + 补充入口函数/Lua 插件说明 + 补全示例插件列表
2026-07-28 21:56:54 +08:00
04837f237d
plugindev: 全面 plg.json 持久化 + --replace 支持 + 文档
v0.7.2
2026-07-25 15:47:49 +08:00