feat(skills): 技能加载/校验/真执行第一片(design/09 §6 定稿落地)
- 加载器/校验:目录式 configs/skills/<id>/skill.yaml(兼容旧单文件 <id>.yaml,先目录后文件); 新字段 version/when-to-use/invocation(三态)/triggers/paths/allowed-tools/disallowed-tools/ context(ine|fork)/agent/model/effort/metadata;**依赖环 ⇒ 加载期拒**(运行期绝不挂死); depends 指向不存在步骤 ⇒ 拒;同一工具同时在 allowed 与 disallowed ⇒ 拒;错误可分类(哨兵 + %w) - Runner.Run 真执行(替换 ErrRunPending):步骤拓扑(无依赖可并行、上限 2)+ guardrail 强制点 (enforced 违反 ⇒ 中止并如实报「哪条、哪一步」;否则 WARN 继续)+ 失败不静默跳过 + ctx 取消传播 - 🔴 能力收缩只收不放:该轮可用工具 = allowed-tools ∩ 安全闸 ∩ 注册表,再减 disallowed-tools - 另有渐进披露相关的 listing/surface/activation(清单投影与激活判定) ⚠️ 未接线:本片是库与执行器,尚未把技能清单接进模型面(见 report 的分阶段)。
This commit is contained in:
1 parent
29181245ce
commit
f75e7a9c9b
17 files changed
+3067
-164
No files matched your search
@@ -150,9 +150,77 @@ func TestStore_Upsert_Invalid_异常(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestStore_SQLInjection_安全 stores a name/description that looks like SQL and
|
||||
// verifies the values round-trip verbatim (parameterized statements), that the
|
||||
// table survives, and that unrelated rows are unaffected.
|
||||
// TestStore_Spec往返_正常 断言 design/09 §6.2 的新增字段真的落盘并能原样读回 ——
|
||||
// 尤其是 allowed-tools:读回来变成「不限」就等于把能力收缩放宽,那是安全红线,
|
||||
// 所以这里逐字段钉死。
|
||||
func TestStore_Spec往返_正常(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newTestStore(t)
|
||||
ctx := context.Background()
|
||||
skill, err := New("guarded", "d", "p", nil, nil, nil, WithSpec(Spec{
|
||||
Version: 2,
|
||||
WhenToUse: "何时用",
|
||||
Invocation: sdk.InvocationUserOnly,
|
||||
Triggers: []string{"简报"},
|
||||
Paths: []string{"src/**/*.go"},
|
||||
AllowedTools: []string{"read"},
|
||||
DisallowedTools: []string{"write"},
|
||||
Context: sdk.ContextFork,
|
||||
Agent: "researcher",
|
||||
Model: "fast",
|
||||
Effort: "high",
|
||||
Metadata: map[string]string{"owner": "yeiji"},
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("New: %v", err)
|
||||
}
|
||||
if err := s.Upsert(ctx, skill); err != nil {
|
||||
t.Fatalf("Upsert: %v", err)
|
||||
}
|
||||
got, err := s.Get(ctx, "guarded")
|
||||
if err != nil {
|
||||
t.Fatalf("Get: %v", err)
|
||||
}
|
||||
if got.Version() != 2 || got.WhenToUse() != "何时用" || got.Invocation() != sdk.InvocationUserOnly {
|
||||
t.Fatalf("往返后 Version/WhenToUse/Invocation = %d/%q/%q", got.Version(), got.WhenToUse(), got.Invocation())
|
||||
}
|
||||
if len(got.AllowedTools()) != 1 || got.AllowedTools()[0] != "read" {
|
||||
t.Fatalf("AllowedTools = %v, want [read](丢了它等于放宽能力)", got.AllowedTools())
|
||||
}
|
||||
if len(got.DisallowedTools()) != 1 || got.DisallowedTools()[0] != "write" {
|
||||
t.Fatalf("DisallowedTools = %v, want [write]", got.DisallowedTools())
|
||||
}
|
||||
if got.ContextMode() != sdk.ContextFork || got.Agent() != "researcher" {
|
||||
t.Fatalf("Context/Agent = %q/%q, want fork/researcher", got.ContextMode(), got.Agent())
|
||||
}
|
||||
if got.Model() != "fast" || got.Effort() != "high" {
|
||||
t.Fatalf("Model/Effort = %q/%q, want fast/high", got.Model(), got.Effort())
|
||||
}
|
||||
if len(got.Triggers()) != 1 || len(got.Paths()) != 1 || got.Metadata()["owner"] != "yeiji" {
|
||||
t.Fatalf("Triggers/Paths/Metadata 往返失败:%v/%v/%v", got.Triggers(), got.Paths(), got.Metadata())
|
||||
}
|
||||
}
|
||||
|
||||
// TestStore_基础Skill往返_缺省 断言只实现基础 sdk.Skill 的技能也能落盘,读回来是
|
||||
// 文档规定的缺省值(而不是零值)。
|
||||
func TestStore_基础Skill往返_缺省(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newTestStore(t)
|
||||
ctx := context.Background()
|
||||
if err := s.Upsert(ctx, mockSkill{name: "plain"}); err != nil {
|
||||
t.Fatalf("Upsert: %v", err)
|
||||
}
|
||||
got, err := s.Get(ctx, "plain")
|
||||
if err != nil {
|
||||
t.Fatalf("Get: %v", err)
|
||||
}
|
||||
if got.Version() != sdk.DefaultSkillVersion || got.Invocation() != sdk.InvocationBoth || got.ContextMode() != sdk.ContextInline {
|
||||
t.Fatalf("缺省值 = %d/%q/%q, want 1/both/inline", got.Version(), got.Invocation(), got.ContextMode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestStore_SQLInjection_安全 存入形似 SQL 的名字/描述,验证值被原样往返(参数化
|
||||
// 语句)、表仍然完好、且不波及其它行。
|
||||
func TestStore_SQLInjection_安全(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newTestStore(t)
|
||||
|
||||
Reference in new issue
Block a user