- 插件/MCP 改为通用工具仓库:内置、HTTP MCP、本机命令(stdio)三类工具,可增删改、 按 Agent 分配;定义变更自动提升下发版本并重发,删除即回收分配并通知 Agent 清空。 - 旧 desktop-filesystem 降级为仓库中的一个内置条目;本机预设工具改为总控只展示、 Agent 默认启用,任务执行来源新增 builtin(审计 task.builtin_tool_execution)。 - Skills 补齐录入闭环:新建或按名称覆盖更新,并可从旧版技能目录(SKILL.md + skill.json) 批量导入;同名更新保持标识不变并自动重新应用,旧包入口脚本只提示不执行。 - 单独指派支持基于某条已完成任务的多轮追问:任务内容框仍下发无上下文的一次性任务, 追问时由总控组装历史(仅成功轮次、默认最近 50 轮 / 约 6 万字符),会话按 Agent 隔离; 执行动态把同一会话合并为一条记录,任务详情弹窗底部提供继续对话输入框。 - 任务详情支持运行中观测:打开期间每 2 秒刷新,终端风格输出全部事件并自动滚动; progress 事件不改变任务状态且顺延无进展超时,避免正在执行工具的任务被误判超时。 - 修复 build.ps1 缺 UTF-8 BOM 导致 build.bat 在 PowerShell 5.1 下语法报错。
72 lines
2.4 KiB
Go
72 lines
2.4 KiB
Go
package controlplane
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSkillDocumentProvidesReadOnlyMarkdownWithSensitiveLinesRedacted(t *testing.T) {
|
|
t.Setenv("MULTICLAW_CONTROLLER_DATA_DIR", t.TempDir())
|
|
controller, err := Open(filepath.Join(t.TempDir(), "controller-config.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer controller.Close()
|
|
skill, err := controller.SaveSkill(SkillInput{
|
|
Name: "只读预览测试",
|
|
Version: "1.0.0",
|
|
Content: "# Skill\n\npassword: demo-password\napi_key=demo-key\n普通说明\n<think>不应显示</think>",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
document, err := controller.SkillDocument(skill.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if document.ID != skill.ID || document.Name != skill.Name || !strings.Contains(document.Content, "普通说明") {
|
|
t.Fatalf("unexpected Skill document: %#v", document)
|
|
}
|
|
for _, forbidden := range []string{"demo-password", "demo-key", "不应显示"} {
|
|
if strings.Contains(document.Content, forbidden) {
|
|
t.Fatalf("Skill preview leaked sensitive or hidden content: %q", forbidden)
|
|
}
|
|
}
|
|
if strings.Count(document.Content, "[已脱敏]") != 2 {
|
|
t.Fatalf("expected both sensitive values to be redacted: %q", document.Content)
|
|
}
|
|
if _, err := controller.SkillDocument("not-a-skill-id"); err == nil {
|
|
t.Fatal("invalid Skill ID must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestSaveSkillKeepsItsIdentityWhenAnUpdatedVersionIsImported(t *testing.T) {
|
|
t.Setenv("MULTICLAW_CONTROLLER_DATA_DIR", t.TempDir())
|
|
controller, err := Open(filepath.Join(t.TempDir(), "controller-config.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer controller.Close()
|
|
first, err := controller.SaveSkill(SkillInput{Name: "Docker Essentials", Version: "1.0.0", Content: "# Docker\n第一版"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := controller.SaveSkill(SkillInput{Name: "Docker Essentials", Version: "1.1.0", Content: "# Docker\n第二版"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if second.ID != first.ID || second.Version != "1.1.0" || second.Checksum == first.Checksum {
|
|
t.Fatalf("expected an in-place update, got %#v then %#v", first, second)
|
|
}
|
|
skills, err := controller.Skills()
|
|
if err != nil || len(skills) != 1 {
|
|
t.Fatalf("a renamed import must not create a duplicate package: %#v / %v", skills, err)
|
|
}
|
|
document, err := controller.SkillDocument(second.ID)
|
|
if err != nil || !strings.Contains(document.Content, "第二版") {
|
|
t.Fatalf("expected the updated Markdown, got %#v / %v", document, err)
|
|
}
|
|
}
|