pi-blackhole 0.4.2 → 0.4.3
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.
- package/README.md +15 -0
- package/dist/index.js +11660 -0
- package/dist/index.js.map +1 -0
- package/example-config.json +1 -1
- package/index.ts +37 -63
- package/package.json +21 -9
- package/src/commands/cleanup.ts +279 -240
- package/src/commands/memory.ts +236 -184
- package/src/commands/pi-vcc.ts +202 -152
- package/src/commands/vcc-recall.ts +126 -95
- package/src/core/brief.ts +167 -33
- package/src/core/build-sections.ts +8 -2
- package/src/core/config-env.ts +117 -0
- package/src/core/content.ts +31 -7
- package/src/core/drill-down.ts +41 -11
- package/src/core/filter-noise.ts +9 -3
- package/src/core/format-recall.ts +15 -6
- package/src/core/format.ts +14 -4
- package/src/core/lineage.ts +9 -3
- package/src/core/load-messages.ts +24 -5
- package/src/core/normalize.ts +38 -14
- package/src/core/recall-scope.ts +11 -3
- package/src/core/render-entries.ts +22 -6
- package/src/core/sanitize.ts +5 -1
- package/src/core/search-entries.ts +111 -19
- package/src/core/settings.ts +1 -3
- package/src/core/summarize.ts +42 -21
- package/src/core/unified-config.ts +549 -411
- package/src/extract/commits.ts +4 -2
- package/src/extract/files.ts +10 -5
- package/src/extract/goals.ts +7 -2
- package/src/hooks/before-compact.ts +210 -88
- package/src/om/agents/dropper/agent.ts +380 -265
- package/src/om/agents/dropper/coverage.ts +102 -82
- package/src/om/agents/observer/agent.ts +242 -206
- package/src/om/agents/reflector/agent.ts +212 -153
- package/src/om/cleanup.ts +239 -218
- package/src/om/clipboard.ts +59 -51
- package/src/om/compaction-trigger.ts +448 -333
- package/src/om/config.ts +13 -6
- package/src/om/configure-overlay.ts +518 -355
- package/src/om/consolidation.ts +1460 -953
- package/src/om/cooldown.ts +75 -65
- package/src/om/debug-log.ts +86 -68
- package/src/om/ids.ts +1 -1
- package/src/om/ledger/fold.ts +89 -78
- package/src/om/ledger/progress.ts +181 -153
- package/src/om/ledger/projection.ts +248 -185
- package/src/om/ledger/recall.ts +247 -196
- package/src/om/ledger/render-summary.ts +79 -50
- package/src/om/ledger/types.ts +146 -117
- package/src/om/model-budget.ts +23 -13
- package/src/om/pending.ts +243 -179
- package/src/om/provider-stream.ts +52 -7
- package/src/om/retryable-error.ts +12 -16
- package/src/om/reverse-recall.ts +97 -91
- package/src/om/runtime.ts +474 -375
- package/src/om/serialize.ts +190 -166
- package/src/om/status-overlay.ts +246 -195
- package/src/om/tokens.ts +28 -21
- package/src/pi-base/blackhole-settings.ts +437 -0
- package/src/pi-base/config-manager.ts +440 -0
- package/src/pi-base/config.ts +469 -0
- package/src/pi-base/env.ts +43 -0
- package/src/pi-base/paths.ts +47 -0
- package/src/pi-base/settings/body.ts +1648 -0
- package/src/pi-base/settings/fields/action.ts +43 -0
- package/src/pi-base/settings/fields/boolean.ts +47 -0
- package/src/pi-base/settings/fields/custom.ts +72 -0
- package/src/pi-base/settings/fields/enum.ts +310 -0
- package/src/pi-base/settings/fields/index.ts +46 -0
- package/src/pi-base/settings/fields/model.ts +452 -0
- package/src/pi-base/settings/fields/string.ts +527 -0
- package/src/pi-base/settings/fields/text.ts +115 -0
- package/src/pi-base/settings/frame.ts +197 -0
- package/src/pi-base/settings/index.ts +77 -0
- package/src/pi-base/settings/inline-edit.ts +313 -0
- package/src/pi-base/settings/modal.ts +152 -0
- package/src/pi-base/settings/types.ts +500 -0
- package/src/pi-base/settings/validate-field.ts +113 -0
- package/src/pi-base/shell.ts +117 -0
- package/src/pi-base/types.ts +6 -0
- package/src/pi-base/ui.ts +32 -0
- package/src/tools/recall.ts +347 -225
- package/src/types.ts +20 -3
- package/tsup.config.ts +23 -0
- package/vitest.config.ts +15 -15
|
@@ -6,252 +6,315 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { selectPriorObservations } from "./render-summary.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
OM_FOLDED,
|
|
10
|
+
isMemoryDetails,
|
|
11
|
+
isObservationsDroppedEntry,
|
|
12
|
+
isObservationsRecordedEntry,
|
|
13
|
+
isReflectionsRecordedEntry,
|
|
14
|
+
type Entry,
|
|
15
|
+
type MemoryDetails,
|
|
16
|
+
type Observation,
|
|
17
|
+
type Reflection,
|
|
18
18
|
} from "./types.js";
|
|
19
19
|
|
|
20
20
|
export type Projection = {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
observations: Observation[];
|
|
22
|
+
reflections: Reflection[];
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export type ProjectionDiff = {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
observationsOnlyInFull: Observation[];
|
|
27
|
+
reflectionsOnlyInFull: Reflection[];
|
|
28
|
+
droppedOnlyInFull: Observation[];
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export type CompactionProjectionConfig = {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
observationsPoolMaxTokens: number;
|
|
33
|
+
fullFoldAlways?: boolean;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export type CompactionProjection = Projection & {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
fullFold: boolean;
|
|
38
|
+
details: MemoryDetails;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
type ProjectionBoundary =
|
|
42
|
-
|
|
43
|
-
| { kind: "tip" }
|
|
44
|
-
| { kind: "none" };
|
|
42
|
+
{ kind: "entry"; entryId: string } | { kind: "tip" } | { kind: "none" };
|
|
45
43
|
|
|
46
44
|
type ProjectionFoldOptions = {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
observationsBoundary: ProjectionBoundary;
|
|
46
|
+
reflectionsBoundary: ProjectionBoundary;
|
|
47
|
+
dropsBoundary: ProjectionBoundary;
|
|
50
48
|
};
|
|
51
49
|
|
|
52
50
|
function entryIndexById(entries: Entry[]): Map<string, number> {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
const indexes = new Map<string, number>();
|
|
52
|
+
for (let i = 0; i < entries.length; i++) indexes.set(entries[i].id, i);
|
|
53
|
+
return indexes;
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
function entryBoundary(entryId: string): ProjectionBoundary {
|
|
59
|
-
|
|
57
|
+
return { kind: "entry", entryId };
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
function tipBoundary(): ProjectionBoundary {
|
|
63
|
-
|
|
61
|
+
return { kind: "tip" };
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
function noneBoundary(): ProjectionBoundary {
|
|
67
|
-
|
|
65
|
+
return { kind: "none" };
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
function boundaryIndex(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
function boundaryIndex(
|
|
69
|
+
entries: Entry[],
|
|
70
|
+
indexes: Map<string, number>,
|
|
71
|
+
boundary: ProjectionBoundary,
|
|
72
|
+
): number {
|
|
73
|
+
if (boundary.kind === "tip") return entries.length - 1;
|
|
74
|
+
if (boundary.kind === "none") return -1;
|
|
75
|
+
return indexes.get(boundary.entryId) ?? -1;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
function coverageIndex(
|
|
77
|
-
|
|
78
|
+
function coverageIndex(
|
|
79
|
+
entry: Entry & { data: { coversUpToId: string } },
|
|
80
|
+
indexes: Map<string, number>,
|
|
81
|
+
): number {
|
|
82
|
+
return indexes.get(entry.data.coversUpToId) ?? -1;
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
function isAtOrBefore(index: number, boundaryIndex: number): boolean {
|
|
81
|
-
|
|
86
|
+
return index >= 0 && boundaryIndex >= 0 && index <= boundaryIndex;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
function isCoveredAtOrBefore(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
entry: Entry & { data: { coversUpToId: string } },
|
|
91
|
+
indexes: Map<string, number>,
|
|
92
|
+
boundaryIndex: number,
|
|
88
93
|
): boolean {
|
|
89
|
-
|
|
94
|
+
return isAtOrBefore(coverageIndex(entry, indexes), boundaryIndex);
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
function foldProjection(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
97
|
+
function foldProjection(
|
|
98
|
+
entries: Entry[],
|
|
99
|
+
options: ProjectionFoldOptions,
|
|
100
|
+
): Projection {
|
|
101
|
+
const indexes = entryIndexById(entries);
|
|
102
|
+
const observationsBoundary = boundaryIndex(
|
|
103
|
+
entries,
|
|
104
|
+
indexes,
|
|
105
|
+
options.observationsBoundary,
|
|
106
|
+
);
|
|
107
|
+
const reflectionsBoundary = boundaryIndex(
|
|
108
|
+
entries,
|
|
109
|
+
indexes,
|
|
110
|
+
options.reflectionsBoundary,
|
|
111
|
+
);
|
|
112
|
+
const dropsBoundary = boundaryIndex(entries, indexes, options.dropsBoundary);
|
|
113
|
+
const observations: Observation[] = [];
|
|
114
|
+
const reflections: Reflection[] = [];
|
|
115
|
+
const observationsById = new Set<string>();
|
|
116
|
+
const reflectionsById = new Set<string>();
|
|
117
|
+
const droppedObservationIds = new Set<string>();
|
|
118
|
+
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
if (
|
|
121
|
+
isObservationsRecordedEntry(entry) &&
|
|
122
|
+
isCoveredAtOrBefore(entry, indexes, observationsBoundary)
|
|
123
|
+
) {
|
|
124
|
+
for (const observation of entry.data.observations) {
|
|
125
|
+
if (observationsById.has(observation.id)) continue;
|
|
126
|
+
observationsById.add(observation.id);
|
|
127
|
+
observations.push(observation);
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
isReflectionsRecordedEntry(entry) &&
|
|
134
|
+
isCoveredAtOrBefore(entry, indexes, reflectionsBoundary)
|
|
135
|
+
) {
|
|
136
|
+
for (const reflection of entry.data.reflections) {
|
|
137
|
+
if (reflectionsById.has(reflection.id)) continue;
|
|
138
|
+
reflectionsById.add(reflection.id);
|
|
139
|
+
reflections.push(reflection);
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
isObservationsDroppedEntry(entry) &&
|
|
146
|
+
isCoveredAtOrBefore(entry, indexes, dropsBoundary)
|
|
147
|
+
) {
|
|
148
|
+
for (const observationId of entry.data.observationIds)
|
|
149
|
+
droppedObservationIds.add(observationId);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
observations: observations.filter(
|
|
155
|
+
(observation) => !droppedObservationIds.has(observation.id),
|
|
156
|
+
),
|
|
157
|
+
reflections,
|
|
158
|
+
};
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
function projectionFromMemoryDetails(details: MemoryDetails): Projection {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
162
|
+
return {
|
|
163
|
+
observations: [...details.observations],
|
|
164
|
+
reflections: [...details.reflections],
|
|
165
|
+
};
|
|
138
166
|
}
|
|
139
167
|
|
|
140
|
-
function latestV3CompactionDetails(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
168
|
+
function latestV3CompactionDetails(
|
|
169
|
+
entries: Entry[],
|
|
170
|
+
): MemoryDetails | undefined {
|
|
171
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
172
|
+
const entry = entries[i];
|
|
173
|
+
if (entry.type !== "compaction") continue;
|
|
174
|
+
const details = unwrapMemoryDetails(entry);
|
|
175
|
+
if (details) return details;
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
148
178
|
}
|
|
149
179
|
|
|
150
|
-
export function fullProjection(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
export function fullProjection(
|
|
181
|
+
entries: Entry[],
|
|
182
|
+
upToEntryId?: string,
|
|
183
|
+
): Projection {
|
|
184
|
+
const boundary = upToEntryId ? entryBoundary(upToEntryId) : tipBoundary();
|
|
185
|
+
return foldProjection(entries, {
|
|
186
|
+
observationsBoundary: boundary,
|
|
187
|
+
reflectionsBoundary: boundary,
|
|
188
|
+
dropsBoundary: boundary,
|
|
189
|
+
});
|
|
157
190
|
}
|
|
158
191
|
|
|
159
|
-
export function visibleProjection(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
192
|
+
export function visibleProjection(
|
|
193
|
+
entries: Entry[],
|
|
194
|
+
upToEntryId?: string,
|
|
195
|
+
): Projection {
|
|
196
|
+
if (!upToEntryId) {
|
|
197
|
+
const details = latestV3CompactionDetails(entries);
|
|
198
|
+
if (details) return projectionFromMemoryDetails(details);
|
|
199
|
+
// No compaction has run yet — show everything so the user sees
|
|
200
|
+
// recorded data until first /blackhole creates a proper snapshot.
|
|
201
|
+
return fullProjection(entries);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return buildCompactionProjection(entries, upToEntryId, {
|
|
205
|
+
observationsPoolMaxTokens: Number.POSITIVE_INFINITY,
|
|
206
|
+
});
|
|
169
207
|
}
|
|
170
208
|
|
|
171
209
|
function unwrapMemoryDetails(entry: Entry): MemoryDetails | undefined {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
210
|
+
if (isMemoryDetails(entry.details)) return entry.details;
|
|
211
|
+
if (
|
|
212
|
+
entry.details &&
|
|
213
|
+
typeof entry.details === "object" &&
|
|
214
|
+
!Array.isArray(entry.details)
|
|
215
|
+
) {
|
|
216
|
+
const nested = (entry.details as Record<string, unknown>)["om.folded"];
|
|
217
|
+
if (isMemoryDetails(nested)) return nested;
|
|
218
|
+
}
|
|
219
|
+
return undefined;
|
|
178
220
|
}
|
|
179
221
|
|
|
180
222
|
export function latestFullFoldBoundaryId(entries: Entry[]): string | undefined {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
223
|
+
const indexes = entryIndexById(entries);
|
|
224
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
225
|
+
const entry = entries[i];
|
|
226
|
+
if (entry.type !== "compaction") continue;
|
|
227
|
+
const details = unwrapMemoryDetails(entry);
|
|
228
|
+
if (!details) continue;
|
|
229
|
+
if (!details.fullFold) continue;
|
|
230
|
+
if (!entry.firstKeptEntryId) continue;
|
|
231
|
+
if (!indexes.has(entry.firstKeptEntryId)) continue;
|
|
232
|
+
return entry.firstKeptEntryId;
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
193
235
|
}
|
|
194
236
|
|
|
195
237
|
export function buildCompactionProjection(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
238
|
+
entries: Entry[],
|
|
239
|
+
firstKeptEntryId: string,
|
|
240
|
+
config: CompactionProjectionConfig,
|
|
199
241
|
): CompactionProjection {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
const fullFoldBoundaryId = latestFullFoldBoundaryId(entries);
|
|
243
|
+
const maintenanceBoundary = fullFoldBoundaryId
|
|
244
|
+
? entryBoundary(fullFoldBoundaryId)
|
|
245
|
+
: config.fullFoldAlways
|
|
246
|
+
? entryBoundary(firstKeptEntryId)
|
|
247
|
+
: noneBoundary();
|
|
248
|
+
const normalProjection = foldProjection(entries, {
|
|
249
|
+
observationsBoundary: entryBoundary(firstKeptEntryId),
|
|
250
|
+
reflectionsBoundary: maintenanceBoundary,
|
|
251
|
+
dropsBoundary: maintenanceBoundary,
|
|
252
|
+
});
|
|
253
|
+
const observationTokens = normalProjection.observations.reduce(
|
|
254
|
+
(total, observation) => total + observation.tokenCount,
|
|
255
|
+
0,
|
|
256
|
+
);
|
|
257
|
+
const fullFold = observationTokens >= config.observationsPoolMaxTokens;
|
|
258
|
+
let projection = fullFold
|
|
259
|
+
? fullProjection(entries, firstKeptEntryId)
|
|
260
|
+
: normalProjection;
|
|
261
|
+
|
|
262
|
+
// Cap observations to budget using relevance-tiered + recency scoring.
|
|
263
|
+
// Even if the dropper determined some old observations are worth keeping,
|
|
264
|
+
// this safety valve ensures the compaction output never exceeds the pool
|
|
265
|
+
// token budget. Observations survive in the branch regardless.
|
|
266
|
+
if (
|
|
267
|
+
config.observationsPoolMaxTokens > 0 &&
|
|
268
|
+
observationTokens >= config.observationsPoolMaxTokens
|
|
269
|
+
) {
|
|
270
|
+
projection = {
|
|
271
|
+
observations: selectPriorObservations(
|
|
272
|
+
projection.observations,
|
|
273
|
+
config.observationsPoolMaxTokens,
|
|
274
|
+
),
|
|
275
|
+
reflections: projection.reflections,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const details: MemoryDetails = {
|
|
280
|
+
type: OM_FOLDED,
|
|
281
|
+
version: 1,
|
|
282
|
+
fullFold,
|
|
283
|
+
observations: projection.observations,
|
|
284
|
+
reflections: projection.reflections,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
fullFold,
|
|
289
|
+
observations: projection.observations,
|
|
290
|
+
reflections: projection.reflections,
|
|
291
|
+
details,
|
|
292
|
+
};
|
|
245
293
|
}
|
|
246
294
|
|
|
247
|
-
export function diffProjection(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
295
|
+
export function diffProjection(
|
|
296
|
+
visible: Projection,
|
|
297
|
+
full: Projection,
|
|
298
|
+
): ProjectionDiff {
|
|
299
|
+
const visibleObservationIds = new Set(
|
|
300
|
+
visible.observations.map((observation) => observation.id),
|
|
301
|
+
);
|
|
302
|
+
const fullObservationIds = new Set(
|
|
303
|
+
full.observations.map((observation) => observation.id),
|
|
304
|
+
);
|
|
305
|
+
const visibleReflectionIds = new Set(
|
|
306
|
+
visible.reflections.map((reflection) => reflection.id),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
observationsOnlyInFull: full.observations.filter(
|
|
311
|
+
(observation) => !visibleObservationIds.has(observation.id),
|
|
312
|
+
),
|
|
313
|
+
reflectionsOnlyInFull: full.reflections.filter(
|
|
314
|
+
(reflection) => !visibleReflectionIds.has(reflection.id),
|
|
315
|
+
),
|
|
316
|
+
droppedOnlyInFull: visible.observations.filter(
|
|
317
|
+
(observation) => !fullObservationIds.has(observation.id),
|
|
318
|
+
),
|
|
319
|
+
};
|
|
257
320
|
}
|