返回 DeepSeek-Reasonix
recovery_filename.go
根目录 / internal / agent / recovery_filename.go
1 package agent
2
3 import (
4 "path/filepath"
5 "strings"
6 )
7
8 // RecoveryFilenameParentID recognizes legacy automatic recovery files named
9 // <stem>-recovery-<16 hex>.jsonl when meta is missing or incomplete. The parent
10 // ID is the stem before the final -recovery-<hex> suffix (not a content proof).
11 func RecoveryFilenameParentID(path string) (string, bool) {
12 id := BranchID(path)
13 parent, ok := peelRecoveryFilenameSuffix(id)
14 if !ok {
15 return "", false
16 }
17 return parent, true
18 }
19
20 // RecoveryFilenameRootID peels nested -recovery-<16 hex> suffixes to the
21 // non-recovery stem used as a stable recovery-group key for old files.
22 func RecoveryFilenameRootID(path string) (string, bool) {
23 id := BranchID(path)
24 root, ok := peelRecoveryFilenameSuffix(id)
25 if !ok {
26 return "", false
27 }
28 for {
29 next, nested := peelRecoveryFilenameSuffix(root)
30 if !nested {
31 break
32 }
33 root = next
34 }
35 if strings.TrimSpace(root) == "" {
36 return "", false
37 }
38 return root, true
39 }
40
41 // LooksLikeRecoveryFilename reports whether the path follows the automatic
42 // recovery naming convention used before meta always carried Recovered=true.
43 func LooksLikeRecoveryFilename(path string) bool {
44 _, ok := peelRecoveryFilenameSuffix(BranchID(path))
45 return ok
46 }
47
48 func peelRecoveryFilenameSuffix(id string) (string, bool) {
49 id = strings.TrimSpace(id)
50 const marker = "-recovery-"
51 index := strings.LastIndex(id, marker)
52 if index <= 0 {
53 return "", false
54 }
55 suffix := id[index+len(marker):]
56 if len(suffix) != 16 || !isLowerHex(suffix) {
57 return "", false
58 }
59 parent := strings.TrimSpace(id[:index])
60 if parent == "" || parent == "." || filepath.Base(parent) != parent {
61 return "", false
62 }
63 return parent, true
64 }
65
66 func isLowerHex(value string) bool {
67 if value == "" {
68 return false
69 }
70 for _, r := range value {
71 switch {
72 case r >= '0' && r <= '9':
73 case r >= 'a' && r <= 'f':
74 case r >= 'A' && r <= 'F':
75 default:
76 return false
77 }
78 }
79 return true
80 }
81
81 lines GO