feat(canvas): 云端 Agent - 完成策略分层、节点能力注册与可解释审批改造

This commit is contained in:
ddCat
2026-09-14 17:58:21 +08:00
parent 4b0e9d8965
commit 01486872c4
65 changed files with 5347 additions and 918 deletions

View File

@@ -141,6 +141,114 @@ func TestCloudAgentLegacyRunSurvivesTaskInputCompaction(t *testing.T) {
}
}
func TestCloudAgentTerminalRunWithOlderCapabilityCanContinueOnCurrentContract(t *testing.T) {
s, db, _, _ := creationTestService(t)
if err := db.Create(&model.CanvasProject{ID: "agent-canvas", UserID: "user", PayloadJSON: `{"nodes":[]}`}).Error; err != nil {
t.Fatal(err)
}
parentRequest := agentTestRequest()
parentRequest.IdempotencyKey = "older-capability-parent"
parent, err := s.CreateCloudAgentRun("user", parentRequest, "")
if err != nil {
t.Fatal(err)
}
parentExecution, err := s.repo.CloudAgent("user", parent.ID)
if err != nil {
t.Fatal(err)
}
parentState, err := cloudAgentDecode(parentExecution)
if err != nil {
t.Fatal(err)
}
parentState.Policy.CapabilitySetVersion = "canvas-capabilities/v1"
parentState.Policy.CapabilitySetHash = agentProfileHash("historical capability contract")
if err = cloudAgentSave(parentExecution, &parentState); err != nil {
t.Fatal(err)
}
if err = db.Model(&model.CloudAgentExecution{}).Where("id = ?", parent.ID).Updates(map[string]any{
"status": "completed", "state_json": parentExecution.StateJSON,
}).Error; err != nil {
t.Fatal(err)
}
if err = db.Model(&model.Task{}).Where("id = ?", parent.ID).Updates(map[string]any{
"status": model.TaskStatusSucceeded, "result_json": `{"text":"旧合同下的可信回复"}`,
}).Error; err != nil {
t.Fatal(err)
}
historicalStateJSON := parentExecution.StateJSON
childRequest := agentTestRequest()
childRequest.Prompt = "继续操作"
childRequest.IdempotencyKey = "current-capability-child"
child, err := s.CreateCloudAgentRun("user", childRequest, parent.ID)
if err != nil {
t.Fatalf("terminal historical run cannot continue: %v", err)
}
childExecution, err := s.repo.CloudAgent("user", child.ID)
if err != nil {
t.Fatal(err)
}
childState, err := cloudAgentDecode(childExecution)
if err != nil {
t.Fatal(err)
}
if err = validateCloudAgentPolicySnapshot(childState.Policy); err != nil {
t.Fatalf("child did not use the current execution contract: %v", err)
}
if childState.Policy.CapabilitySetVersion != cloudAgentCapabilitySetVersion || childState.Policy.CapabilitySetHash != cloudAgentCapabilitySetHash() {
t.Fatalf("child capability contract is stale: %+v", childState.Policy)
}
if childState.ParentID != parent.ID || len(childState.TextHistory) != 2 || childState.TextHistory[1].Content != "旧合同下的可信回复" {
t.Fatalf("historical conversation context was not preserved: %+v", childState)
}
var storedParent model.CloudAgentExecution
if err = db.First(&storedParent, "id = ?", parent.ID).Error; err != nil {
t.Fatal(err)
}
if storedParent.StateJSON != historicalStateJSON {
t.Fatal("continuation mutated the frozen parent runtime")
}
}
func TestCloudAgentActiveRunWithOlderCapabilityIsTerminatedWithoutResume(t *testing.T) {
s, db, _, _ := creationTestService(t)
if err := db.Create(&model.CanvasProject{ID: "agent-canvas", UserID: "user", PayloadJSON: `{"nodes":[]}`}).Error; err != nil {
t.Fatal(err)
}
req := agentTestRequest()
req.IdempotencyKey = "older-active-contract"
run, err := s.CreateCloudAgentRun("user", req, "")
if err != nil {
t.Fatal(err)
}
execution, err := s.repo.CloudAgent("user", run.ID)
if err != nil {
t.Fatal(err)
}
state, err := cloudAgentDecode(execution)
if err != nil {
t.Fatal(err)
}
state.Policy.CapabilitySetVersion = "canvas-capabilities/v1"
state.Policy.CapabilitySetHash = agentProfileHash("historical capability contract")
if err = cloudAgentSave(execution, &state); err != nil {
t.Fatal(err)
}
if err = db.Model(&model.CloudAgentExecution{}).Where("id = ?", run.ID).Update("state_json", execution.StateJSON).Error; err != nil {
t.Fatal(err)
}
if err = s.advanceCloudAgentByID("user", run.ID); err != nil {
t.Fatal(err)
}
execution, err = s.repo.CloudAgent("user", run.ID)
if err != nil {
t.Fatal(err)
}
if execution.Status != "failed" || !strings.Contains(execution.FailureMessage, "旧版执行合同") {
t.Fatalf("stale active contract was resumed or failed opaquely: status=%s message=%q", execution.Status, execution.FailureMessage)
}
}
func TestCloudAgentValidation(t *testing.T) {
for _, mutate := range []func(*CloudAgentRequest){
func(r *CloudAgentRequest) { r.PermissionMode = "unrestricted" },
@@ -346,7 +454,7 @@ func TestCloudAgentToolLoopPersistsApprovalAndAppliesCanvasWrite(t *testing.T) {
t.Fatal(err)
}
req := agentTestRequest()
req.Thinking = true
req.ReasoningMode = "deep"
req.PermissionMode = "request_approval"
root, err := s.CreateCloudAgentRun("user", req, "")
if err != nil {