memorix 1.2.1 → 1.2.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/CHANGELOG.md +23 -0
- package/README.md +14 -2
- package/README.zh-CN.md +14 -2
- package/dist/cli/index.js +15424 -13780
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1337 -536
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8458 -8087
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +23 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +1365 -542
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
- package/docs/API_REFERENCE.md +24 -4
- package/docs/README.md +1 -1
- package/docs/dev-log/progress.txt +101 -11
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/cleanup.ts +5 -1
- package/src/cli/commands/codegraph.ts +15 -5
- package/src/cli/commands/context.ts +3 -2
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/explain.ts +9 -3
- package/src/cli/commands/handoff.ts +21 -7
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-image.ts +5 -3
- package/src/cli/commands/lock.ts +11 -10
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +19 -14
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +16 -6
- package/src/cli/commands/reasoning.ts +17 -3
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +8 -2
- package/src/cli/commands/session.ts +44 -10
- package/src/cli/commands/skills.ts +10 -5
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +26 -17
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +63 -10
- package/src/cli/identity.ts +89 -0
- package/src/cli/index.ts +96 -19
- package/src/cli/invocation.ts +115 -0
- package/src/cli/tui/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/session-service.ts +3 -2
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- package/src/codegraph/auto-context.ts +31 -2
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/project-context.ts +2 -0
- package/src/compact/engine.ts +26 -10
- package/src/compact/index-format.ts +25 -2
- package/src/dashboard/server.ts +46 -9
- package/src/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/workset.ts +179 -10
- package/src/memory/admission.ts +57 -0
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +6 -1
- package/src/memory/export-import.ts +11 -3
- package/src/memory/graph-context.ts +8 -2
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/retention.ts +22 -2
- package/src/memory/session.ts +29 -11
- package/src/memory/visibility.ts +80 -0
- package/src/orchestrate/memorix-bridge.ts +38 -0
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +18 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/maintenance-runner.ts +2 -0
- package/src/runtime/project-maintenance.ts +89 -0
- package/src/sdk.ts +35 -5
- package/src/server.ts +267 -83
- package/src/store/orama-store.ts +61 -6
- package/src/store/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/handoff.ts +7 -0
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
|
@@ -90,6 +90,14 @@ async function getSearchObservations() {
|
|
|
90
90
|
return _searchObservations;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function automaticActorId(...parts: string[]): string {
|
|
94
|
+
return `orchestrator:${createHash('sha256').update(parts.join('\u0000')).digest('hex').slice(0, 24)}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isEligibleAutomaticLesson(entry: { admissionState?: string }): boolean {
|
|
98
|
+
return entry.admissionState !== 'candidate' && entry.admissionState !== 'ephemeral';
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
// ── 5a. Fix Loop Memory (CORE — always on) ────────────────────────
|
|
94
102
|
|
|
95
103
|
/**
|
|
@@ -104,6 +112,7 @@ export function storeVerifiedFix(fix: FixResult): void {
|
|
|
104
112
|
|
|
105
113
|
const errorHash = hashErrorPattern(fix.errorOutput);
|
|
106
114
|
const confidence = fix.fixAttempt === 1 ? 'high' : fix.fixAttempt === 2 ? 'medium' : 'low';
|
|
115
|
+
const createdByAgentId = automaticActorId(fix.projectId, 'verified-fix', fix.gate, errorHash);
|
|
107
116
|
|
|
108
117
|
// Fire-and-forget — catches all errors silently
|
|
109
118
|
void (async () => {
|
|
@@ -126,6 +135,11 @@ export function storeVerifiedFix(fix: FixResult): void {
|
|
|
126
135
|
source: 'agent',
|
|
127
136
|
sourceDetail: 'hook',
|
|
128
137
|
valueCategory: confidence === 'high' ? 'core' : 'contextual',
|
|
138
|
+
admissionState: 'candidate',
|
|
139
|
+
admissionReason: 'verified orchestration fix awaits current Code Memory qualification',
|
|
140
|
+
visibility: 'personal',
|
|
141
|
+
createdByAgentId,
|
|
142
|
+
visibilityReader: { projectId: fix.projectId, agentId: createdByAgentId },
|
|
129
143
|
});
|
|
130
144
|
} catch { /* fire-and-forget */ }
|
|
131
145
|
})();
|
|
@@ -138,6 +152,7 @@ export function storeVerifiedFix(fix: FixResult): void {
|
|
|
138
152
|
*/
|
|
139
153
|
export function storeFixExhausted(fix: FixResult): void {
|
|
140
154
|
const errorHash = hashErrorPattern(fix.errorOutput);
|
|
155
|
+
const createdByAgentId = automaticActorId(fix.projectId, 'fix-exhausted', fix.gate, errorHash);
|
|
141
156
|
|
|
142
157
|
void (async () => {
|
|
143
158
|
try {
|
|
@@ -158,6 +173,11 @@ export function storeFixExhausted(fix: FixResult): void {
|
|
|
158
173
|
source: 'agent',
|
|
159
174
|
sourceDetail: 'hook',
|
|
160
175
|
valueCategory: 'contextual',
|
|
176
|
+
admissionState: 'candidate',
|
|
177
|
+
admissionReason: 'automatic orchestration failure awaits current Code Memory qualification',
|
|
178
|
+
visibility: 'personal',
|
|
179
|
+
createdByAgentId,
|
|
180
|
+
visibilityReader: { projectId: fix.projectId, agentId: createdByAgentId },
|
|
161
181
|
});
|
|
162
182
|
} catch { /* fire-and-forget */ }
|
|
163
183
|
})();
|
|
@@ -182,6 +202,7 @@ export async function searchKnownFixes(
|
|
|
182
202
|
search({
|
|
183
203
|
query: searchQuery,
|
|
184
204
|
projectId,
|
|
205
|
+
reader: { projectId },
|
|
185
206
|
type: 'problem-solution',
|
|
186
207
|
limit: 3,
|
|
187
208
|
status: 'active',
|
|
@@ -190,6 +211,7 @@ export async function searchKnownFixes(
|
|
|
190
211
|
]);
|
|
191
212
|
|
|
192
213
|
return (results as any[])
|
|
214
|
+
.filter(isEligibleAutomaticLesson)
|
|
193
215
|
.filter((r: any) => (r.score ?? 1) > 0.3) // Only reasonably relevant
|
|
194
216
|
.map((r: any) => ({
|
|
195
217
|
id: r.id ?? 0,
|
|
@@ -226,6 +248,7 @@ export async function searchLessons(
|
|
|
226
248
|
search({
|
|
227
249
|
query,
|
|
228
250
|
projectId,
|
|
251
|
+
reader: { projectId },
|
|
229
252
|
limit: config.maxLessons + 2, // Fetch extra for filtering
|
|
230
253
|
status: 'active',
|
|
231
254
|
}),
|
|
@@ -233,6 +256,7 @@ export async function searchLessons(
|
|
|
233
256
|
]);
|
|
234
257
|
|
|
235
258
|
const lessons = (results as any[])
|
|
259
|
+
.filter(isEligibleAutomaticLesson)
|
|
236
260
|
.filter((r: any) =>
|
|
237
261
|
(r.type === 'problem-solution' || r.type === 'gotcha') &&
|
|
238
262
|
(r.score ?? 1) > 0.5, // Only high-relevance (Safeguard 5: project isolation already handled by projectId filter)
|
|
@@ -282,6 +306,8 @@ export function storeTaskCompletion(opts: {
|
|
|
282
306
|
durationMs: number;
|
|
283
307
|
tailOutput: string;
|
|
284
308
|
}): void {
|
|
309
|
+
const createdByAgentId = automaticActorId(opts.projectId, opts.pipelineId, opts.taskId, opts.agentName);
|
|
310
|
+
|
|
285
311
|
void (async () => {
|
|
286
312
|
try {
|
|
287
313
|
const store = await getStoreObservation();
|
|
@@ -300,6 +326,11 @@ export function storeTaskCompletion(opts: {
|
|
|
300
326
|
source: 'agent',
|
|
301
327
|
sourceDetail: 'hook',
|
|
302
328
|
valueCategory: 'ephemeral',
|
|
329
|
+
admissionState: 'ephemeral',
|
|
330
|
+
admissionReason: 'pipeline completion retained only as a short-lived trace',
|
|
331
|
+
visibility: 'personal',
|
|
332
|
+
createdByAgentId,
|
|
333
|
+
visibilityReader: { projectId: opts.projectId, agentId: createdByAgentId },
|
|
303
334
|
});
|
|
304
335
|
} catch { /* fire-and-forget */ }
|
|
305
336
|
})();
|
|
@@ -318,6 +349,8 @@ export function storePipelineSummary(opts: {
|
|
|
318
349
|
failed: number;
|
|
319
350
|
elapsedMs: number;
|
|
320
351
|
}): void {
|
|
352
|
+
const createdByAgentId = automaticActorId(opts.projectId, opts.pipelineId, 'summary');
|
|
353
|
+
|
|
321
354
|
void (async () => {
|
|
322
355
|
try {
|
|
323
356
|
const store = await getStoreObservation();
|
|
@@ -338,6 +371,11 @@ export function storePipelineSummary(opts: {
|
|
|
338
371
|
source: 'agent',
|
|
339
372
|
sourceDetail: 'hook',
|
|
340
373
|
valueCategory: 'contextual',
|
|
374
|
+
admissionState: 'candidate',
|
|
375
|
+
admissionReason: 'automatic pipeline summary awaits current Code Memory qualification',
|
|
376
|
+
visibility: 'personal',
|
|
377
|
+
createdByAgentId,
|
|
378
|
+
visibilityReader: { projectId: opts.projectId, agentId: createdByAgentId },
|
|
341
379
|
});
|
|
342
380
|
} catch { /* fire-and-forget */ }
|
|
343
381
|
})();
|
package/src/runtime/lifecycle.ts
CHANGED
|
@@ -65,6 +65,24 @@ export function enqueueClaimDerivation(input: {
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Automatic captures are initially only candidates. Qualification runs in the
|
|
70
|
+
* durable maintenance lane after Code Memory has a chance to refresh.
|
|
71
|
+
*/
|
|
72
|
+
export function enqueueObservationQualification(input: {
|
|
73
|
+
dataDir: string;
|
|
74
|
+
projectId: string;
|
|
75
|
+
source: string;
|
|
76
|
+
queue?: MaintenanceQueue;
|
|
77
|
+
}): void {
|
|
78
|
+
queueFor(input).enqueue({
|
|
79
|
+
projectId: input.projectId,
|
|
80
|
+
kind: 'observation-qualify',
|
|
81
|
+
dedupeKey: 'observation-qualify',
|
|
82
|
+
payload: { source: input.source, limit: 100 },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
68
86
|
/**
|
|
69
87
|
* The follow-up jobs operate on the same workspace the Workset reads: a
|
|
70
88
|
* versioned workspace when present, otherwise the local workspace.
|
|
@@ -3,6 +3,7 @@ import { loadDotenv } from '../config/dotenv-loader.js';
|
|
|
3
3
|
import { initProjectRoot } from '../config/yaml-loader.js';
|
|
4
4
|
import { initLLM } from '../llm/provider.js';
|
|
5
5
|
import { initObservationStore } from '../store/obs-store.js';
|
|
6
|
+
import { initObservations } from '../memory/observations.js';
|
|
6
7
|
import {
|
|
7
8
|
MAINTENANCE_JOB_KINDS,
|
|
8
9
|
type MaintenanceJob,
|
|
@@ -58,6 +59,7 @@ export async function executeMaintenanceRequest(
|
|
|
58
59
|
initProjectRoot(request.projectRoot);
|
|
59
60
|
loadDotenv(request.projectRoot);
|
|
60
61
|
await initObservationStore(request.dataDir);
|
|
62
|
+
await initObservations(request.dataDir);
|
|
61
63
|
if (request.job.kind === 'consolidation') initLLM();
|
|
62
64
|
|
|
63
65
|
const handler = createProjectMaintenanceHandler(
|
|
@@ -6,6 +6,7 @@ import { runMaintenanceInChildProcess } from './isolated-maintenance.js';
|
|
|
6
6
|
import {
|
|
7
7
|
enqueueClaimRequalification,
|
|
8
8
|
enqueueKnowledgeFollowups,
|
|
9
|
+
enqueueObservationQualification,
|
|
9
10
|
type MaintenanceQueue,
|
|
10
11
|
} from './lifecycle.js';
|
|
11
12
|
|
|
@@ -14,6 +15,7 @@ const DEFAULT_RETENTION_BATCH_SIZE = 100;
|
|
|
14
15
|
const DEFAULT_CONSOLIDATION_BATCH_SIZE = 200;
|
|
15
16
|
const DEFAULT_CODEGRAPH_MAX_FILES = 5_000;
|
|
16
17
|
const DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
18
|
+
const DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
17
19
|
const VECTOR_RETRY_DELAY_MS = 5_000;
|
|
18
20
|
const DEDUP_PER_PAIR_TIMEOUT_MS = 5_000;
|
|
19
21
|
|
|
@@ -68,6 +70,19 @@ function claimDerivationCursor(payload: Record<string, unknown>): number {
|
|
|
68
70
|
: 0;
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
function observationQualificationBatchSize(payload: Record<string, unknown>): number {
|
|
74
|
+
const value = payload.limit;
|
|
75
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE;
|
|
76
|
+
return Math.min(500, Math.max(1, Math.floor(value)));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function observationQualificationCursor(payload: Record<string, unknown>): number {
|
|
80
|
+
const value = payload.cursor;
|
|
81
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
82
|
+
? Math.max(0, Math.floor(value))
|
|
83
|
+
: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
71
86
|
function observationId(payload: Record<string, unknown>): number | undefined {
|
|
72
87
|
const value = payload.observationId;
|
|
73
88
|
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0
|
|
@@ -275,6 +290,80 @@ export function createProjectMaintenanceHandler(
|
|
|
275
290
|
...(snapshot?.id ? { snapshotId: snapshot.id } : {}),
|
|
276
291
|
...(options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}),
|
|
277
292
|
});
|
|
293
|
+
enqueueObservationQualification({
|
|
294
|
+
projectId,
|
|
295
|
+
dataDir: projectDir,
|
|
296
|
+
source: 'codegraph-refresh',
|
|
297
|
+
...(options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}),
|
|
298
|
+
});
|
|
299
|
+
return { action: 'complete' };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (job.kind === 'observation-qualify') {
|
|
303
|
+
const [
|
|
304
|
+
{ CodeGraphStore },
|
|
305
|
+
{ bindObservationToCode },
|
|
306
|
+
{ getObservationStore },
|
|
307
|
+
{ qualifyCandidateFromCurrentCode },
|
|
308
|
+
{ updateObservationAdmission },
|
|
309
|
+
] = await Promise.all([
|
|
310
|
+
import('../codegraph/store.js'),
|
|
311
|
+
import('../codegraph/binder.js'),
|
|
312
|
+
import('../store/obs-store.js'),
|
|
313
|
+
import('../memory/admission.js'),
|
|
314
|
+
import('../memory/observations.js'),
|
|
315
|
+
]);
|
|
316
|
+
const codeStore = new CodeGraphStore();
|
|
317
|
+
await codeStore.init(projectDir);
|
|
318
|
+
const limit = observationQualificationBatchSize(job.payload);
|
|
319
|
+
const page = await getObservationStore().loadByProject(projectId, {
|
|
320
|
+
status: 'active',
|
|
321
|
+
afterId: observationQualificationCursor(job.payload),
|
|
322
|
+
limit: limit + 1,
|
|
323
|
+
});
|
|
324
|
+
const hasMore = page.length > limit;
|
|
325
|
+
const observations = hasMore ? page.slice(0, limit) : page;
|
|
326
|
+
const indexedAtMs = Date.parse(codeStore.status(projectId).indexedAt ?? '');
|
|
327
|
+
|
|
328
|
+
for (const observation of observations) {
|
|
329
|
+
if (observation.admissionState !== 'candidate') continue;
|
|
330
|
+
// A candidate may only earn delivery after a Code Memory scan that
|
|
331
|
+
// happened at or after it was captured. A stale snapshot is evidence
|
|
332
|
+
// about an earlier project state, not a reason to inject this record.
|
|
333
|
+
if (!Number.isFinite(indexedAtMs) || indexedAtMs < Date.parse(observation.createdAt)) continue;
|
|
334
|
+
await bindObservationToCode(codeStore, observation);
|
|
335
|
+
const currentCodeReferenceCount = codeStore
|
|
336
|
+
.listObservationRefs(projectId, observation.id)
|
|
337
|
+
.filter((reference) => reference.status === 'current')
|
|
338
|
+
.length;
|
|
339
|
+
const qualification = qualifyCandidateFromCurrentCode({
|
|
340
|
+
observation,
|
|
341
|
+
currentCodeReferenceCount,
|
|
342
|
+
});
|
|
343
|
+
if (!qualification) continue;
|
|
344
|
+
await updateObservationAdmission({
|
|
345
|
+
observationId: observation.id,
|
|
346
|
+
projectId,
|
|
347
|
+
expectedState: 'candidate',
|
|
348
|
+
// Automatic capture earns project visibility only after a current
|
|
349
|
+
// Code Memory link exists. Explicit/private records retain scope.
|
|
350
|
+
...(observation.sourceDetail === 'hook' ? { visibility: 'project' as const } : {}),
|
|
351
|
+
...qualification,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (hasMore && observations.length > 0) {
|
|
356
|
+
return {
|
|
357
|
+
action: 'reschedule',
|
|
358
|
+
delayMs: 0,
|
|
359
|
+
resetAttempts: true,
|
|
360
|
+
payload: {
|
|
361
|
+
...job.payload,
|
|
362
|
+
cursor: observations[observations.length - 1].id,
|
|
363
|
+
limit,
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
278
367
|
return { action: 'complete' };
|
|
279
368
|
}
|
|
280
369
|
|
package/src/sdk.ts
CHANGED
|
@@ -25,7 +25,13 @@ import type {
|
|
|
25
25
|
ProgressInfo,
|
|
26
26
|
ProjectInfo,
|
|
27
27
|
DetectionResult,
|
|
28
|
+
ObservationReader,
|
|
28
29
|
} from './types.js';
|
|
30
|
+
import {
|
|
31
|
+
canManageObservation,
|
|
32
|
+
canReadObservation,
|
|
33
|
+
filterReadableObservations,
|
|
34
|
+
} from './memory/visibility.js';
|
|
29
35
|
|
|
30
36
|
// ── Re-exports for convenience ──────────────────────────────────────
|
|
31
37
|
|
|
@@ -97,12 +103,17 @@ export interface ResolveResult {
|
|
|
97
103
|
* Memorix observations without MCP overhead.
|
|
98
104
|
*
|
|
99
105
|
* Each client initializes its own SQLite backend and Orama search index,
|
|
100
|
-
* scoped to a single project.
|
|
106
|
+
* scoped to a single project. It is an unbound project reader by default, so
|
|
107
|
+
* it exposes only project-shared records and cannot alter personal or team
|
|
108
|
+
* records. Use an identity-bound MCP session for private coordination data.
|
|
109
|
+
* Call `close()` when done to release resources.
|
|
101
110
|
*/
|
|
102
111
|
export class MemoryClient {
|
|
103
112
|
private _projectId: string;
|
|
104
113
|
private _projectRoot: string;
|
|
105
114
|
private _dataDir: string;
|
|
115
|
+
/** SDK calls are unbound by default, so they see only project-shared records. */
|
|
116
|
+
private _reader: ObservationReader;
|
|
106
117
|
private _closed = false;
|
|
107
118
|
|
|
108
119
|
// Internal module references — loaded lazily to avoid top-level side effects
|
|
@@ -116,6 +127,7 @@ export class MemoryClient {
|
|
|
116
127
|
this._projectId = projectId;
|
|
117
128
|
this._projectRoot = projectRoot;
|
|
118
129
|
this._dataDir = dataDir;
|
|
130
|
+
this._reader = { projectId };
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
/** The canonical project ID (derived from Git remote or local path) */
|
|
@@ -182,6 +194,7 @@ export class MemoryClient {
|
|
|
182
194
|
return this._observations.storeObservation({
|
|
183
195
|
...input,
|
|
184
196
|
projectId: this._projectId,
|
|
197
|
+
visibilityReader: this._reader,
|
|
185
198
|
});
|
|
186
199
|
}
|
|
187
200
|
|
|
@@ -207,6 +220,7 @@ export class MemoryClient {
|
|
|
207
220
|
type: options.type,
|
|
208
221
|
source: options.source,
|
|
209
222
|
status: options.status === 'all' ? undefined : (options.status ?? 'active'),
|
|
223
|
+
reader: this._reader,
|
|
210
224
|
};
|
|
211
225
|
|
|
212
226
|
return this._oramaStore.searchObservations(searchOpts);
|
|
@@ -218,7 +232,8 @@ export class MemoryClient {
|
|
|
218
232
|
async get(id: number): Promise<Observation | undefined> {
|
|
219
233
|
this._ensureOpen();
|
|
220
234
|
await this._freshness.withFreshIndex(() => {});
|
|
221
|
-
|
|
235
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
236
|
+
return observation && canReadObservation(observation, this._reader) ? observation : undefined;
|
|
222
237
|
}
|
|
223
238
|
|
|
224
239
|
/**
|
|
@@ -227,7 +242,10 @@ export class MemoryClient {
|
|
|
227
242
|
async getAll(): Promise<Observation[]> {
|
|
228
243
|
this._ensureOpen();
|
|
229
244
|
await this._freshness.withFreshIndex(() => {});
|
|
230
|
-
return
|
|
245
|
+
return filterReadableObservations(
|
|
246
|
+
this._observations.getProjectObservations(this._projectId),
|
|
247
|
+
this._reader,
|
|
248
|
+
);
|
|
231
249
|
}
|
|
232
250
|
|
|
233
251
|
/**
|
|
@@ -236,7 +254,10 @@ export class MemoryClient {
|
|
|
236
254
|
async count(): Promise<number> {
|
|
237
255
|
this._ensureOpen();
|
|
238
256
|
await this._freshness.withFreshIndex(() => {});
|
|
239
|
-
return
|
|
257
|
+
return filterReadableObservations(
|
|
258
|
+
this._observations.getProjectObservations(this._projectId),
|
|
259
|
+
this._reader,
|
|
260
|
+
).length;
|
|
240
261
|
}
|
|
241
262
|
|
|
242
263
|
/**
|
|
@@ -253,7 +274,16 @@ export class MemoryClient {
|
|
|
253
274
|
*/
|
|
254
275
|
async resolve(ids: number[], status: ObservationStatus = 'resolved'): Promise<ResolveResult> {
|
|
255
276
|
this._ensureOpen();
|
|
256
|
-
|
|
277
|
+
const manageableIds = ids.filter((id) => {
|
|
278
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
279
|
+
return observation && canManageObservation(observation, this._reader);
|
|
280
|
+
});
|
|
281
|
+
const deniedIds = ids.filter((id) => !manageableIds.includes(id));
|
|
282
|
+
const result = await this._observations.resolveObservations(manageableIds, status);
|
|
283
|
+
return {
|
|
284
|
+
resolved: result.resolved,
|
|
285
|
+
notFound: [...new Set([...result.notFound, ...deniedIds])],
|
|
286
|
+
};
|
|
257
287
|
}
|
|
258
288
|
|
|
259
289
|
/**
|