@shadowforge0/aquifer-memory 1.8.1 → 1.9.1
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/.env.example +1 -0
- package/README.md +82 -26
- package/README_CN.md +33 -23
- package/README_TW.md +25 -24
- package/aquifer.config.example.json +2 -1
- package/consumers/cli.js +587 -33
- package/consumers/codex-active-checkpoint.js +3 -1
- package/consumers/codex-current-memory.js +10 -6
- package/consumers/codex.js +6 -3
- package/consumers/default/daily-entries.js +2 -2
- package/consumers/default/index.js +40 -30
- package/consumers/default/prompts/summary.js +2 -2
- package/consumers/mcp.js +56 -46
- package/consumers/openclaw-ext/index.js +65 -7
- package/consumers/openclaw-ext/openclaw.plugin.json +1 -1
- package/consumers/openclaw-ext/package.json +1 -1
- package/consumers/openclaw-install.js +326 -0
- package/consumers/openclaw-plugin.js +105 -24
- package/consumers/shared/compat-recall.js +101 -0
- package/consumers/shared/config.js +2 -0
- package/consumers/shared/openclaw-product-tools.js +130 -0
- package/consumers/shared/recall-format.js +2 -2
- package/core/aquifer.js +553 -41
- package/core/backends/local.js +169 -1
- package/core/doctor.js +924 -0
- package/core/finalization-inspector.js +164 -0
- package/core/finalization-review.js +88 -42
- package/core/interface.js +629 -0
- package/core/mcp-manifest.js +11 -3
- package/core/memory-bootstrap.js +25 -27
- package/core/memory-consolidation.js +564 -42
- package/core/memory-explain.js +593 -0
- package/core/memory-promotion.js +392 -55
- package/core/memory-recall.js +75 -71
- package/core/memory-records.js +107 -108
- package/core/memory-review.js +891 -0
- package/core/memory-serving.js +61 -4
- package/core/memory-type-policy.js +298 -0
- package/core/operator-observability.js +249 -0
- package/core/postgres-migrations.js +22 -0
- package/core/session-checkpoint-producer.js +3 -1
- package/core/session-checkpoints.js +1 -1
- package/core/session-finalization.js +78 -3
- package/core/storage.js +124 -8
- package/docs/getting-started.md +50 -4
- package/docs/setup.md +163 -24
- package/package.json +5 -4
- package/schema/004-completion.sql +4 -4
- package/schema/010-v1-finalization-review.sql +72 -0
- package/schema/019-v1-memory-review-resolutions.sql +53 -0
- package/schema/020-v1-assistant-shaping-memory.sql +30 -0
- package/scripts/backfill-canonical-key.js +1 -1
- package/scripts/codex-checkpoint-commands.js +28 -0
- package/scripts/codex-checkpoint-runtime.js +109 -0
- package/scripts/codex-recovery.js +16 -4
- package/scripts/diagnose-fts-zh.js +1 -1
- package/scripts/extract-insights-from-recent-sessions.js +4 -4
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function formatDate(value, fallback = '?') {
|
|
4
|
+
if (!value) return fallback;
|
|
5
|
+
const parsed = new Date(value);
|
|
6
|
+
return isNaN(parsed.getTime()) ? fallback : parsed.toISOString().slice(0, 10);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function compactRows(rows = []) {
|
|
10
|
+
return rows.filter(row => row && row.value !== undefined && row.value !== null && row.value !== '');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function renderValue(value) {
|
|
14
|
+
if (Array.isArray(value)) return value.join(', ');
|
|
15
|
+
return String(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function renderRow(row, indent = '') {
|
|
19
|
+
if (!row.label) return `${indent}${renderValue(row.value)}`;
|
|
20
|
+
return `${indent}${row.label}: ${renderValue(row.value)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function arrayPath(value) {
|
|
24
|
+
return Array.isArray(value) ? value.filter(Boolean) : [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hasProjectScope(stats = {}) {
|
|
28
|
+
const path = arrayPath(stats.serving?.activeScopePath);
|
|
29
|
+
if (path.length > 1) return true;
|
|
30
|
+
const key = stats.serving?.activeScopeKey;
|
|
31
|
+
return !!(key && key !== 'global');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function hasSavedContent(stats = {}) {
|
|
35
|
+
if (stats.backendKind === 'local' || stats.backendProfile === 'starter' || stats.degraded) {
|
|
36
|
+
return (stats.sessionTotal || 0) > 0;
|
|
37
|
+
}
|
|
38
|
+
return (stats.sessionTotal || 0) > 0 || (stats.summaries || 0) > 0 || (stats.turnEmbeddings || 0) > 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildReadinessProductStatus(stats = {}) {
|
|
42
|
+
const starter = stats.backendKind === 'local' || stats.backendProfile === 'starter' || stats.degraded;
|
|
43
|
+
const savedContent = hasSavedContent(stats);
|
|
44
|
+
const pendingTotal = stats.pendingSessions?.total || 0;
|
|
45
|
+
const fullMemorySupported = !starter && stats.memoryRecords?.available !== false;
|
|
46
|
+
const fullMemoryEnabled = fullMemorySupported && (stats.serving?.mode || 'legacy') === 'curated';
|
|
47
|
+
const fullMemoryLinked = fullMemoryEnabled && hasProjectScope(stats);
|
|
48
|
+
const activeFullMemoryRows = fullMemorySupported ? (stats.memoryRecords?.active || 0) : 0;
|
|
49
|
+
const fullMemoryReady = fullMemoryLinked && activeFullMemoryRows > 0;
|
|
50
|
+
const fullMemoryEmpty = fullMemoryLinked && activeFullMemoryRows === 0;
|
|
51
|
+
|
|
52
|
+
let code = 'online_memory_ready';
|
|
53
|
+
let level = 'ready';
|
|
54
|
+
let headline = 'Online. Memory is ready.';
|
|
55
|
+
const available = ['Aquifer is online'];
|
|
56
|
+
const attention = [];
|
|
57
|
+
let action = 'No action required.';
|
|
58
|
+
|
|
59
|
+
if (starter) {
|
|
60
|
+
if (savedContent) {
|
|
61
|
+
code = 'online_starter_saved_content';
|
|
62
|
+
level = 'limited';
|
|
63
|
+
headline = 'Online. Saved local content is available. Complete memory requires full storage.';
|
|
64
|
+
available.push('Saved local content is available');
|
|
65
|
+
attention.push('Complete memory requires full storage.');
|
|
66
|
+
action = 'Use full storage when complete memory is needed.';
|
|
67
|
+
} else {
|
|
68
|
+
code = 'online_starter_empty';
|
|
69
|
+
level = 'empty';
|
|
70
|
+
headline = 'Online. No saved content is available yet.';
|
|
71
|
+
attention.push('Save content, or use full storage for complete memory.');
|
|
72
|
+
action = 'Save content to start building memory.';
|
|
73
|
+
}
|
|
74
|
+
} else if (!savedContent && !fullMemoryReady) {
|
|
75
|
+
code = 'online_empty';
|
|
76
|
+
level = 'empty';
|
|
77
|
+
headline = 'Online. No saved content is available yet.';
|
|
78
|
+
attention.push('No saved content is available yet.');
|
|
79
|
+
action = 'Save content to start building memory.';
|
|
80
|
+
} else {
|
|
81
|
+
if (savedContent) available.push('Saved content is available');
|
|
82
|
+
if (fullMemoryReady) available.push('Memory is available');
|
|
83
|
+
|
|
84
|
+
if (!fullMemoryEnabled) {
|
|
85
|
+
code = savedContent ? 'online_saved_content_memory_preparing' : 'online_memory_preparing';
|
|
86
|
+
level = 'limited';
|
|
87
|
+
headline = savedContent
|
|
88
|
+
? 'Online. Saved content is available. Memory is still getting ready.'
|
|
89
|
+
: 'Online. Memory is still getting ready.';
|
|
90
|
+
attention.push('Memory is still getting ready.');
|
|
91
|
+
action = 'Finish preparing memory.';
|
|
92
|
+
} else if (!fullMemoryLinked) {
|
|
93
|
+
code = 'online_saved_content_memory_not_linked';
|
|
94
|
+
level = 'limited';
|
|
95
|
+
headline = 'Online. Saved content is available. Memory is not linked to this app yet.';
|
|
96
|
+
attention.push('Memory is not linked to this app yet.');
|
|
97
|
+
action = 'Link this app to memory.';
|
|
98
|
+
} else if (fullMemoryEmpty) {
|
|
99
|
+
code = 'online_saved_content_memory_empty';
|
|
100
|
+
level = 'limited';
|
|
101
|
+
headline = 'Online. Saved content is available. Memory is not available yet.';
|
|
102
|
+
attention.push('No memory is available yet.');
|
|
103
|
+
action = 'Finish preparing memory.';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (fullMemoryReady) {
|
|
107
|
+
code = pendingTotal > 0 ? 'online_memory_with_saved_content_preparing' : 'online_memory_ready';
|
|
108
|
+
level = pendingTotal > 0 ? 'limited' : 'ready';
|
|
109
|
+
headline = pendingTotal > 0
|
|
110
|
+
? 'Online. Memory is ready. Some saved content is still being prepared.'
|
|
111
|
+
: 'Online. Memory is ready.';
|
|
112
|
+
if (pendingTotal > 0) {
|
|
113
|
+
attention.push('Some saved content is still being prepared.');
|
|
114
|
+
action = 'Let Aquifer finish preparing saved content.';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const capabilities = {
|
|
120
|
+
service: 'online',
|
|
121
|
+
savedContent: savedContent ? (starter ? 'local_available' : 'available') : 'empty',
|
|
122
|
+
memory: starter
|
|
123
|
+
? 'requires_full_storage'
|
|
124
|
+
: fullMemoryReady
|
|
125
|
+
? 'available'
|
|
126
|
+
: !fullMemoryEnabled
|
|
127
|
+
? 'preparing'
|
|
128
|
+
: !fullMemoryLinked
|
|
129
|
+
? 'not_linked'
|
|
130
|
+
: fullMemoryEmpty
|
|
131
|
+
? 'empty'
|
|
132
|
+
: 'preparing',
|
|
133
|
+
savedContentPreparation: pendingTotal > 0 ? 'preparing' : 'ready',
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
kind: 'aquifer_status',
|
|
138
|
+
code,
|
|
139
|
+
level,
|
|
140
|
+
headline,
|
|
141
|
+
available,
|
|
142
|
+
attention: [...new Set(attention)],
|
|
143
|
+
action,
|
|
144
|
+
capabilities,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildReadinessSurface(stats = {}, context = {}) {
|
|
149
|
+
const productStatus = buildReadinessProductStatus(stats);
|
|
150
|
+
const nextActions = productStatus.action === 'No action required.'
|
|
151
|
+
? []
|
|
152
|
+
: [productStatus.action];
|
|
153
|
+
return {
|
|
154
|
+
status: productStatus.level === 'ready'
|
|
155
|
+
? 'ready'
|
|
156
|
+
: productStatus.level === 'empty'
|
|
157
|
+
? 'empty'
|
|
158
|
+
: 'partial',
|
|
159
|
+
summary: productStatus.headline,
|
|
160
|
+
answer: productStatus.headline,
|
|
161
|
+
works: productStatus.available.slice(),
|
|
162
|
+
needs: productStatus.attention.slice(),
|
|
163
|
+
nextStep: productStatus.action,
|
|
164
|
+
checks: Array.isArray(context.checks) ? context.checks : [],
|
|
165
|
+
nextActions,
|
|
166
|
+
productStatus,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function numericCount(value) {
|
|
171
|
+
const parsed = Number(value);
|
|
172
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function buildBacklogProductStatus(input = {}) {
|
|
176
|
+
const statusCounts = input.statusCounts && typeof input.statusCounts === 'object'
|
|
177
|
+
? input.statusCounts
|
|
178
|
+
: {};
|
|
179
|
+
const pending = numericCount(statusCounts.pending ?? input.pending);
|
|
180
|
+
const failed = numericCount(statusCounts.failed ?? input.failed);
|
|
181
|
+
const explicitTotal = input.total === undefined ? null : numericCount(input.total);
|
|
182
|
+
const total = explicitTotal === null ? pending + failed : explicitTotal;
|
|
183
|
+
|
|
184
|
+
let code = 'clear';
|
|
185
|
+
let level = 'ready';
|
|
186
|
+
let headline = 'All saved content is ready.';
|
|
187
|
+
let action = 'No action required.';
|
|
188
|
+
|
|
189
|
+
if (total > 0 && pending > 0 && failed > 0) {
|
|
190
|
+
code = 'mixed_attention';
|
|
191
|
+
level = 'attention';
|
|
192
|
+
headline = 'Some saved content is still being prepared.';
|
|
193
|
+
action = 'Finish preparing saved content, then review items with errors.';
|
|
194
|
+
} else if (total > 0 && failed > 0) {
|
|
195
|
+
code = 'needs_review';
|
|
196
|
+
level = 'attention';
|
|
197
|
+
headline = 'Some saved content needs review.';
|
|
198
|
+
action = 'Review items with errors before retrying or closing them.';
|
|
199
|
+
} else if (total > 0) {
|
|
200
|
+
code = 'recoverable';
|
|
201
|
+
level = 'limited';
|
|
202
|
+
headline = 'Some saved content is still being prepared.';
|
|
203
|
+
action = 'Let Aquifer finish preparing saved content.';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
kind: 'saved_content_status',
|
|
208
|
+
code,
|
|
209
|
+
level,
|
|
210
|
+
headline,
|
|
211
|
+
action,
|
|
212
|
+
counts: {
|
|
213
|
+
total,
|
|
214
|
+
pending,
|
|
215
|
+
failed,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function renderPresentation(presentation = {}) {
|
|
221
|
+
const lines = [];
|
|
222
|
+
for (const row of compactRows(presentation.summaryRows || [])) {
|
|
223
|
+
lines.push(renderRow(row));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const warning of presentation.warnings || []) {
|
|
227
|
+
if (!warning.diagnosticsOnly) lines.push(`Warning: ${warning.text}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
for (const section of presentation.diagnosticSections || []) {
|
|
231
|
+
const rows = compactRows(section.rows || []);
|
|
232
|
+
if (rows.length === 0) continue;
|
|
233
|
+
if (lines.length > 0) lines.push('');
|
|
234
|
+
lines.push(`${section.title}:`);
|
|
235
|
+
for (const row of rows) {
|
|
236
|
+
lines.push(renderRow(row, section.indentRows === false ? '' : ' '));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (const warning of presentation.warnings || []) {
|
|
241
|
+
if (warning.diagnosticsOnly) lines.push(`Warning: ${warning.text}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return lines.join('\n');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function readinessAction(readiness = {}) {
|
|
248
|
+
if (readiness.nextStep) return readiness.nextStep;
|
|
249
|
+
if (Array.isArray(readiness.nextActions) && readiness.nextActions.length > 0) {
|
|
250
|
+
return readiness.nextActions.slice(0, 2).join(' ');
|
|
251
|
+
}
|
|
252
|
+
return 'No action required.';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildStatsPresentation(stats = {}, opts = {}) {
|
|
256
|
+
const diagnostics = opts.diagnostics === true;
|
|
257
|
+
const readiness = stats.readiness?.productStatus
|
|
258
|
+
? stats.readiness
|
|
259
|
+
: buildReadinessSurface(stats, { checks: stats.readiness?.checks || [] });
|
|
260
|
+
const productStatus = readiness.productStatus || buildReadinessProductStatus(stats);
|
|
261
|
+
const presentation = {
|
|
262
|
+
kind: 'memory_stats',
|
|
263
|
+
summaryRows: compactRows([
|
|
264
|
+
{
|
|
265
|
+
id: 'status',
|
|
266
|
+
label: 'Aquifer status',
|
|
267
|
+
value: productStatus.headline || readiness.summary || readiness.answer || 'unavailable.',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'available',
|
|
271
|
+
label: 'Available',
|
|
272
|
+
value: Array.isArray(productStatus.available) && productStatus.available.length > 0 ? productStatus.available : null,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'attention',
|
|
276
|
+
label: 'Attention',
|
|
277
|
+
value: Array.isArray(productStatus.attention) && productStatus.attention.length > 0 ? productStatus.attention : null,
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: 'action',
|
|
281
|
+
label: 'Action',
|
|
282
|
+
value: productStatus.action || readinessAction(readiness),
|
|
283
|
+
},
|
|
284
|
+
]),
|
|
285
|
+
diagnosticSections: [],
|
|
286
|
+
warnings: [],
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
if (!diagnostics) return presentation;
|
|
290
|
+
|
|
291
|
+
const sessionRows = compactRows([
|
|
292
|
+
{ id: 'backend', label: 'Backend', value: `${stats.backendKind || 'unknown'} (${stats.backendProfile || 'unknown'})` },
|
|
293
|
+
{ id: 'serving_mode', label: 'Serving mode', value: stats.serving?.mode || 'legacy' },
|
|
294
|
+
{ id: 'active_scope', label: 'Active scope', value: stats.serving?.activeScopePath?.join(' > ') || stats.serving?.activeScopeKey || 'none' },
|
|
295
|
+
{ id: 'sessions', label: 'Sessions', value: `${stats.sessionTotal ?? 0}${formatSessionStatuses(stats.sessions || {})}` },
|
|
296
|
+
stats.pendingSessions?.available
|
|
297
|
+
? { id: 'actionable_pending', label: 'Actionable pending/failed', value: stats.pendingSessions.total ?? 0 }
|
|
298
|
+
: null,
|
|
299
|
+
{ id: 'summaries', label: 'Summaries', value: stats.summaries ?? 0 },
|
|
300
|
+
{ id: 'turn_embeddings', label: 'Turn embeddings', value: stats.turnEmbeddings ?? 0 },
|
|
301
|
+
{ id: 'entities', label: 'Entities', value: stats.entities ?? 0 },
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
if (stats.memoryRecords) {
|
|
305
|
+
sessionRows.push(
|
|
306
|
+
{
|
|
307
|
+
id: 'memory_records',
|
|
308
|
+
label: 'Memory records',
|
|
309
|
+
value: `${stats.memoryRecords.total ?? 0} (${stats.memoryRecords.active ?? 0} active, ${stats.memoryRecords.visibleInRecall ?? 0} recall-visible, ${stats.memoryRecords.visibleInBootstrap ?? 0} bootstrap-visible)`,
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
if (stats.memoryRecords.latest) {
|
|
313
|
+
sessionRows.push({
|
|
314
|
+
id: 'memory_record_range',
|
|
315
|
+
label: 'Memory record range',
|
|
316
|
+
value: `${formatDate(stats.memoryRecords.earliest)}..${formatDate(stats.memoryRecords.latest)}`,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (stats.sessionFinalizations?.available) {
|
|
322
|
+
const statusText = Object.entries(stats.sessionFinalizations.statuses || {})
|
|
323
|
+
.map(([status, count]) => `${status}: ${count}`)
|
|
324
|
+
.join(', ') || 'none';
|
|
325
|
+
sessionRows.push({
|
|
326
|
+
id: 'session_finalizations',
|
|
327
|
+
label: 'Session finalizations',
|
|
328
|
+
value: `${stats.sessionFinalizations.total ?? 0} (${statusText})`,
|
|
329
|
+
});
|
|
330
|
+
if (stats.sessionFinalizations.latestFinalizedAt) {
|
|
331
|
+
sessionRows.push({
|
|
332
|
+
id: 'latest_finalization',
|
|
333
|
+
label: 'Latest finalization',
|
|
334
|
+
value: formatDate(stats.sessionFinalizations.latestFinalizedAt),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (stats.earliest) {
|
|
340
|
+
sessionRows.push({
|
|
341
|
+
id: 'range',
|
|
342
|
+
label: 'Range',
|
|
343
|
+
value: `${formatDate(stats.earliest)}..${formatDate(stats.latest)}`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
presentation.diagnosticSections.push({
|
|
348
|
+
id: 'storage',
|
|
349
|
+
title: 'Diagnostics',
|
|
350
|
+
rows: sessionRows,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
if ((stats.serving?.mode || 'legacy') !== 'curated') {
|
|
354
|
+
presentation.warnings.push({
|
|
355
|
+
id: 'legacy_serving',
|
|
356
|
+
diagnosticsOnly: true,
|
|
357
|
+
text: 'legacy serving returns session/evidence material; configure curated serving with an active scope for current-memory status.',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return presentation;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function buildStatsEnvelope(stats = {}, opts = {}) {
|
|
365
|
+
const diagnostics = opts.diagnostics === true;
|
|
366
|
+
const readiness = stats.readiness?.productStatus
|
|
367
|
+
? stats.readiness
|
|
368
|
+
: buildReadinessSurface(stats, { checks: stats.readiness?.checks || [] });
|
|
369
|
+
const productStatus = readiness.productStatus || buildReadinessProductStatus(stats);
|
|
370
|
+
const presentation = buildStatsPresentation(stats, { diagnostics });
|
|
371
|
+
const envelope = {
|
|
372
|
+
kind: 'memory_stats',
|
|
373
|
+
productStatus,
|
|
374
|
+
readiness: {
|
|
375
|
+
status: readiness.status,
|
|
376
|
+
summary: readiness.summary,
|
|
377
|
+
answer: readiness.answer,
|
|
378
|
+
works: Array.isArray(readiness.works) ? readiness.works.slice() : [],
|
|
379
|
+
needs: Array.isArray(readiness.needs) ? readiness.needs.slice() : [],
|
|
380
|
+
nextStep: readiness.nextStep || readinessAction(readiness),
|
|
381
|
+
nextActions: Array.isArray(readiness.nextActions) ? readiness.nextActions.slice() : [],
|
|
382
|
+
productStatus,
|
|
383
|
+
},
|
|
384
|
+
summaryRows: presentation.summaryRows,
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
if (diagnostics) {
|
|
388
|
+
envelope.diagnostics = {
|
|
389
|
+
raw: stats,
|
|
390
|
+
sections: presentation.diagnosticSections,
|
|
391
|
+
warnings: presentation.warnings,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return envelope;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function formatSessionStatuses(statuses = {}) {
|
|
399
|
+
const entries = Object.entries(statuses);
|
|
400
|
+
if (entries.length === 0) return '';
|
|
401
|
+
return ` (${entries.map(([status, count]) => `${status}: ${count}`).join(', ')})`;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function pendingSummary(report = {}) {
|
|
405
|
+
if (report.publicStatus?.headline) return report.publicStatus.headline;
|
|
406
|
+
if (report.summary) return report.summary;
|
|
407
|
+
return buildBacklogProductStatus(report).headline;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function pendingAction(report = {}) {
|
|
411
|
+
if (report.publicStatus?.action) return report.publicStatus.action;
|
|
412
|
+
if (report.nextStep) return report.nextStep;
|
|
413
|
+
return buildBacklogProductStatus(report).action;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function planRow(plan = {}) {
|
|
417
|
+
if (!plan.action || plan.action === 'inspect') return null;
|
|
418
|
+
return {
|
|
419
|
+
id: 'plan',
|
|
420
|
+
label: 'Plan',
|
|
421
|
+
value: `${plan.action} allowed=${plan.allowed ?? 0} blocked=${plan.blocked ?? 0} dry-run`,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function buildBacklogPresentation(report = {}, opts = {}) {
|
|
426
|
+
const diagnostics = opts.diagnostics === true;
|
|
427
|
+
const includePlan = opts.includePlan === true;
|
|
428
|
+
const presentation = {
|
|
429
|
+
kind: 'memory_pending',
|
|
430
|
+
summaryRows: compactRows([
|
|
431
|
+
{ id: 'status', label: 'Saved content status', value: pendingSummary(report) },
|
|
432
|
+
{ id: 'action', label: 'Action', value: pendingAction(report) },
|
|
433
|
+
includePlan ? planRow(report.plan || {}) : null,
|
|
434
|
+
]),
|
|
435
|
+
diagnosticSections: [],
|
|
436
|
+
warnings: [],
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
if (!diagnostics) return presentation;
|
|
440
|
+
|
|
441
|
+
const detailRows = [
|
|
442
|
+
{ id: 'actionable_backlog', label: 'Actionable backlog', value: report.total ?? 0 },
|
|
443
|
+
];
|
|
444
|
+
const filters = report.filters || {};
|
|
445
|
+
if (filters.source || filters.agentId || filters.statuses?.length) {
|
|
446
|
+
detailRows.push({
|
|
447
|
+
id: 'filters',
|
|
448
|
+
label: 'Filters',
|
|
449
|
+
value: `source=${filters.source || '*'} agent=${filters.agentId || '*'} status=${(filters.statuses || []).join(',') || '*'}`,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
presentation.diagnosticSections.push({
|
|
453
|
+
id: 'backlog',
|
|
454
|
+
title: 'Diagnostics',
|
|
455
|
+
rows: detailRows,
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
if (report.terminalFiltering === false) {
|
|
459
|
+
presentation.warnings.push({
|
|
460
|
+
id: 'terminal_filtering_unavailable',
|
|
461
|
+
diagnosticsOnly: true,
|
|
462
|
+
text: 'session_finalizations table is unavailable; terminal finalization filtering was not applied.',
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const groups = Array.isArray(report.groups) ? report.groups : [];
|
|
467
|
+
if (groups.length > 0) {
|
|
468
|
+
presentation.diagnosticSections.push({
|
|
469
|
+
id: 'buckets',
|
|
470
|
+
title: 'Buckets',
|
|
471
|
+
rows: groups.map(group => ({
|
|
472
|
+
id: `${group.source}:${group.agentId}:${group.status}`,
|
|
473
|
+
value: `${group.source}/${group.agentId} [${group.status}] count=${group.count} range=${formatDate(group.earliest)}..${formatDate(group.latest)} users=${group.userCount} messages=${group.msgCount}`,
|
|
474
|
+
})),
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const guidance = Array.isArray(report.guidance) ? report.guidance : [];
|
|
479
|
+
if (guidance.length > 0) {
|
|
480
|
+
presentation.diagnosticSections.push({
|
|
481
|
+
id: 'guidance',
|
|
482
|
+
title: 'Guidance',
|
|
483
|
+
rows: guidance.map(item => ({
|
|
484
|
+
id: item.status,
|
|
485
|
+
value: `${item.status}: prefer ${item.recommendedAction}; ${item.reason}`,
|
|
486
|
+
})),
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const samples = Array.isArray(report.samples) ? report.samples : [];
|
|
491
|
+
if (samples.length > 0) {
|
|
492
|
+
presentation.diagnosticSections.push({
|
|
493
|
+
id: 'samples',
|
|
494
|
+
title: 'Samples',
|
|
495
|
+
rows: samples.slice(0, 10).map(row => {
|
|
496
|
+
const suffix = row.processingError ? ` error=${String(row.processingError).slice(0, 80)}` : '';
|
|
497
|
+
return {
|
|
498
|
+
id: row.sessionId,
|
|
499
|
+
value: `${row.sessionId} source=${row.source} agent=${row.agentId} status=${row.status} started=${formatDate(row.startedAt)}${suffix}`,
|
|
500
|
+
};
|
|
501
|
+
}),
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (includePlan && report.plan?.action && report.plan.action !== 'inspect') {
|
|
506
|
+
const rows = (report.plan.notes || []).map((note, index) => ({
|
|
507
|
+
id: `note_${index}`,
|
|
508
|
+
value: note,
|
|
509
|
+
}));
|
|
510
|
+
if (rows.length > 0) {
|
|
511
|
+
presentation.diagnosticSections.push({
|
|
512
|
+
id: 'plan_notes',
|
|
513
|
+
title: 'Plan notes',
|
|
514
|
+
rows,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return presentation;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function buildBacklogEnvelope(report = {}, opts = {}) {
|
|
523
|
+
const diagnostics = opts.diagnostics === true;
|
|
524
|
+
const includePlan = opts.includePlan === true;
|
|
525
|
+
const publicStatus = report.publicStatus || buildBacklogProductStatus(report);
|
|
526
|
+
const presentation = buildBacklogPresentation(report, { diagnostics, includePlan });
|
|
527
|
+
const envelope = {
|
|
528
|
+
kind: 'memory_pending',
|
|
529
|
+
publicStatus,
|
|
530
|
+
productStatus: publicStatus,
|
|
531
|
+
summaryRows: presentation.summaryRows,
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
if (includePlan && report.plan?.action && report.plan.action !== 'inspect') {
|
|
535
|
+
envelope.plan = {
|
|
536
|
+
action: report.plan.action,
|
|
537
|
+
dryRunOnly: report.plan.dryRunOnly !== false,
|
|
538
|
+
allowed: report.plan.allowed ?? 0,
|
|
539
|
+
blocked: report.plan.blocked ?? 0,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (diagnostics) {
|
|
544
|
+
envelope.diagnostics = {
|
|
545
|
+
raw: report,
|
|
546
|
+
sections: presentation.diagnosticSections,
|
|
547
|
+
warnings: presentation.warnings,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return envelope;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function buildPendingRowsPresentation(rows = [], opts = {}) {
|
|
555
|
+
const diagnostics = opts.diagnostics === true;
|
|
556
|
+
const total = rows.length;
|
|
557
|
+
const statusCounts = {};
|
|
558
|
+
for (const row of rows) {
|
|
559
|
+
const status = row?.processing_status;
|
|
560
|
+
if (!status) continue;
|
|
561
|
+
statusCounts[status] = (statusCounts[status] || 0) + 1;
|
|
562
|
+
}
|
|
563
|
+
const publicStatus = buildBacklogProductStatus({ total, statusCounts });
|
|
564
|
+
const presentation = {
|
|
565
|
+
kind: 'memory_pending',
|
|
566
|
+
summaryRows: [
|
|
567
|
+
{
|
|
568
|
+
id: 'status',
|
|
569
|
+
label: 'Saved content status',
|
|
570
|
+
value: publicStatus.headline,
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: 'action',
|
|
574
|
+
label: 'Action',
|
|
575
|
+
value: publicStatus.action,
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
diagnosticSections: [],
|
|
579
|
+
warnings: [],
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
if (!diagnostics) return presentation;
|
|
583
|
+
|
|
584
|
+
presentation.diagnosticSections.push({
|
|
585
|
+
id: 'backlog',
|
|
586
|
+
title: 'Diagnostics',
|
|
587
|
+
rows: [{ id: 'actionable_backlog', label: 'Actionable backlog', value: total }],
|
|
588
|
+
});
|
|
589
|
+
if (total > 0) {
|
|
590
|
+
presentation.diagnosticSections.push({
|
|
591
|
+
id: 'samples',
|
|
592
|
+
title: 'Samples',
|
|
593
|
+
rows: rows.slice(0, 10).map(row => ({
|
|
594
|
+
id: row.session_id,
|
|
595
|
+
value: `${row.session_id} source=${row.source || 'api'} agent=${row.agent_id} status=${row.processing_status}`,
|
|
596
|
+
})),
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return presentation;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function formatMemoryStatsInterface(stats = {}, opts = {}) {
|
|
604
|
+
return renderPresentation(buildStatsPresentation(stats, opts));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function formatPendingWorkInterface(report = {}, opts = {}) {
|
|
608
|
+
return renderPresentation(buildBacklogPresentation(report, opts));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function formatPendingRowsInterface(rows = [], opts = {}) {
|
|
612
|
+
return renderPresentation(buildPendingRowsPresentation(rows, opts));
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
module.exports = {
|
|
616
|
+
buildBacklogEnvelope,
|
|
617
|
+
buildBacklogPresentation,
|
|
618
|
+
buildBacklogProductStatus,
|
|
619
|
+
buildPendingRowsPresentation,
|
|
620
|
+
buildReadinessProductStatus,
|
|
621
|
+
buildReadinessSurface,
|
|
622
|
+
buildStatsEnvelope,
|
|
623
|
+
buildStatsPresentation,
|
|
624
|
+
formatDate,
|
|
625
|
+
formatMemoryStatsInterface,
|
|
626
|
+
formatPendingRowsInterface,
|
|
627
|
+
formatPendingWorkInterface,
|
|
628
|
+
renderPresentation,
|
|
629
|
+
};
|
package/core/mcp-manifest.js
CHANGED
|
@@ -137,6 +137,8 @@ const MCP_TOOL_MANIFEST = Object.freeze([
|
|
|
137
137
|
source: { type: 'string', description: 'Filter by source (e.g., gateway, cc)' },
|
|
138
138
|
dateFrom: { type: 'string', description: 'Start date YYYY-MM-DD' },
|
|
139
139
|
dateTo: { type: 'string', description: 'End date YYYY-MM-DD' },
|
|
140
|
+
host: { type: 'string', description: 'Audit boundary host filter' },
|
|
141
|
+
sessionId: { type: 'string', description: 'Audit boundary session ID filter' },
|
|
140
142
|
entities: { type: 'array', items: { type: 'string' }, description: 'Entity names to match' },
|
|
141
143
|
entityMode: { type: 'string', enum: ['any', 'all'], description: '"any" (default, boost) or "all" hard filter' },
|
|
142
144
|
mode: { type: 'string', enum: ['fts', 'hybrid', 'vector'], description: 'Legacy evidence recall mode' },
|
|
@@ -179,21 +181,27 @@ const MCP_TOOL_MANIFEST = Object.freeze([
|
|
|
179
181
|
},
|
|
180
182
|
{
|
|
181
183
|
name: 'memory_stats',
|
|
182
|
-
description: 'Return
|
|
184
|
+
description: 'Return current operational Aquifer product status. For user-facing status/readiness/health requests, treat this tool result as the answer boundary: report the service state, availability, attention, and action. Do not merge repository, package, release, local-development, project-ledger, log, source-search, or historical-memory detail unless the user explicitly asks for diagnostic/development detail. Set diagnostics=true for raw storage counters.',
|
|
183
185
|
inputSchema: {
|
|
184
186
|
type: 'object',
|
|
185
187
|
additionalProperties: false,
|
|
186
|
-
properties: {
|
|
188
|
+
properties: {
|
|
189
|
+
diagnostics: { type: 'boolean', description: 'Include raw storage counters and serving diagnostics. Default false.' },
|
|
190
|
+
},
|
|
187
191
|
},
|
|
188
192
|
},
|
|
189
193
|
{
|
|
190
194
|
name: 'memory_pending',
|
|
191
|
-
description: '
|
|
195
|
+
description: 'Return current saved-content preparation status. Use this as the operational backlog/readiness surface after product status when the user needs preparation state or review action. Do not merge repository, package, release, local-development, project-ledger, log, source-search, or historical-memory detail unless the user explicitly asks for diagnostic/development detail. Set diagnostics=true for grouped actionable pending or failed sessions.',
|
|
192
196
|
inputSchema: {
|
|
193
197
|
type: 'object',
|
|
194
198
|
additionalProperties: false,
|
|
195
199
|
properties: {
|
|
196
200
|
limit: { type: 'integer', minimum: 1, maximum: 200, description: 'Max results (default 20)' },
|
|
201
|
+
source: { type: 'string', description: 'Filter by source' },
|
|
202
|
+
agentId: { type: 'string', description: 'Filter by agent ID' },
|
|
203
|
+
status: { type: 'string', enum: ['pending', 'failed'], description: 'Filter by processing status' },
|
|
204
|
+
diagnostics: { type: 'boolean', description: 'Include source/agent/status buckets, guidance, and samples. Default false.' },
|
|
197
205
|
},
|
|
198
206
|
},
|
|
199
207
|
},
|