Wire EXT-1 into downstream example; bump backend/documents gitlinks
Why: EXT-1 基座侧已落子仓(backend:处理器+owner 作用域句柄最小集;documents:申请卡提案), 下游示例随公开面更新示范 rdm.ping,Dev 记录两子仓指针。 Evidence: examples/downstream-rdm 注册 rdm.ping 处理器(读工件元数据+写审计)并补 fake registry/host 测试; go.mod 按 compat-policy §4 追加 agent-system-core require+replace;cd examples/downstream-rdm && go test ./... 通过。 Impact: 仅示例与 gitlink;基座行为见 backend 提交。 Refs: modules/documents a47754b;modules/backend(feature/ext-1-inproc-handler);任务卡 .task/feature-ext-1-inproc-handler.md。
This commit is contained in:
@@ -2,6 +2,14 @@ module example.com/agent-system-plugin-rdm
|
|||||||
|
|
||||||
go 1.27
|
go 1.27
|
||||||
|
|
||||||
require agent-system-backend v0.0.0
|
require (
|
||||||
|
agent-system-backend v0.0.0
|
||||||
|
agent-system-core v0.0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 声明式路径(仅 pkg/extension 根包)只需下面两条 replace。
|
||||||
|
// 空白导入 pkg/extension/link 拉起完整后端时,须复制基座 go.mod 的
|
||||||
|
// 全部 8 条 deps replace——见 README 与 extension-compat-policy §4。
|
||||||
replace agent-system-backend => ../../modules/backend
|
replace agent-system-backend => ../../modules/backend
|
||||||
|
|
||||||
|
replace agent-system-core => ../../modules/backend/deps/agent-system-core
|
||||||
|
|||||||
@@ -1,12 +1,50 @@
|
|||||||
package rdm
|
package rdm
|
||||||
|
|
||||||
import "agent-system-backend/pkg/extension"
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"agent-system-backend/pkg/extension"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
extension.RegisterCompiled(extension.InProc{
|
extension.RegisterCompiled(extension.InProc{
|
||||||
ID: "rdm",
|
ID: "rdm",
|
||||||
Register: func(reg extension.Registry) error {
|
Register: register,
|
||||||
return reg.AddSkill("rdm-note", "# 研发记忆\n\n下游切片技能,不修改基座。\n")
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register 登记下游扩展:技能 + 进程内命令处理器(EXT-1 最小集示范)。
|
||||||
|
func register(reg extension.Registry) error {
|
||||||
|
if err := reg.AddSkill("rdm-note", "# 研发记忆\n\n下游切片技能,不修改基座。\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return reg.AddCommandHandler(json.RawMessage(`{
|
||||||
|
"id": "rdm.ping",
|
||||||
|
"title": "连通性自检",
|
||||||
|
"description": "读工件元数据 + 写审计,验证 EXT-1 owner 作用域宿主句柄",
|
||||||
|
"paramsSchema": {"type": "object", "properties": {"artifactId": {"type": "string"}}}
|
||||||
|
}`), rdmPing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rdmPing 经 owner 作用域宿主句柄:可选读工件元数据 → 写审计 → 返回回执。
|
||||||
|
// 处理器自身不持有任何 owner 凭证,一切能力经 h 取用。
|
||||||
|
func rdmPing(ctx context.Context, h extension.Host, req extension.Request) (any, error) {
|
||||||
|
artifact := ""
|
||||||
|
if id, _ := req.Params["artifactId"].(string); id != "" {
|
||||||
|
a, err := h.ArtifactGet(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
artifact = a.Name
|
||||||
|
}
|
||||||
|
if err := h.Audit("rdm.ping", req.ID, "{}"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return map[string]any{
|
||||||
|
"pong": true,
|
||||||
|
"owner": h.Owner(),
|
||||||
|
"plugin": h.PluginID(),
|
||||||
|
"artifact": artifact,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package rdm
|
package rdm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"agent-system-backend/pkg/extension"
|
"agent-system-backend/pkg/extension"
|
||||||
|
core "agent-system-core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompiledSkillRegistration(t *testing.T) {
|
func TestCompiledSkillRegistration(t *testing.T) {
|
||||||
@@ -17,3 +21,83 @@ func TestCompiledSkillRegistration(t *testing.T) {
|
|||||||
t.Fatal("surface version required")
|
t.Fatal("surface version required")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fakeRegistry 记录 register() 的登记动作,不拉起基座。
|
||||||
|
type fakeRegistry struct {
|
||||||
|
skills []string
|
||||||
|
commands []string
|
||||||
|
handlers int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeRegistry) AddSkill(name, markdown string) error {
|
||||||
|
f.skills = append(f.skills, name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (f *fakeRegistry) AddCommand(spec json.RawMessage) error { return nil }
|
||||||
|
func (f *fakeRegistry) AddCommandHandler(spec json.RawMessage, h extension.Handler) error {
|
||||||
|
var s struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(spec, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if h == nil {
|
||||||
|
return errors.New("nil handler")
|
||||||
|
}
|
||||||
|
f.commands = append(f.commands, s.ID)
|
||||||
|
f.handlers++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (f *fakeRegistry) Note(kind, name string, payload json.RawMessage) error { return nil }
|
||||||
|
|
||||||
|
func TestRegisterBindsPingHandler(t *testing.T) {
|
||||||
|
reg := &fakeRegistry{}
|
||||||
|
if err := register(reg); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(reg.skills) != 1 || reg.skills[0] != "rdm-note" {
|
||||||
|
t.Fatalf("skills: %+v", reg.skills)
|
||||||
|
}
|
||||||
|
if len(reg.commands) != 1 || reg.commands[0] != "rdm.ping" || reg.handlers != 1 {
|
||||||
|
t.Fatalf("commands: %+v handlers=%d", reg.commands, reg.handlers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fakeHost 是下游测试里的最小句柄替身:只实现 rdm.ping 用到的能力。
|
||||||
|
type fakeHost struct{ owner string }
|
||||||
|
|
||||||
|
func (f fakeHost) PluginID() string { return "rdm" }
|
||||||
|
func (f fakeHost) Owner() string { return f.owner }
|
||||||
|
func (f fakeHost) KVPut(key, value string) error { return nil }
|
||||||
|
func (f fakeHost) KVGet(key string) (string, bool, error) { return "", false, nil }
|
||||||
|
func (f fakeHost) Audit(action, target, evidence string) error { return nil }
|
||||||
|
func (f fakeHost) Complete(ctx context.Context, prompt string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
func (f fakeHost) KnowledgeSearch(ctx context.Context, projectID core.ID, text string, topK int) ([]core.RetrievalHit, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (f fakeHost) KnowledgeWrite(ctx context.Context, docs []core.KnowledgeDoc, embed bool) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (f fakeHost) TaskCreate(kind, title, conversationID string) (string, error) {
|
||||||
|
return "task_1", nil
|
||||||
|
}
|
||||||
|
func (f fakeHost) TaskUpdate(taskID, status string, progress int, errText string) error { return nil }
|
||||||
|
func (f fakeHost) ArtifactGet(artifactID string) (core.Artifact, error) {
|
||||||
|
return core.Artifact{}, errors.New("not found")
|
||||||
|
}
|
||||||
|
func (f fakeHost) ArtifactContent(artifactID string) ([]byte, error) { return nil, nil }
|
||||||
|
|
||||||
|
func TestRdmPingHandler(t *testing.T) {
|
||||||
|
out, err := rdmPing(context.Background(), fakeHost{owner: "alice"}, extension.Request{
|
||||||
|
ID: "rdm.ping", Owner: "alice", Params: map[string]any{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m, _ := out.(map[string]any)
|
||||||
|
if m["pong"] != true || m["owner"] != "alice" || m["plugin"] != "rdm" {
|
||||||
|
t.Fatalf("payload: %+v", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
Submodule modules/backend updated: 3f438b9e80...cb26e4c947
+1
-1
Submodule modules/documents updated: f3f823ef8d...a47754b494
Reference in New Issue
Block a user