@fgv/ts-agent-memory 5.1.0-36 → 5.1.0-39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/.rush/temp/{f6a88bfdd66517ccb98c2c7ae1be6e6fe9e15d38.tar.log → b82cf6bdece20481260e6bab946179eeec9d7b46.tar.log} +116 -2
  2. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +4 -4
  3. package/.rush/temp/operation/build/all.log +4 -4
  4. package/.rush/temp/operation/build/log-chunks.jsonl +4 -4
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/.rush/temp/shrinkwrap-deps.json +222 -221
  7. package/config/jest.config.json +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/packlets/ingest/cycleGuard.js +111 -0
  11. package/dist/packlets/ingest/cycleGuard.js.map +1 -0
  12. package/dist/packlets/ingest/hostStages.js +6 -0
  13. package/dist/packlets/ingest/hostStages.js.map +1 -0
  14. package/dist/packlets/ingest/index.js +9 -0
  15. package/dist/packlets/ingest/index.js.map +1 -0
  16. package/dist/packlets/ingest/model.js +6 -0
  17. package/dist/packlets/ingest/model.js.map +1 -0
  18. package/dist/packlets/ingest/orchestrator.js +438 -0
  19. package/dist/packlets/ingest/orchestrator.js.map +1 -0
  20. package/dist/packlets/retrieve/index.js +1 -0
  21. package/dist/packlets/retrieve/index.js.map +1 -1
  22. package/dist/packlets/retrieve/temporalRetrievers.js +172 -0
  23. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -0
  24. package/dist/packlets/store/fileTreeMemoryStore.js +323 -41
  25. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  26. package/dist/packlets/tools/index.js +6 -0
  27. package/dist/packlets/tools/index.js.map +1 -0
  28. package/dist/packlets/tools/memoryTools.js +337 -0
  29. package/dist/packlets/tools/memoryTools.js.map +1 -0
  30. package/dist/packlets/types/identityCodec.js +115 -0
  31. package/dist/packlets/types/identityCodec.js.map +1 -1
  32. package/dist/packlets/types/index.js +1 -0
  33. package/dist/packlets/types/index.js.map +1 -1
  34. package/dist/packlets/types/temporal.js +85 -0
  35. package/dist/packlets/types/temporal.js.map +1 -0
  36. package/dist/packlets/types/writePolicy.js +96 -0
  37. package/dist/packlets/types/writePolicy.js.map +1 -1
  38. package/dist/test/unit/converters/antagonistRoundTrip.test.js +95 -0
  39. package/dist/test/unit/converters/antagonistRoundTrip.test.js.map +1 -0
  40. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js +273 -0
  41. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -0
  42. package/dist/test/unit/ingest/cycleGuard.test.js +54 -0
  43. package/dist/test/unit/ingest/cycleGuard.test.js.map +1 -0
  44. package/dist/test/unit/ingest/orchestrator.test.js +913 -0
  45. package/dist/test/unit/ingest/orchestrator.test.js.map +1 -0
  46. package/dist/test/unit/retrieve/temporalRetrievers.test.js +182 -0
  47. package/dist/test/unit/retrieve/temporalRetrievers.test.js.map +1 -0
  48. package/dist/test/unit/store/antagonistTemporalBoundary.test.js +120 -0
  49. package/dist/test/unit/store/antagonistTemporalBoundary.test.js.map +1 -0
  50. package/dist/test/unit/store/fileTreeMemoryStore.test.js +74 -7
  51. package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
  52. package/dist/test/unit/store/temporalStore.test.js +398 -0
  53. package/dist/test/unit/store/temporalStore.test.js.map +1 -0
  54. package/dist/test/unit/tools/memoryTools.test.js +572 -0
  55. package/dist/test/unit/tools/memoryTools.test.js.map +1 -0
  56. package/dist/test/unit/types/temporalCodec.test.js +203 -0
  57. package/dist/test/unit/types/temporalCodec.test.js.map +1 -0
  58. package/dist/test/unit/types/temporalPolicy.test.js +62 -0
  59. package/dist/test/unit/types/temporalPolicy.test.js.map +1 -0
  60. package/dist/ts-agent-memory.d.ts +1042 -10
  61. package/dist/tsdoc-metadata.json +1 -1
  62. package/etc/ts-agent-memory.api.md +284 -0
  63. package/lib/index.d.ts +2 -0
  64. package/lib/index.d.ts.map +1 -1
  65. package/lib/index.js +2 -0
  66. package/lib/index.js.map +1 -1
  67. package/lib/packlets/ingest/cycleGuard.d.ts +41 -0
  68. package/lib/packlets/ingest/cycleGuard.d.ts.map +1 -0
  69. package/lib/packlets/ingest/cycleGuard.js +115 -0
  70. package/lib/packlets/ingest/cycleGuard.js.map +1 -0
  71. package/lib/packlets/ingest/hostStages.d.ts +88 -0
  72. package/lib/packlets/ingest/hostStages.d.ts.map +1 -0
  73. package/lib/packlets/ingest/hostStages.js +7 -0
  74. package/lib/packlets/ingest/hostStages.js.map +1 -0
  75. package/lib/packlets/ingest/index.d.ts +5 -0
  76. package/lib/packlets/ingest/index.d.ts.map +1 -0
  77. package/lib/packlets/ingest/index.js +25 -0
  78. package/lib/packlets/ingest/index.js.map +1 -0
  79. package/lib/packlets/ingest/model.d.ts +177 -0
  80. package/lib/packlets/ingest/model.d.ts.map +1 -0
  81. package/lib/packlets/ingest/model.js +7 -0
  82. package/lib/packlets/ingest/model.js.map +1 -0
  83. package/lib/packlets/ingest/orchestrator.d.ts +206 -0
  84. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -0
  85. package/lib/packlets/ingest/orchestrator.js +442 -0
  86. package/lib/packlets/ingest/orchestrator.js.map +1 -0
  87. package/lib/packlets/retrieve/index.d.ts +1 -0
  88. package/lib/packlets/retrieve/index.d.ts.map +1 -1
  89. package/lib/packlets/retrieve/index.js +1 -0
  90. package/lib/packlets/retrieve/index.js.map +1 -1
  91. package/lib/packlets/retrieve/temporalRetrievers.d.ts +78 -0
  92. package/lib/packlets/retrieve/temporalRetrievers.d.ts.map +1 -0
  93. package/lib/packlets/retrieve/temporalRetrievers.js +178 -0
  94. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -0
  95. package/lib/packlets/store/fileTreeMemoryStore.d.ts +118 -9
  96. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  97. package/lib/packlets/store/fileTreeMemoryStore.js +322 -40
  98. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  99. package/lib/packlets/tools/index.d.ts +2 -0
  100. package/lib/packlets/tools/index.d.ts.map +1 -0
  101. package/lib/packlets/tools/index.js +22 -0
  102. package/lib/packlets/tools/index.js.map +1 -0
  103. package/lib/packlets/tools/memoryTools.d.ts +139 -0
  104. package/lib/packlets/tools/memoryTools.d.ts.map +1 -0
  105. package/lib/packlets/tools/memoryTools.js +341 -0
  106. package/lib/packlets/tools/memoryTools.js.map +1 -0
  107. package/lib/packlets/types/identityCodec.d.ts +86 -0
  108. package/lib/packlets/types/identityCodec.d.ts.map +1 -1
  109. package/lib/packlets/types/identityCodec.js +118 -1
  110. package/lib/packlets/types/identityCodec.js.map +1 -1
  111. package/lib/packlets/types/index.d.ts +1 -0
  112. package/lib/packlets/types/index.d.ts.map +1 -1
  113. package/lib/packlets/types/index.js +1 -0
  114. package/lib/packlets/types/index.js.map +1 -1
  115. package/lib/packlets/types/temporal.d.ts +40 -0
  116. package/lib/packlets/types/temporal.d.ts.map +1 -0
  117. package/lib/packlets/types/temporal.js +92 -0
  118. package/lib/packlets/types/temporal.js.map +1 -0
  119. package/lib/packlets/types/writePolicy.d.ts +49 -0
  120. package/lib/packlets/types/writePolicy.d.ts.map +1 -1
  121. package/lib/packlets/types/writePolicy.js +98 -1
  122. package/lib/packlets/types/writePolicy.js.map +1 -1
  123. package/lib/test/unit/converters/antagonistRoundTrip.test.d.ts +10 -0
  124. package/lib/test/unit/converters/antagonistRoundTrip.test.d.ts.map +1 -0
  125. package/lib/test/unit/converters/antagonistRoundTrip.test.js +97 -0
  126. package/lib/test/unit/converters/antagonistRoundTrip.test.js.map +1 -0
  127. package/lib/test/unit/ingest/antagonistCycleAndParity.test.d.ts +9 -0
  128. package/lib/test/unit/ingest/antagonistCycleAndParity.test.d.ts.map +1 -0
  129. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js +275 -0
  130. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -0
  131. package/lib/test/unit/ingest/cycleGuard.test.d.ts +2 -0
  132. package/lib/test/unit/ingest/cycleGuard.test.d.ts.map +1 -0
  133. package/lib/test/unit/ingest/cycleGuard.test.js +56 -0
  134. package/lib/test/unit/ingest/cycleGuard.test.js.map +1 -0
  135. package/lib/test/unit/ingest/orchestrator.test.d.ts +2 -0
  136. package/lib/test/unit/ingest/orchestrator.test.d.ts.map +1 -0
  137. package/lib/test/unit/ingest/orchestrator.test.js +915 -0
  138. package/lib/test/unit/ingest/orchestrator.test.js.map +1 -0
  139. package/lib/test/unit/retrieve/temporalRetrievers.test.d.ts +2 -0
  140. package/lib/test/unit/retrieve/temporalRetrievers.test.d.ts.map +1 -0
  141. package/lib/test/unit/retrieve/temporalRetrievers.test.js +184 -0
  142. package/lib/test/unit/retrieve/temporalRetrievers.test.js.map +1 -0
  143. package/lib/test/unit/store/antagonistTemporalBoundary.test.d.ts +9 -0
  144. package/lib/test/unit/store/antagonistTemporalBoundary.test.d.ts.map +1 -0
  145. package/lib/test/unit/store/antagonistTemporalBoundary.test.js +122 -0
  146. package/lib/test/unit/store/antagonistTemporalBoundary.test.js.map +1 -0
  147. package/lib/test/unit/store/fileTreeMemoryStore.test.js +74 -7
  148. package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
  149. package/lib/test/unit/store/temporalStore.test.d.ts +2 -0
  150. package/lib/test/unit/store/temporalStore.test.d.ts.map +1 -0
  151. package/lib/test/unit/store/temporalStore.test.js +400 -0
  152. package/lib/test/unit/store/temporalStore.test.js.map +1 -0
  153. package/lib/test/unit/tools/memoryTools.test.d.ts +2 -0
  154. package/lib/test/unit/tools/memoryTools.test.d.ts.map +1 -0
  155. package/lib/test/unit/tools/memoryTools.test.js +574 -0
  156. package/lib/test/unit/tools/memoryTools.test.js.map +1 -0
  157. package/lib/test/unit/types/temporalCodec.test.d.ts +2 -0
  158. package/lib/test/unit/types/temporalCodec.test.d.ts.map +1 -0
  159. package/lib/test/unit/types/temporalCodec.test.js +205 -0
  160. package/lib/test/unit/types/temporalCodec.test.js.map +1 -0
  161. package/lib/test/unit/types/temporalPolicy.test.d.ts +2 -0
  162. package/lib/test/unit/types/temporalPolicy.test.d.ts.map +1 -0
  163. package/lib/test/unit/types/temporalPolicy.test.js +64 -0
  164. package/lib/test/unit/types/temporalPolicy.test.js.map +1 -0
  165. package/package.json +19 -19
  166. package/rush-logs/ts-agent-memory.build.cache.log +1 -1
  167. package/rush-logs/ts-agent-memory.build.log +4 -4
  168. package/src/index.ts +2 -0
  169. package/src/packlets/ingest/cycleGuard.ts +142 -0
  170. package/src/packlets/ingest/hostStages.ts +111 -0
  171. package/src/packlets/ingest/index.ts +9 -0
  172. package/src/packlets/ingest/model.ts +184 -0
  173. package/src/packlets/ingest/orchestrator.ts +797 -0
  174. package/src/packlets/retrieve/index.ts +1 -0
  175. package/src/packlets/retrieve/temporalRetrievers.ts +210 -0
  176. package/src/packlets/store/fileTreeMemoryStore.ts +460 -66
  177. package/src/packlets/tools/index.ts +6 -0
  178. package/src/packlets/tools/memoryTools.ts +579 -0
  179. package/src/packlets/types/identityCodec.ts +184 -0
  180. package/src/packlets/types/index.ts +1 -0
  181. package/src/packlets/types/temporal.ts +96 -0
  182. package/src/packlets/types/writePolicy.ts +127 -0
  183. package/src/test/unit/converters/antagonistRoundTrip.test.ts +110 -0
  184. package/src/test/unit/ingest/antagonistCycleAndParity.test.ts +362 -0
  185. package/src/test/unit/ingest/cycleGuard.test.ts +68 -0
  186. package/src/test/unit/ingest/orchestrator.test.ts +1158 -0
  187. package/src/test/unit/retrieve/temporalRetrievers.test.ts +226 -0
  188. package/src/test/unit/store/antagonistTemporalBoundary.test.ts +158 -0
  189. package/src/test/unit/store/fileTreeMemoryStore.test.ts +98 -7
  190. package/src/test/unit/store/temporalStore.test.ts +469 -0
  191. package/src/test/unit/tools/memoryTools.test.ts +771 -0
  192. package/src/test/unit/types/temporalCodec.test.ts +259 -0
  193. package/src/test/unit/types/temporalPolicy.test.ts +96 -0
  194. package/temp/build/lint/_eslint-5eVG3S6w.json +122 -46
  195. package/temp/build/typescript/ts_8nwakTlr.json +1 -1
  196. package/temp/ts-agent-memory.api.json +11984 -6314
  197. package/temp/ts-agent-memory.api.md +284 -0
