返回 DeepSeek-Reasonix
types.go
1 // Package sessioncatalog maintains a disposable SQLite projection of Reasonix
2 // session sidecars. Session JSONL/event/meta files remain authoritative; every
3 // row in this package may be discarded and rebuilt.
4 package sessioncatalog
5
6 import (
7 "context"
8 "path/filepath"
9 "strings"
10 "time"
11
12 "reasonix/internal/agent"
13 "reasonix/internal/config"
14 )
15
16 const (
17 SchemaVersion = 13
18 repairEngineVersion = 1
19 DefaultLimit = 50
20 MaxLimit = 200
21 )
22
23 type TurnsState string
24
25 const (
26 TurnsUnknown TurnsState = "unknown"
27 TurnsValid TurnsState = "valid"
28 TurnsCorrupt TurnsState = "corrupt"
29 )
30
31 type Health string
32
33 const (
34 HealthOK Health = "ok"
35 HealthMissing Health = "missing"
36 HealthCorrupt Health = "corrupt"
37 HealthDegraded Health = "degraded"
38 )
39
40 type State string
41
42 const (
43 StateOpening State = "opening"
44 StateReady State = "ready"
45 StateDegraded State = "degraded"
46 StateRebuilding State = "rebuilding"
47 StateClosed State = "closed"
48 )
49
50 type Mode string
51
52 const (
53 ModeDisk Mode = "disk"
54 ModeMemory Mode = "memory"
55 )
56
57 type Status struct {
58 State State `json:"state"`
59 Mode Mode `json:"mode"`
60 Path string `json:"path,omitempty"`
61 Revision uint64 `json:"revision"`
62 Indexed int64 `json:"indexed"`
63 Total int64 `json:"total"`
64 RepairPending int64 `json:"repairPending"`
65 RepairActive int64 `json:"repairActive"`
66 RepairDeferred int64 `json:"repairDeferred"`
67 RepairBlocked int64 `json:"repairBlocked"`
68 NextRepairAt int64 `json:"nextRepairAt,omitempty"`
69 RepairErrorKinds map[string]int64 `json:"repairErrorKinds,omitempty"`
70 LastRepairDurationMS int64 `json:"lastRepairDurationMs,omitempty"`
71 PhysicalSessions int64 `json:"physicalSessions"`
72 LogicalSessions int64 `json:"logicalSessions"`
73 RecoveryGroups int64 `json:"recoveryGroups"`
74 RecoveryBranches int64 `json:"recoveryBranches"`
75 RecoveryDiverged int64 `json:"recoveryDiverged"`
76 CleanupEligible int64 `json:"cleanupEligible"`
77 // RepairReason records the last integrity condition that caused a
78 // directory to be scanned instead of trusting its persisted projection.
79 // It is diagnostic-only; the transcript and sidecars remain authoritative.
80 RepairReason string `json:"repairReason,omitempty"`
81 SourceCount int64 `json:"sourceCount"`
82 LastRepairAt int64 `json:"lastRepairAt,omitempty"`
83 LastError string `json:"lastError,omitempty"`
84 QuarantinedPath string `json:"quarantinedPath,omitempty"`
85 }
86
87 type Options struct {
88 Path string
89 InMemory bool
90 DisableRepair bool
91 MissingGrace time.Duration
92 QueueCapacity int
93 Now func() time.Time
94 OnRevision func(uint64, []string, string)
95 // repairSession replaces the filesystem repair. Open installs it before
96 // starting repairLoop, so scheduler tests can drive the real wake path
97 // without racing the hook assignment.
98 repairSession func(context.Context, string) (agent.SessionListingRepairResult, error)
99 }
100
101 type DirectoryTarget struct {
102 Path string `json:"path"`
103 Scope string `json:"scope"`
104 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
105 mutationSeq uint64
106 }
107
108 type ProjectRecord struct {
109 Scope string `json:"scope"`
110 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
111 Title string `json:"title"`
112 Color string `json:"color,omitempty"`
113 Pinned bool `json:"pinned,omitempty"`
114 SortOrder int `json:"sortOrder,omitempty"`
115 }
116
117 type TopicMetadata struct {
118 Scope string `json:"scope"`
119 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
120 TopicID string `json:"topicId"`
121 Title string `json:"title"`
122 TitleSource string `json:"titleSource,omitempty"`
123 Pinned bool `json:"pinned,omitempty"`
124 SortOrder int `json:"sortOrder,omitempty"`
125 CreatedAt int64 `json:"createdAt,omitempty"`
126 }
127
128 type SessionRecord struct {
129 Path string `json:"path"`
130 pathKey string
131 enqueueSequence uint64
132 Directory string `json:"directory"`
133 Scope string `json:"scope"`
134 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
135 TopicID string `json:"topicId,omitempty"`
136 TopicTitle string `json:"topicTitle,omitempty"`
137 CustomTitle string `json:"customTitle,omitempty"`
138 CreatedAt int64 `json:"createdAt,omitempty"`
139 LastActivityAt int64 `json:"lastActivityAt,omitempty"`
140 Preview string `json:"preview,omitempty"`
141 Turns int `json:"turns"`
142 TurnsState TurnsState `json:"turnsState"`
143 Recovered bool `json:"recovered,omitempty"`
144 RecoveryReason string `json:"recoveryReason,omitempty"`
145 RecoveryDigest string `json:"recoveryDigest,omitempty"`
146 ParentID string `json:"parentId,omitempty"`
147 RecoveryPreferred bool `json:"recoveryPreferred,omitempty"`
148 // RecoveryCopy is true only when real content is still covered by the parent.
149 RecoveryCopy bool `json:"recoveryCopy,omitempty"`
150 // RecoveryGroupID clusters a lineage of normal + recovery branches that
151 // share content ancestry. Empty for ordinary non-recovery sessions.
152 RecoveryGroupID string `json:"recoveryGroupId,omitempty"`
153 // RecoveryRole is normal | covered_copy | adopted | preferred | diverged.
154 RecoveryRole string `json:"recoveryRole,omitempty"`
155 // RecoveryCanonical marks the unique leaf that covers the group and should
156 // be opened by default. Never moves or rewrites files.
157 RecoveryCanonical bool `json:"recoveryCanonical,omitempty"`
158 // LogicalTopicID is the ordinary-list topic for this physical file. Recovery
159 // copies are re-anchored onto the root conversation's topic in the catalog
160 // only; authoritative sidecars keep their original topic_id.
161 LogicalTopicID string `json:"logicalTopicId,omitempty"`
162 // OrdinaryVisible is true only for the single logical representative that
163 // may appear in the ordinary project tree.
164 OrdinaryVisible bool `json:"ordinaryVisible,omitempty"`
165 // LogFormat is 2 for sessions whose event log is the append-only DAG; the
166 // head fields mirror its selected head and are zero for schema 1.
167 LogFormat int `json:"logFormat,omitempty"`
168 HeadCount int `json:"headCount,omitempty"`
169 SelectedHeadID string `json:"selectedHeadId,omitempty"`
170 heads []HeadRecord
171 ContentFingerprint string `json:"contentFingerprint,omitempty"`
172 MetaFingerprint string `json:"metaFingerprint,omitempty"`
173 Health Health `json:"health"`
174 MissingSince int64 `json:"missingSince,omitempty"`
175 }
176
177 // Recovery role constants for catalog lineage classification.
178 const (
179 RecoveryRoleNormal = "normal"
180 RecoveryRoleCoveredCopy = "covered_copy"
181 RecoveryRoleAdopted = "adopted"
182 RecoveryRolePreferred = "preferred"
183 RecoveryRoleDiverged = "diverged"
184 )
185
186 type TopicKey struct {
187 Scope string `json:"scope"`
188 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
189 TopicID string `json:"topicId"`
190 workspaceKey string
191 }
192
193 type TopicRecord struct {
194 Scope string `json:"scope"`
195 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
196 TopicID string `json:"topicId"`
197 Title string `json:"title"`
198 TitleSource string `json:"titleSource,omitempty"`
199 Pinned bool `json:"pinned,omitempty"`
200 SortOrder int `json:"sortOrder,omitempty"`
201 Turns int `json:"turns"`
202 TurnsState TurnsState `json:"turnsState"`
203 CreatedAt int64 `json:"createdAt,omitempty"`
204 LastActivityAt int64 `json:"lastActivityAt,omitempty"`
205 RecoveryState string `json:"recoveryState,omitempty"`
206 RecoveryBranchCount int `json:"recoveryBranchCount,omitempty"`
207 RecoveryUnresolvedCount int `json:"recoveryUnresolvedCount,omitempty"`
208 RecoveryCleanupEligibleCount int `json:"recoveryCleanupEligibleCount,omitempty"`
209 // RepresentativePath is the automatic open target for this logical topic.
210 RepresentativePath string `json:"representativePath,omitempty"`
211 Health Health `json:"health"`
212 Sessions []SessionRecord `json:"sessions"`
213 }
214
215 type TopicPageRequest struct {
216 Scope string `json:"scope"`
217 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
218 Cursor string `json:"cursor,omitempty"`
219 Limit int `json:"limit,omitempty"`
220 Query string `json:"query,omitempty"`
221 TimeFilter string `json:"timeFilter,omitempty"`
222 SortMode string `json:"sortMode,omitempty"`
223 // IncludeTopicIDsJSON and ExcludeTopicIDsJSON carry a JSON string array into
224 // SQLite's json_each table function. They keep large sidebar groups bounded
225 // to one SQL parameter instead of expanding one placeholder per topic.
226 IncludeTopicIDsJSON string `json:"-"`
227 ExcludeTopicIDsJSON string `json:"-"`
228 ExcludePinned bool `json:"-"`
229 CursorBinding string `json:"-"`
230 // ManualOrder makes sort_order the primary key within each pinned bucket.
231 // It is intentionally request-scoped: users who have never reordered keep
232 // the activity/created ordering even though metadata rows have a sort value.
233 ManualOrder bool `json:"manualOrder,omitempty"`
234 }
235
236 type TopicPage struct {
237 Items []TopicRecord `json:"items"`
238 NextCursor string `json:"nextCursor,omitempty"`
239 Revision uint64 `json:"revision"`
240 }
241
242 type SessionPageRequest struct {
243 Scope string `json:"scope"`
244 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
245 Directory string `json:"-"`
246 Cursor string `json:"cursor,omitempty"`
247 Limit int `json:"limit,omitempty"`
248 Query string `json:"query,omitempty"`
249 TimeFilter string `json:"timeFilter,omitempty"`
250 }
251
252 type SessionPage struct {
253 Items []SessionRecord `json:"items"`
254 NextCursor string `json:"nextCursor,omitempty"`
255 Revision uint64 `json:"revision"`
256 StaleCursor bool `json:"staleCursor,omitempty"`
257 }
258
259 // DefaultPath is the disposable cache file under CacheDir ("" when unavailable).
260 // v9.sqlite isolates path-identity-v2 keys from older writers.
261 // Session JSONL/WAL/sidecars remain authoritative and older binaries may keep
262 // using their own disposable cache without cross-writing this one.
263 func DefaultPath() string {
264 cache := strings.TrimSpace(config.CacheDir())
265 if cache == "" {
266 return ""
267 }
268 return filepath.Join(cache, "session-catalog", "v9.sqlite")
269 }
270
270 lines GO