feat: add plgreload tool for atomic plugin reload

- Agent can now call plgreload to reload plugins/ directory
- Atomic swap: new devices Start → routes swapped → old devices Stop
- Output channel capability validation on output_send
- output_list_channels tool for channel discovery
- OneBot V11 protocol implementation (Reverse WebSocket)
- Plugin = container with embedded IO channel (composition)
- Unified ToolDef type (agentIO.ToolDef = plugin.ToolDef)
This commit is contained in:
root
2026-07-02 12:26:41 +08:00
parent e450edf865
commit 3d7d24ebb8
3 changed files with 34 additions and 9 deletions

View File

@ -251,6 +251,8 @@ func main() {
DocStore: docStore,
Knowledge: ks,
Personality: personality,
PluginReg: pluginReg,
PluginDir: filepath.Join(cfg.Daemon.DataDir, "plugins"),
})
agent.Start()
defer agent.Stop()
@ -292,12 +294,4 @@ func main() {
log.Printf("[homed] stopped")
}
func countIOPlugins(r *plugin.Registry) int {
n := 0
for _, p := range r.List() {
if p.IOConfig() != nil {
n++
}
}
return n
}

BIN
homed

Binary file not shown.

View File

@ -49,6 +49,7 @@ type Agent struct {
// 插件注册表(用于 plgreload
pluginReg *plugin.Registry
pluginDir string
// 定期心跳蒸馏
distillInterval time.Duration
@ -108,6 +109,7 @@ func New(cfg AgentConfig) *Agent {
knowledge: cfg.Knowledge,
personality: cfg.Personality,
pluginReg: cfg.PluginReg,
pluginDir: cfg.PluginDir,
distillInterval: cfg.DistillInterval,
maxContextSize: cfg.MaxContextSize,
}
@ -318,6 +320,8 @@ func (a *Agent) executeToolCall(tc agentAPI.ToolCall) string {
return a.executeOutputSendTool(tc)
case tc.Name == "output_list_channels":
return a.executeOutputListChannels()
case tc.Name == "plgreload":
return a.executePluginReload()
}
if a.tracker != nil {
@ -690,6 +694,21 @@ func (a *Agent) buildToolDefs() []interface{} {
})
}
// 插件重载工具
if a.pluginReg != nil && a.pluginDir != "" {
tools = append(tools, map[string]interface{}{
"type": "function",
"function": map[string]interface{}{
"name": "plgreload",
"description": "重载 plugins/ 目录的所有插件。扫描目录变更,原子化替换 IO 设备。",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{},
},
},
})
}
// 输出通道工具
tools = append(tools, map[string]interface{}{
"type": "function",
@ -1045,6 +1064,18 @@ func getString(m map[string]interface{}, key string) string {
return ""
}
// executePluginReload — 重载所有插件(原子替换 IO 设备)
func (a *Agent) executePluginReload() string {
if a.pluginReg == nil {
return "插件系统未启用"
}
msg, err := a.pluginReg.Reload(a.pluginDir)
if err != nil {
return fmt.Sprintf("插件重载失败: %v", err)
}
return msg
}
func getFloat(m map[string]interface{}, key string) float64 {
if v, ok := m[key]; ok {
switch n := v.(type) {