返回 DeepSeek-Reasonix
fingerprint.go
根目录 / internal / recovery / fingerprint.go
1 package recovery
2
3 import (
4 "crypto/sha256"
5 "encoding/hex"
6 "encoding/json"
7 "fmt"
8 "strings"
9 )
10
11 // CallFingerprint builds a one-shot authorization fingerprint from the
12 // tool name, resolved subject/target, arguments, and preview summary.
13 // A continue decision only authorizes a later call whose fingerprint matches.
14 func CallFingerprint(tool, subject, preview string, args json.RawMessage) string {
15 tool = strings.TrimSpace(tool)
16 subject = strings.TrimSpace(subject)
17 preview = strings.TrimSpace(preview)
18 normArgs := normalizeArgs(args)
19 h := sha256.New()
20 _, _ = fmt.Fprintf(h, "tool=%s\n", tool)
21 _, _ = fmt.Fprintf(h, "subject=%s\n", subject)
22 _, _ = fmt.Fprintf(h, "preview=%s\n", preview)
23 _, _ = fmt.Fprintf(h, "args=%s\n", normArgs)
24 return hex.EncodeToString(h.Sum(nil))
25 }
26
27 func normalizeArgs(args json.RawMessage) string {
28 if len(args) == 0 {
29 return ""
30 }
31 var v any
32 if err := json.Unmarshal(args, &v); err != nil {
33 return string(args)
34 }
35 b, err := json.Marshal(v)
36 if err != nil {
37 return string(args)
38 }
39 return string(b)
40 }
41
42 // ArgsSummary produces a short display string for diagnostics.
43 func ArgsSummary(args json.RawMessage, max int) string {
44 if max <= 0 {
45 max = 200
46 }
47 s := strings.TrimSpace(string(args))
48 if s == "" {
49 return ""
50 }
51 var compact map[string]any
52 if err := json.Unmarshal(args, &compact); err == nil {
53 if cmd, ok := compact["command"].(string); ok && strings.TrimSpace(cmd) != "" {
54 s = strings.TrimSpace(cmd)
55 } else if path, ok := compact["path"].(string); ok && strings.TrimSpace(path) != "" {
56 s = strings.TrimSpace(path)
57 } else if p, ok := compact["file_path"].(string); ok && strings.TrimSpace(p) != "" {
58 s = strings.TrimSpace(p)
59 }
60 }
61 s = strings.Join(strings.Fields(s), " ")
62 if len(s) > max {
63 return s[:max] + "…"
64 }
65 return s
66 }
67
67 lines GO