Commit Graph

14 Commits

Author SHA1 Message Date
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 的发布说明。
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
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
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
5c1574be25 bump: sdk v0.9.2 (SetToolBlocks + DataDir) 2026-08-27 09:32:01 +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
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
b166697dd7 bump version to v0.8.0 2026-07-29 15:45:33 +08:00
04837f237d plugindev: 全面 plg.json 持久化 + --replace 支持 + 文档 2026-07-25 15:47:49 +08:00
1fa3c843ab refactor: centralize ABI metadata into meta/meta.go
- meta/meta.go: add ABIVersion, ABIVersionMin, 45 dispatch method IDs
- tools/plugindev/cmd_init.go: cabiVersion -> meta.ABIVersion
- tools/plugindev/templates.go: C header comments reference meta.go
2026-07-25 14:42:32 +08:00
cb7999ca71 feat: sdk NoMemory/Cleaner + example build fixes
- sdk/plugin.go: ToolDef adds NoMemory/Cleaner fields
- sdk/plugin_test.go: unit tests for NoMemory/Cleaner
- all example plugins: NoMemory/Cleaner annotated for each tool
- plugindev/templates.go: template shows NoMemory/Cleaner pattern
- plugindev/cmd_build.go: fix ensureGoMod, buildBundle/buildTarget sdkPath param, NewPluginFactory
- example/go.mod: add external dependency declarations (chromedp, gofeed)
- add main.go stubs for all example plugins
2026-07-25 11:17:21 +08:00
1762f0c34b docs: 修正全部文档使其与源码实现一致
- Plugin.Start(sdk *PluginSDK) 接口签名改为指针
- 方法表重写: 移除 CallLLM/QueryKnowledge/SetMemory 等不存在方法
- IOInjector 参数顺序修正为 (source, channel, text)
- 删除虚构 SDKConfig, 替换为实际 New() 构造函数签名
- .hmap 内容描述一致化 (plugin.so + plugin.dll + main.lua)
- 添加 meta/ 包元数据文件
2026-07-18 20:47:17 +08:00