Pin the extension roadmap and downstream spike #132
@@ -0,0 +1,27 @@
|
||||
# feature/base-extension-roadmap 任务卡
|
||||
|
||||
任务主记录:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/132 ;状态:进行中;owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= f8fefa0f888e8f5fd5207c9e97099e59935ff69b
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-base-extension-roadmap(worktree);分支:feature/base-extension-roadmap
|
||||
- 授权与交付终点:在本 worktree 落地路线图提案与基座实现,并在各子仓与父仓本地提交。不推送、不打版本 tag、不新建下游远程仓。
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:公开扩展面(`pkg/extension`)、插件契约 v2 四种形态、生命周期治理、质量回路与工程基座接线;下游薄切片只 import 公开 SDK。
|
||||
- 非目标:打 `v0.2.0-experimental`、发布 npm、改 Go module path、把目标系统(两树一网)做进基座、容器编排、旧资产导入。
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | f8fefa0f888e8f5fd5207c9e97099e59935ff69b | f8fefa0f888e8f5fd5207c9e97099e59935ff69b | 2026-10-01T10:10:35Z |
|
||||
| 同步前验收 | 26dfea7ed62575097f14fedf1b7713a77dedb66f | 6d496139b45bbe0d32818e56ed6c5fbba649d777 | 2026-10-01T10:39:24Z |
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:documents 三份提案与开发指南;core manifest v2;backend 声明/sidecar/进程内/Host API/迁移 v13/质量回路/审计与命令对拍;ui SDK 与 frontend ESM 加载;examples 与 `make plugin-new`、`make test-boundaries`。
|
||||
- 验证:`cd modules/core && go test ./...`;`cd modules/backend && go test ./...`;`bash scripts/lint-boundaries.sh`;`cd examples/downstream-rdm && go test .`;UI `tsc`+vitest 71;frontend vitest。
|
||||
- 影响与剩余项:apiVersion `2.0-experimental`。版本 tag 与 npm 发布命令留在兼容提案,由用户确认后执行。下游远程仓由用户另建。Playwright 对拍文件在 `modules/frontend/e2e/`,未安装 `@playwright/test`,不进 vitest。合入顺序:Core #14、Documents #31、UI #25、Frontend #31,然后 Backend #41,最后 Dev #132。子仓不要 squash,否则 Backend / Dev 的 gitlink 会指向不存在的提交。
|
||||
@@ -96,7 +96,14 @@ test-ui: contracts
|
||||
test-agentcmd:
|
||||
cd deps/agent-command && go vet ./... && go test -count=1 ./...
|
||||
|
||||
test: test-core test-backend test-frontend test-ui test-agentcmd
|
||||
test-boundaries:
|
||||
bash scripts/lint-boundaries.sh
|
||||
|
||||
plugin-new:
|
||||
@test -n "$(NAME)" || (echo "用法:make plugin-new NAME=<id>"; exit 2)
|
||||
bash scripts/plugin-new.sh "$(NAME)"
|
||||
|
||||
test: test-boundaries test-core test-backend test-frontend test-ui test-agentcmd
|
||||
|
||||
# 运行演示(mock 模式):单二进制 + 示例插件 + 嵌入式存储,浏览器 http://127.0.0.1:8787
|
||||
# 环境服务管理(scripts/svc.sh):deploy = 停旧 → 构建 → 启动;dev/prod 相互隔离。
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# 下游薄切片
|
||||
|
||||
本目录模拟独立仓库 `Agent-System-Plugin-RDM`。它只 import `agent-system-backend/pkg/extension`,不改基座。
|
||||
|
||||
把本目录搬到新仓库时,由 owner 创建 Git 仓库。`replace` 改成基座检出路径或已发布版本。需要拉起完整二进制时再空白导入 `agent-system-backend/pkg/extension/link`,并复制基座 `go.mod` 的 replace(下游模块不会继承它们)。
|
||||
|
||||
```
|
||||
go test .
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
module example.com/agent-system-plugin-rdm
|
||||
|
||||
go 1.27
|
||||
|
||||
require agent-system-backend v0.0.0
|
||||
|
||||
replace agent-system-backend => ../../modules/backend
|
||||
@@ -0,0 +1,12 @@
|
||||
package rdm
|
||||
|
||||
import "agent-system-backend/pkg/extension"
|
||||
|
||||
func init() {
|
||||
extension.RegisterCompiled(extension.InProc{
|
||||
ID: "rdm",
|
||||
Register: func(reg extension.Registry) error {
|
||||
return reg.AddSkill("rdm-note", "# 研发记忆\n\n下游切片技能,不修改基座。\n")
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package rdm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"agent-system-backend/pkg/extension"
|
||||
)
|
||||
|
||||
func TestCompiledSkillRegistration(t *testing.T) {
|
||||
extension.ResetCompiled()
|
||||
extension.RegisterCompiled(extension.InProc{ID: "rdm"})
|
||||
got := extension.Compiled()
|
||||
if len(got) != 1 || got[0].ID != "rdm" {
|
||||
t.Fatalf("%+v", got)
|
||||
}
|
||||
if extension.SurfaceVersion == "" {
|
||||
t.Fatal("surface version required")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export function mount(el, bridge) {
|
||||
const btn = document.createElement('button')
|
||||
btn.type = 'button'
|
||||
btn.textContent = '走命令'
|
||||
btn.addEventListener('click', () => bridge.execCommand('builtin.help', {}))
|
||||
el.replaceChildren(btn)
|
||||
}
|
||||
export function unmount() {}
|
||||
@@ -0,0 +1 @@
|
||||
{ "id": "feishu", "kind": "feishu", "title": "飞书" }
|
||||
@@ -0,0 +1 @@
|
||||
{ "id": "notes", "title": "笔记", "description": "数据集声明,检索引擎在基座" }
|
||||
@@ -0,0 +1,5 @@
|
||||
export function mount(el, bridge) {
|
||||
el.textContent = '声明示例页面'
|
||||
el.dataset.bridge = bridge ? 'ready' : ''
|
||||
}
|
||||
export function unmount() {}
|
||||
@@ -0,0 +1 @@
|
||||
{ "id": "home", "title": "声明示例", "module": "./page.mjs" }
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "decl-demo",
|
||||
"name": "声明示例",
|
||||
"version": "0.1.0",
|
||||
"apiVersion": "2.0-experimental",
|
||||
"requires": { "network": ["api.example.com"] },
|
||||
"provides": {
|
||||
"skills": ["welcome"],
|
||||
"pages": ["home"],
|
||||
"channels": ["feishu"],
|
||||
"knowledge": ["notes"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# welcome
|
||||
|
||||
声明式技能示例。描述任务步骤,不携带可执行代码。
|
||||
@@ -0,0 +1,17 @@
|
||||
// sidecar 示例:只通过 Host API 客户端说话,不导入 internal。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"agent-system-backend/pkg/extension"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := extension.NewHostClient(os.Getenv("AGENT_HOST_API"), os.Getenv("AGENT_HOST_TOKEN"), os.Getenv("AGENT_PLUGIN_ID"), "local")
|
||||
if err := c.Audit(context.Background(), "sidecar.ready", os.Getenv("AGENT_PLUGIN_ID"), []byte(`{}`)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
+1
-1
Submodule modules/backend updated: ba2aa909e8...88f8a476f0
+1
-1
Submodule modules/core updated: d4375300cd...fdc0c1b933
+1
-1
Submodule modules/documents updated: 8ee5d5c467...a0da588f90
+1
-1
Submodule modules/frontend updated: 0ed8ad32f1...22f7d84560
+1
-1
Submodule modules/ui updated: 8bfe8a89db...edc1f3b982
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# 依赖边界:公开扩展 SDK 与示例不得导入后端 internal。
|
||||
# pkg/extension/link 是唯一例外:下游空白导入它才能把 extension.Main 接到产品组合根。
|
||||
set -euo pipefail
|
||||
root=$(cd "$(dirname "$0")/.." && pwd)
|
||||
fail=0
|
||||
|
||||
check() {
|
||||
local path="$1"
|
||||
if [ -d "$path" ] && grep -R -n --include='*.go' --include='*.ts' --include='*.tsx' 'agent-system-backend/internal' "$path" >/dev/null; then
|
||||
echo "boundary: $path imports agent-system-backend/internal" >&2
|
||||
grep -R -n --include='*.go' --include='*.ts' --include='*.tsx' 'agent-system-backend/internal' "$path" >&2
|
||||
fail=1
|
||||
fi
|
||||
}
|
||||
|
||||
sdk="$root/modules/backend/pkg/extension"
|
||||
if [ -d "$sdk" ]; then
|
||||
hits=$(grep -R -n --include='*.go' 'agent-system-backend/internal' "$sdk" | grep -v '/link/' || true)
|
||||
if [ -n "$hits" ]; then
|
||||
echo "boundary: pkg/extension imports agent-system-backend/internal" >&2
|
||||
echo "$hits" >&2
|
||||
fail=1
|
||||
fi
|
||||
fi
|
||||
check "$root/examples"
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "boundaries ok"
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# 生成声明式插件骨架:make plugin-new NAME=<id>
|
||||
set -euo pipefail
|
||||
name="${1:-}"
|
||||
if [ -z "$name" ]; then
|
||||
echo "usage: plugin-new.sh <id>" >&2
|
||||
exit 2
|
||||
fi
|
||||
root=$(cd "$(dirname "$0")/.." && pwd)
|
||||
dest="$root/examples/plugins/$name"
|
||||
if [ -e "$dest" ]; then
|
||||
echo "exists: $dest" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$dest/tools" "$dest/skills" "$dest/pages"
|
||||
cat > "$dest/plugin.json" <<EOF
|
||||
{
|
||||
"id": "$name",
|
||||
"name": "$name",
|
||||
"version": "0.1.0",
|
||||
"apiVersion": "2.0-experimental",
|
||||
"requires": { "network": [] },
|
||||
"provides": { "skills": ["welcome"], "pages": ["home"] }
|
||||
}
|
||||
EOF
|
||||
printf '# %s\n\n描述这个技能做什么、何时使用。\n' "$name" > "$dest/skills/welcome.md"
|
||||
cat > "$dest/pages/home.json" <<EOF
|
||||
{ "id": "home", "title": "$name", "module": "./page.mjs" }
|
||||
EOF
|
||||
cat > "$dest/page.mjs" <<'EOF'
|
||||
export function mount(el, bridge) {
|
||||
el.textContent = '插件页面'
|
||||
el.dataset.bridge = bridge ? 'ready' : ''
|
||||
}
|
||||
export function unmount() {}
|
||||
EOF
|
||||
echo "wrote $dest"
|
||||
Reference in New Issue
Block a user