Plexum-sdk-go now unpacks `_meta.agent_id` from host's MCP tools/call
frames into the ctx. Plugin reads it via plugin.AgentIDFromContext
and uses it for backend api-key resolution (agentKeys map lookup +
bearer token) and HF on_call pre-check.
config.defaultAgentID demoted from "v1 stop-gap for the missing SDK
plumbing" to "fallback for operator-driven plugin-call". Normal turn
loops carry the real caller id through ctx now; defaultAgentID only
fires on host-driven dispatch paths.
Also: log every CallTool dispatch with {tool, agent_id} at info level
so operators can see which agent is hitting which tool without
debug-level chatter.
E2E in sim: PLEXUM_ECHO_FORCE_TOOL_USE drives echo provider →
dialectic_list_topics dispatched via host → plugin sees
agent_id=test-agent in ctx + logs it correctly.
106 lines
2.8 KiB
Go
106 lines
2.8 KiB
Go
// plexum-dialectic-plugin — Plexum-side Dialectic plugin.
|
|
//
|
|
// Ports Dialectic.OpenclawPlugin to the Plexum SDK: 8 dialectic_* tools
|
|
// over HTTP against Dialectic.Backend. Lazy activation (no background
|
|
// services — purely tool-call driven).
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
sdkplugin "git.hangman-lab.top/hzhang/Plexum-sdk-go/plugin"
|
|
|
|
dialcfg "git.hangman-lab.top/zhi/Dialectic.PlexumPlugin/internal/config"
|
|
"git.hangman-lab.top/zhi/Dialectic.PlexumPlugin/internal/tools"
|
|
)
|
|
|
|
var Version = "0.1.0"
|
|
|
|
type dialecticPlugin struct {
|
|
host sdkplugin.HostAPI
|
|
cfg dialcfg.Resolved
|
|
deps tools.Deps
|
|
}
|
|
|
|
func (p *dialecticPlugin) Manifest() sdkplugin.Manifest {
|
|
return manifestFromDisk()
|
|
}
|
|
|
|
func (p *dialecticPlugin) Init(ctx context.Context, host sdkplugin.HostAPI) error {
|
|
p.host = host
|
|
|
|
profileRoot := os.Getenv("PLEXUM_PROFILE_ROOT")
|
|
if profileRoot == "" {
|
|
home, _ := os.UserHomeDir()
|
|
profileRoot = filepath.Join(home, ".plexum")
|
|
}
|
|
raw, err := dialcfg.Load(profileRoot)
|
|
if err != nil {
|
|
return fmt.Errorf("load dialectic config: %w", err)
|
|
}
|
|
p.cfg = dialcfg.Resolve(raw)
|
|
host.Log("info", "dialectic plugin initialized", map[string]any{
|
|
"version": Version,
|
|
"backend": p.cfg.BackendURL,
|
|
"default_agent_id": p.cfg.DefaultAgentID,
|
|
"agent_keys_count": len(p.cfg.AgentKeys),
|
|
"has_default_key": p.cfg.APIKey != "",
|
|
})
|
|
|
|
p.deps = tools.Deps{
|
|
Config: p.cfg,
|
|
Host: host,
|
|
AgentIDFromCtx: func(ctx context.Context) string {
|
|
// Host attaches the caller agent id via tools/call
|
|
// `_meta.agent_id`; SDK unpacks it into ctx.
|
|
if id := sdkplugin.AgentIDFromContext(ctx); id != "" {
|
|
return id
|
|
}
|
|
// Fallback for host paths that don't carry an agent
|
|
// (CLI plugin-call against this plugin from an operator
|
|
// debugging the deployment). Empty when not set.
|
|
return p.cfg.DefaultAgentID
|
|
},
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *dialecticPlugin) CallTool(ctx context.Context, name string, input json.RawMessage) (sdkplugin.ToolResult, error) {
|
|
p.host.Log("info", "tools/call dispatched", map[string]any{
|
|
"tool": name,
|
|
"agent_id": sdkplugin.AgentIDFromContext(ctx),
|
|
})
|
|
return tools.Dispatch(ctx, p.deps, name, input)
|
|
}
|
|
|
|
func manifestFromDisk() sdkplugin.Manifest {
|
|
exe, err := os.Executable()
|
|
if err == nil {
|
|
raw, err := os.ReadFile(filepath.Join(filepath.Dir(exe), "manifest.json"))
|
|
if err == nil {
|
|
var m sdkplugin.Manifest
|
|
if err := json.Unmarshal(raw, &m); err == nil && m.Name != "" {
|
|
return m
|
|
}
|
|
}
|
|
}
|
|
return sdkplugin.Manifest{
|
|
Name: "dialectic",
|
|
Version: Version,
|
|
Activation: sdkplugin.ActivationLazy,
|
|
Executable: "plexum-dialectic-plugin",
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
if err := sdkplugin.Serve(&dialecticPlugin{}); err != nil && !errors.Is(err, context.Canceled) {
|
|
fmt.Fprintf(os.Stderr, "plexum-dialectic-plugin: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|