Files
Agent-System-Backend/internal/cardsvc/task_visibility.go
T

107 lines
3.0 KiB
Go

package cardsvc
import (
core "agent-system-core"
"crypto/sha256"
"encoding/hex"
)
// Task source authorization is conservative: same owner and same project.
// The host rechecks it on every fresh read and stream replay, without an ACL cache.
func (s *Service) taskSourceState(owner, project string, src core.TaskSourceRef) string {
art, e := s.st.GetArtifact(owner, src.ArtifactID)
if e != nil || art.ProjectID != project {
return "unavailable"
}
data, e := s.st.ArtifactContent(owner, src.ArtifactID)
if e != nil {
return "unavailable"
}
sum := sha256.Sum256(data)
if hex.EncodeToString(sum[:]) != src.ContentHash {
return "changed"
}
return "available"
}
func (s *Service) VisibleTaskCard(owner string, c core.ActivityCard) core.ActivityCard {
if c.Task == nil {
return c
}
task := *c.Task
denied := false
changed := false
if _, e := s.st.GetCard(owner, c.ID); e != nil {
denied = true
}
refs := append([]core.TaskSourceRef(nil), task.Inputs...)
for _, f := range task.Findings {
refs = append(refs, f.Sources...)
}
for _, src := range refs {
switch s.taskSourceState(owner, c.ProjectID, src) {
case "unavailable":
denied = true
case "changed":
changed = true
}
}
// Result refs must also be owned and accessible before exposing generated names.
for _, ref := range task.ResultRefs {
if ref.ArtifactID != "" {
a, e := s.st.GetArtifact(owner, ref.ArtifactID)
if e != nil || a.ProjectID != c.ProjectID {
denied = true
}
}
}
if !denied && !changed {
return c
}
task.Goal = ""
task.Focus = ""
task.Coverage = ""
task.Remaining = ""
task.NextStep = ""
task.Conclusion = ""
task.CheckpointKeys = nil
task.Limitations = nil
task.Phase.Label = ""
task.Actions = nil
task.ResultRefs = nil
if denied {
c.Title = "后台任务"
task.Title = c.Title
task.Inputs = nil
task.Findings = nil
task.Conclusion = "相关来源当前不可用,结果暂不展示"
task.Limitations = []string{"相关来源当前不可用,请核实访问权限与文件状态"}
task.Remaining = "请核实材料访问权限与文件是否仍可读取"
} else {
task.Findings = append([]core.TaskFinding(nil), task.Findings...)
for i := range task.Findings {
f := &task.Findings[i]
f.Status = "superseded"
f.Sources = append([]core.TaskSourceRef(nil), f.Sources...)
for j := range f.Sources {
f.Sources[j].Availability = s.taskSourceState(owner, c.ProjectID, f.Sources[j])
}
}
task.Conclusion = "依据文件已更新,当前结论需要重新核验"
task.Limitations = []string{"依据文件已更新,历史发现不能直接作为当前结论"}
task.Remaining = "展开历史记录可查看处理时的合法引用;当前文件与当时版本可能不同"
}
task.OutcomeCompleteness = "partial"
c.Task = &task
c.Actions = nil
c.Body = task.SummaryBody()
// The durable work result and its follow-up choice derive from the same
// sources, so they follow the task conclusion's visibility.
if c.Work != nil {
work := *c.Work
work.Result = ""
work.Decision = nil
c.Work = &work
}
return c
}