@@ -9,4 +9,5 @@ export * from './linkTraversalRetriever';
9
9
  export * from './tagRetriever';
10
10
  export * from './structuredFilterRetriever';
11
11
  export * from './semanticRetriever';
12
+ export * from './temporalRetrievers';
12
13
  export * from './hybridRetriever';
@@ -0,0 +1,210 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Result, succeed } from '@fgv/ts-utils';
7
+ import { IMemoryRecord, isTemporalRecord, selectCurrentVersion, selectVersionAsOf } from '../types';
8
+ import { IMemoryIndex } from '../index';
9
+ import {
10
+ IMemoryQuery,
11
+ IMemoryRetriever,
12
+ IMemoryRetrieverCapabilities,
13
+ guardRetrieverCapabilities,
14
+ indexedRecordMatchesQuery,
15
+ limitRecords,
16
+ recencyCompare
17
+ } from './retriever';
18
+
19
+ /**
20
+ * The capabilities every temporal retriever exposes: temporal "as-of" queries
21
+ * are operational (semantic recall and link traversal are not this retriever's
22
+ * concern).
23
+ * @public
24
+ */
25
+ export const TEMPORAL_CAPABILITIES: IMemoryRetrieverCapabilities = {
26
+ supportsSemanticRecall: false,
27
+ supportsTemporalQuery: true,
28
+ supportsLinkTraversal: false
29
+ };
30
+
31
+ /**
32
+ * Group the temporal records surviving a query's scope / kind / tag / predicate
33
+ * pre-filter by entity (`kind` + `entityId`). Non-temporal records are excluded —
34
+ * the temporal retrievers operate only over versioned entities. The map values
35
+ * are each entity's versions (unordered).
36
+ */
37
+ function groupTemporalVersionsByEntity(
38
+ index: IMemoryIndex,
39
+ query: IMemoryQuery
40
+ ): Map<string, IMemoryRecord<unknown>[]> {
41
+ const groups: Map<string, IMemoryRecord<unknown>[]> = new Map<string, IMemoryRecord<unknown>[]>();
42
+ for (const entry of index.entries()) {
43
+ if (!isTemporalRecord(entry.record) || !indexedRecordMatchesQuery(entry, query)) {
44
+ continue;
45
+ }
46
+ const key: string = `${entry.record.envelope.kind}\0${entry.record.envelope.entityId}`;
47
+ const existing: IMemoryRecord<unknown>[] | undefined = groups.get(key);
48
+ if (existing === undefined) {
49
+ groups.set(key, [entry.record]);
50
+ } else {
51
+ existing.push(entry.record);
52
+ }
53
+ }
54
+ return groups;
55
+ }
56
+
57
+ /**
58
+ * Temporal retriever returning the **current** version of each temporal entity
59
+ * matching the query (the newest version whose `invalid_at` is null/absent),
60
+ * recency-ordered and limited. A fully-invalidated (soft-deleted) entity
61
+ * contributes nothing. Non-temporal records are not this retriever's concern.
62
+ * @public
63
+ */
64
+ export class CurrentValidRetriever implements IMemoryRetriever {
65
+ private readonly _index: IMemoryIndex;
66
+
67
+ private constructor(index: IMemoryIndex) {
68
+ this._index = index;
69
+ }
70
+
71
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
72
+ public get capabilities(): IMemoryRetrieverCapabilities {
73
+ return TEMPORAL_CAPABILITIES;
74
+ }
75
+
76
+ /** Family-convention factory. */
77
+ public static create(index: IMemoryIndex): Result<CurrentValidRetriever> {
78
+ return succeed(new CurrentValidRetriever(index));
79
+ }
80
+
81
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
82
+ public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {
83
+ return Promise.resolve(
84
+ guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {
85
+ const selected: IMemoryRecord<unknown>[] = [];
86
+ for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {
87
+ const current: IMemoryRecord<unknown> | undefined = selectCurrentVersion(versions);
88
+ if (current !== undefined) {
89
+ selected.push(current);
90
+ }
91
+ }
92
+ selected.sort(recencyCompare);
93
+ return succeed(limitRecords(selected, query.limit));
94
+ })
95
+ );
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Temporal retriever returning, for each temporal entity matching the query, the
101
+ * version valid at `query.asOf` (epoch ms). `asOf` is this retriever's axis: a
102
+ * query without it yields an empty success (a no-op contribution to a
103
+ * {@link HybridRetriever}, not a failure). Results are recency-ordered and
104
+ * limited.
105
+ * @public
106
+ */
107
+ export class AsOfRetriever implements IMemoryRetriever {
108
+ private readonly _index: IMemoryIndex;
109
+
110
+ private constructor(index: IMemoryIndex) {
111
+ this._index = index;
112
+ }
113
+
114
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
115
+ public get capabilities(): IMemoryRetrieverCapabilities {
116
+ return TEMPORAL_CAPABILITIES;
117
+ }
118
+
119
+ /** Family-convention factory. */
120
+ public static create(index: IMemoryIndex): Result<AsOfRetriever> {
121
+ return succeed(new AsOfRetriever(index));
122
+ }
123
+
124
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
125
+ public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {
126
+ return Promise.resolve(
127
+ guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {
128
+ if (query.asOf === undefined) {
129
+ return succeed([]);
130
+ }
131
+ const asOf: number = query.asOf;
132
+ const selected: IMemoryRecord<unknown>[] = [];
133
+ for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {
134
+ const valid: IMemoryRecord<unknown> | undefined = selectVersionAsOf(versions, asOf);
135
+ if (valid !== undefined) {
136
+ selected.push(valid);
137
+ }
138
+ }
139
+ selected.sort(recencyCompare);
140
+ return succeed(limitRecords(selected, query.limit));
141
+ })
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Temporal retriever returning **every** version of each temporal entity matching
148
+ * the query, ordered ascending by `valid_at` (then `seq` as a stable tiebreak) —
149
+ * the entity's full history. Limited after ordering.
150
+ *
151
+ * @remarks
152
+ * When the query matches more than one entity, the result is a single
153
+ * CROSS-entity list globally sorted by `valid_at` — versions of different
154
+ * entities interleave, NOT grouped per entity — so `limit` truncates that
155
+ * globally-sorted list. Constrain to one entity (e.g. via `query.scope`) for a
156
+ * single entity's contiguous history.
157
+ * @public
158
+ */
159
+ export class HistoryRetriever implements IMemoryRetriever {
160
+ private readonly _index: IMemoryIndex;
161
+
162
+ private constructor(index: IMemoryIndex) {
163
+ this._index = index;
164
+ }
165
+
166
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
167
+ public get capabilities(): IMemoryRetrieverCapabilities {
168
+ return TEMPORAL_CAPABILITIES;
169
+ }
170
+
171
+ /** Family-convention factory. */
172
+ public static create(index: IMemoryIndex): Result<HistoryRetriever> {
173
+ return succeed(new HistoryRetriever(index));
174
+ }
175
+
176
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
177
+ public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {
178
+ return Promise.resolve(
179
+ guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {
180
+ const history: IMemoryRecord<unknown>[] = [];
181
+ for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {
182
+ history.push(...versions);
183
+ }
184
+ history.sort(HistoryRetriever._byValidAtAscending);
185
+ return succeed(limitRecords(history, query.limit));
186
+ })
187
+ );
188
+ }
189
+
190
+ /** A version's world-truth start: its `valid_at`, defaulting to `created` when absent. */
191
+ private static _startOf(record: IMemoryRecord<unknown>): number {
192
+ const temporal: IMemoryRecord<unknown>['envelope']['temporal'] = record.envelope.temporal;
193
+ /* c8 ignore next 3 -- unreachable: HistoryRetriever orders only temporal records (pre-filtered by isTemporalRecord), so `temporal` is always present; the guard keeps the type honest */
194
+ if (temporal === undefined) {
195
+ return record.envelope.created;
196
+ }
197
+ return temporal.valid_at ?? record.envelope.created;
198
+ }
199
+
200
+ /**
201
+ * Ascending-by-`valid_at` comparator (a version's `valid_at` defaults to its
202
+ * `created` when absent), with `seq` as a stable ascending tiebreak so
203
+ * same-instant versions order by write sequence.
204
+ */
205
+ private static _byValidAtAscending(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number {
206
+ const aStart: number = HistoryRetriever._startOf(a);
207
+ const bStart: number = HistoryRetriever._startOf(b);
208
+ return aStart !== bStart ? aStart - bStart : a.envelope.seq - b.envelope.seq;
209
+ }
210
+ }