@shadowforge0/aquifer-memory 1.8.0 → 1.9.0
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 +49 -22
- package/README_CN.md +24 -22
- package/README_TW.md +20 -22
- package/aquifer.config.example.json +2 -1
- package/consumers/cli.js +560 -4
- package/consumers/codex.js +1 -1
- package/consumers/mcp.js +3 -0
- package/consumers/openclaw-ext/index.js +64 -6
- 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 +39 -1
- package/consumers/shared/config.js +2 -0
- package/core/aquifer.js +180 -33
- package/core/backends/local.js +109 -0
- package/core/doctor.js +924 -0
- package/core/finalization-inspector.js +164 -0
- package/core/memory-explain.js +624 -0
- package/core/memory-recall.js +49 -23
- package/core/memory-records.js +16 -5
- package/core/memory-review.js +891 -0
- package/core/memory-serving.js +61 -4
- package/core/operator-observability.js +249 -0
- package/core/postgres-migrations.js +13 -0
- package/core/session-finalization.js +76 -1
- package/core/storage.js +124 -8
- package/docs/getting-started.md +34 -1
- package/docs/setup.md +102 -22
- package/package.json +5 -4
- package/schema/019-v1-memory-review-resolutions.sql +53 -0
- package/scripts/codex-checkpoint-commands.js +28 -0
- package/scripts/codex-checkpoint-runtime.js +109 -0
- package/scripts/codex-recovery.js +16 -4
package/consumers/cli.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* aquifer backfill [options] Enrich pending sessions
|
|
12
12
|
* aquifer stats [options] Show database statistics
|
|
13
13
|
* aquifer backend-info [--json] Show selected backend capabilities
|
|
14
|
+
* aquifer install-openclaw Install/update OpenClaw MCP + extension wiring
|
|
15
|
+
* aquifer finalization ... Inspect read-only finalization ledger
|
|
14
16
|
* aquifer export [options] Export sessions
|
|
15
17
|
* aquifer operator ... Run operator-safe consolidation jobs
|
|
16
18
|
* aquifer mcp Start MCP server
|
|
@@ -21,6 +23,7 @@ const { createAquiferFromConfig } = require('./shared/factory');
|
|
|
21
23
|
const { loadConfig } = require('./shared/config');
|
|
22
24
|
const { formatRecallResults } = require('./shared/recall-format');
|
|
23
25
|
const { backendCapabilities } = require('../core/backends/capabilities');
|
|
26
|
+
const { summarizeFinalizationListRow } = require('../core/finalization-inspector');
|
|
24
27
|
|
|
25
28
|
function formatDate(value, fallback) {
|
|
26
29
|
if (!value) return fallback;
|
|
@@ -183,6 +186,9 @@ function parseArgs(argv) {
|
|
|
183
186
|
'policy-version', 'worker-id', 'apply-token', 'claim-lease-seconds', 'snapshot-as-of',
|
|
184
187
|
'scope-id', 'scope-key', 'scope-keys', 'scope-kind', 'context-key', 'topic-key', 'authority', 'input',
|
|
185
188
|
'synthesis-summary', 'synthesis-summary-file', 'min-finalizations', 'checkpoint-key',
|
|
189
|
+
'openclaw-home', 'id', 'transcript-hash', 'phase', 'host', 'run-id', 'kind', 'query',
|
|
190
|
+
'memory-type', 'visibility', 'as-of', 'resolution', 'reason', 'actor-kind', 'actor-id',
|
|
191
|
+
'defer-until', 'expected-latest-issue-feedback-id', 'expected-latest-issue-feedback-at',
|
|
186
192
|
]);
|
|
187
193
|
for (let i = 0; i < argv.length; i++) {
|
|
188
194
|
if (argv[i] === '--') { args._.push(...argv.slice(i + 1)); break; }
|
|
@@ -204,7 +210,29 @@ function parseArgs(argv) {
|
|
|
204
210
|
// Commands
|
|
205
211
|
// ---------------------------------------------------------------------------
|
|
206
212
|
|
|
207
|
-
async function cmdMigrate(aquifer) {
|
|
213
|
+
async function cmdMigrate(aquifer, args = { flags: {} }) {
|
|
214
|
+
if (args.flags && args.flags.json) {
|
|
215
|
+
const notices = [];
|
|
216
|
+
const originalStderrWrite = process.stderr.write;
|
|
217
|
+
process.stderr.write = function writeCapturedStderr(chunk, encoding, callback) {
|
|
218
|
+
notices.push(Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk));
|
|
219
|
+
if (typeof encoding === 'function') encoding();
|
|
220
|
+
if (typeof callback === 'function') callback();
|
|
221
|
+
return true;
|
|
222
|
+
};
|
|
223
|
+
try {
|
|
224
|
+
await aquifer.migrate();
|
|
225
|
+
} finally {
|
|
226
|
+
process.stderr.write = originalStderrWrite;
|
|
227
|
+
}
|
|
228
|
+
console.log(JSON.stringify({
|
|
229
|
+
ok: true,
|
|
230
|
+
migrated: true,
|
|
231
|
+
notices: notices.join('').split(/\r?\n/).map(line => line.trim()).filter(Boolean),
|
|
232
|
+
}, null, 2));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
208
236
|
await aquifer.migrate();
|
|
209
237
|
console.log('Migrations applied successfully.');
|
|
210
238
|
}
|
|
@@ -390,6 +418,9 @@ async function cmdStats(aquifer, args) {
|
|
|
390
418
|
console.log(`Serving mode: ${stats.serving?.mode || 'legacy'}`);
|
|
391
419
|
console.log(`Active scope: ${stats.serving?.activeScopePath?.join(' > ') || stats.serving?.activeScopeKey || 'none'}`);
|
|
392
420
|
console.log(`Sessions: ${stats.sessionTotal} (${Object.entries(stats.sessions).map(([k, v]) => `${k}: ${v}`).join(', ')})`);
|
|
421
|
+
if (stats.pendingSessions?.available) {
|
|
422
|
+
console.log(`Actionable pending/failed: ${stats.pendingSessions.total}`);
|
|
423
|
+
}
|
|
393
424
|
console.log(`Summaries: ${stats.summaries}`);
|
|
394
425
|
console.log(`Turn embeddings: ${stats.turnEmbeddings}`);
|
|
395
426
|
console.log(`Entities: ${stats.entities}`);
|
|
@@ -451,6 +482,149 @@ async function cmdBackendInfo(args) {
|
|
|
451
482
|
}
|
|
452
483
|
}
|
|
453
484
|
|
|
485
|
+
function formatDoctor(result = {}) {
|
|
486
|
+
const checks = Array.isArray(result.checks) ? result.checks : [];
|
|
487
|
+
if (checks.length === 0) return `Doctor: ${result.status || 'unknown'}`;
|
|
488
|
+
return checks.map(check => {
|
|
489
|
+
const next = check.nextAction ? ` next=${check.nextAction}` : '';
|
|
490
|
+
return `${check.status || 'unknown'} ${check.id || 'check'}: ${check.summary || ''}${next}`;
|
|
491
|
+
}).join('\n');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function cmdDoctor(aquifer, args) {
|
|
495
|
+
const result = await aquifer.doctor.run({
|
|
496
|
+
host: args.flags.host || undefined,
|
|
497
|
+
openclawHome: args.flags['openclaw-home'] || undefined,
|
|
498
|
+
limit: parsePositiveInt(args.flags.limit, 20),
|
|
499
|
+
});
|
|
500
|
+
if (args.flags.json) {
|
|
501
|
+
console.log(JSON.stringify(result, null, 2));
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
console.log(formatDoctor(result));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function formatTimestamp(value) {
|
|
508
|
+
if (!value) return '?';
|
|
509
|
+
const parsed = new Date(value);
|
|
510
|
+
return isNaN(parsed.getTime()) ? String(value) : parsed.toISOString();
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function shortHash(value) {
|
|
514
|
+
if (!value) return '?';
|
|
515
|
+
return String(value).slice(0, 12);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function formatFinalizationList(rows = []) {
|
|
519
|
+
if (!rows || rows.length === 0) return 'No finalization rows found.';
|
|
520
|
+
return rows.map(row => {
|
|
521
|
+
const item = summarizeFinalizationListRow(row);
|
|
522
|
+
const session = `${item.source}/${item.agentId}/${item.sessionId}`;
|
|
523
|
+
const phase = item.phase ? ` phase=${item.phase}` : '';
|
|
524
|
+
const mode = item.mode ? ` mode=${item.mode}` : '';
|
|
525
|
+
const hash = item.transcriptHashPrefix ? ` hash=${item.transcriptHashPrefix}` : '';
|
|
526
|
+
return `#${item.id} ${item.status} ${session}${phase}${mode}${hash} updated=${formatTimestamp(item.updatedAt)}`;
|
|
527
|
+
}).join('\n');
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function formatFinalizationInspect(report = {}) {
|
|
531
|
+
const item = report.finalization || {};
|
|
532
|
+
const candidateSummary = report.candidateSummary || {};
|
|
533
|
+
const lineage = report.lineage || {};
|
|
534
|
+
const envelope = report.candidateEnvelope || {};
|
|
535
|
+
const memoryResult = report.memoryResult || {};
|
|
536
|
+
const lines = [
|
|
537
|
+
`Finalization #${item.id || '?'} ${item.status || 'unknown'}`,
|
|
538
|
+
`Session: ${item.source || '?'}/${item.agentId || '?'}/${item.sessionId || '?'} phase=${item.phase || '?'} mode=${item.mode || '?'} host=${item.host || '?'}`,
|
|
539
|
+
`Transcript hash: ${item.transcriptHashPrefix || shortHash(item.transcriptHash)}`,
|
|
540
|
+
`Updated: ${formatTimestamp(item.updatedAt)} finalized=${formatTimestamp(item.finalizedAt)}`,
|
|
541
|
+
];
|
|
542
|
+
|
|
543
|
+
if (item.finalizerModel) lines.push(`Finalizer: ${item.finalizerModel}`);
|
|
544
|
+
if (item.scopeKind || item.scopeKey || item.contextKey || item.topicKey) {
|
|
545
|
+
lines.push(`Scope: kind=${item.scopeKind || '?'} key=${item.scopeKey || '?'} context=${item.contextKey || '?'} topic=${item.topicKey || '?'}`);
|
|
546
|
+
}
|
|
547
|
+
if (item.error) lines.push(`Error: ${item.error}`);
|
|
548
|
+
|
|
549
|
+
lines.push(`Memory result: candidates=${memoryResult.candidates || 0} promoted=${memoryResult.promoted || 0} quarantined=${memoryResult.quarantined || 0} skipped=${memoryResult.skipped || 0}`);
|
|
550
|
+
lines.push(`Candidate envelope: version=${envelope.version || '?'} hash=${shortHash(envelope.hash)} candidates=${envelope.candidateCount || 0}`);
|
|
551
|
+
lines.push(`Candidate actions: total=${candidateSummary.total || 0} promote=${candidateSummary.promote || 0} quarantine=${candidateSummary.quarantine || 0} skip=${candidateSummary.skip || 0} supersede=${candidateSummary.supersede || 0} error=${candidateSummary.error || 0}`);
|
|
552
|
+
lines.push(`Lineage: memories=${(lineage.memoryRecordIds || []).join(',') || 'none'} facts=${(lineage.factAssertionIds || []).join(',') || 'none'} evidenceRefs=${lineage.evidenceRefCount || 0} evidenceItems=${lineage.evidenceItemCount || 0}`);
|
|
553
|
+
|
|
554
|
+
if (report.review?.humanReviewText) {
|
|
555
|
+
lines.push('\nHuman review:\n' + report.review.humanReviewText);
|
|
556
|
+
}
|
|
557
|
+
if (report.review?.sessionStartText) {
|
|
558
|
+
lines.push('\nSessionStart:\n' + report.review.sessionStartText);
|
|
559
|
+
}
|
|
560
|
+
if (Array.isArray(report.candidates) && report.candidates.length > 0) {
|
|
561
|
+
lines.push('\nCandidates:');
|
|
562
|
+
for (const candidate of report.candidates) {
|
|
563
|
+
lines.push(` [${candidate.index}] ${candidate.action}${candidate.reason ? `/${candidate.reason}` : ''} ${candidate.memoryType || '?'} ${candidate.canonicalKey || '?'}${candidate.memoryRecordId ? ` memory=${candidate.memoryRecordId}` : ''}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return lines.join('\n');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function cmdFinalization(aquifer, args) {
|
|
570
|
+
const verb = args._[1] || 'list';
|
|
571
|
+
|
|
572
|
+
if (verb === 'list') {
|
|
573
|
+
const rows = await aquifer.finalization.list({
|
|
574
|
+
status: args.flags.status || undefined,
|
|
575
|
+
agentId: args.flags['agent-id'] || undefined,
|
|
576
|
+
source: args.flags.source || undefined,
|
|
577
|
+
host: args.flags.host || undefined,
|
|
578
|
+
sessionId: args.flags['session-id'] || undefined,
|
|
579
|
+
transcriptHash: args.flags['transcript-hash'] || undefined,
|
|
580
|
+
phase: args.flags.phase || undefined,
|
|
581
|
+
mode: args.flags.mode || undefined,
|
|
582
|
+
limit: parsePositiveInt(args.flags.limit, 50),
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
if (args.flags.json) {
|
|
586
|
+
console.log(JSON.stringify({
|
|
587
|
+
readOnly: true,
|
|
588
|
+
finalizations: rows.map(summarizeFinalizationListRow),
|
|
589
|
+
}, null, 2));
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
console.log(formatFinalizationList(rows));
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (verb === 'inspect') {
|
|
597
|
+
const id = args.flags.id || args._[2] || undefined;
|
|
598
|
+
const sessionId = args.flags['session-id'];
|
|
599
|
+
if (id && (sessionId || args.flags['agent-id'] || args.flags.source || args.flags['transcript-hash'])) {
|
|
600
|
+
console.error('Usage: --id is mutually exclusive with --session-id/--agent-id/--source/--transcript-hash.');
|
|
601
|
+
process.exit(1);
|
|
602
|
+
}
|
|
603
|
+
if (!id && (!sessionId || !args.flags['agent-id'] || !args.flags.source)) {
|
|
604
|
+
console.error('Usage: aquifer finalization inspect (--id ID | --session-id ID --agent-id ID --source SOURCE [--transcript-hash HASH]) [--json]');
|
|
605
|
+
process.exit(1);
|
|
606
|
+
}
|
|
607
|
+
const report = await aquifer.finalization.inspect({
|
|
608
|
+
id,
|
|
609
|
+
sessionId: sessionId || undefined,
|
|
610
|
+
agentId: args.flags['agent-id'] || undefined,
|
|
611
|
+
source: args.flags.source || undefined,
|
|
612
|
+
transcriptHash: args.flags['transcript-hash'] || undefined,
|
|
613
|
+
phase: args.flags.phase || undefined,
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
if (args.flags.json) {
|
|
617
|
+
console.log(JSON.stringify(report, null, 2));
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
console.log(formatFinalizationInspect(report));
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
console.error('Usage: aquifer finalization <list|inspect> [options]');
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
|
|
454
628
|
async function cmdLocalQuickstart(aquifer) {
|
|
455
629
|
const cfg = aquifer.getConfig();
|
|
456
630
|
console.log('Aquifer quickstart — verifying local starter backend.\n');
|
|
@@ -622,10 +796,319 @@ async function cmdBootstrap(aquifer, args) {
|
|
|
622
796
|
}
|
|
623
797
|
}
|
|
624
798
|
|
|
799
|
+
function formatExplain(result = {}) {
|
|
800
|
+
const selected = Array.isArray(result.selected) ? result.selected : [];
|
|
801
|
+
const excluded = Array.isArray(result.excluded) ? result.excluded : [];
|
|
802
|
+
const lines = [
|
|
803
|
+
`Explain ${result.lane || 'memory'}: selected=${selected.length} excluded=${excluded.length}`,
|
|
804
|
+
];
|
|
805
|
+
if (selected.length > 0) {
|
|
806
|
+
lines.push('Selected:');
|
|
807
|
+
for (const row of selected) {
|
|
808
|
+
lines.push(` ${row.memoryId || row.id || '?'} ${row.canonicalKey || '?'} reason=${row.reason || 'selected'}`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (excluded.length > 0) {
|
|
812
|
+
lines.push('Excluded:');
|
|
813
|
+
for (const row of excluded) {
|
|
814
|
+
lines.push(` ${row.memoryId || row.id || '?'} ${row.canonicalKey || '?'} reason=${row.reason || 'unknown'}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (result.budget) {
|
|
818
|
+
lines.push(`Budget: limit=${result.budget.limit || '?'} maxChars=${result.budget.maxChars || '?'} overflow=${result.budget.overflow === true}`);
|
|
819
|
+
}
|
|
820
|
+
return lines.join('\n');
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function formatFeedbackCounts(counts = {}) {
|
|
824
|
+
const entries = Object.entries(counts).filter(([, count]) => count > 0);
|
|
825
|
+
if (entries.length === 0) return 'none';
|
|
826
|
+
return entries.map(([type, count]) => `${type}=${count}`).join(' ');
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function formatReviewResolution(resolution = null) {
|
|
830
|
+
if (!resolution || !resolution.resolution) return 'none';
|
|
831
|
+
const at = resolution.resolvedAt ? ` at=${formatTimestamp(resolution.resolvedAt)}` : '';
|
|
832
|
+
const defer = resolution.deferUntil ? ` deferUntil=${formatTimestamp(resolution.deferUntil)}` : '';
|
|
833
|
+
return `${resolution.resolution}${at}${defer}`;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function formatReviewQueue(result = {}) {
|
|
837
|
+
if (result.available === false) {
|
|
838
|
+
return result.error || 'Memory review queue is unavailable.';
|
|
839
|
+
}
|
|
840
|
+
const rows = Array.isArray(result.items) ? result.items : (Array.isArray(result.reviewQueue) ? result.reviewQueue : []);
|
|
841
|
+
if (rows.length === 0) return 'No memory review items found.';
|
|
842
|
+
return rows.map(row => {
|
|
843
|
+
const scope = row.scope?.key ? ` scope=${row.scope.key}` : '';
|
|
844
|
+
const latest = row.latestFeedbackAt ? ` latest=${formatTimestamp(row.latestFeedbackAt)}` : '';
|
|
845
|
+
const resolution = row.resolution ? ` resolution=${formatReviewResolution(row.resolution)}` : '';
|
|
846
|
+
const counts = formatFeedbackCounts(row.feedbackCounts);
|
|
847
|
+
return `#${row.memoryId} severity=${row.severityScore} issues=${row.issueCount}/${row.feedbackCount} ${row.status || '?'}${scope} ${row.canonicalKey || '?'} feedback=${counts}${latest}${resolution}`;
|
|
848
|
+
}).join('\n');
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function formatReviewInspect(result = {}) {
|
|
852
|
+
const memory = result.memory || {};
|
|
853
|
+
const review = result.review || {};
|
|
854
|
+
const visibility = memory.visibility || {};
|
|
855
|
+
const lines = [
|
|
856
|
+
`Memory #${memory.memoryId || '?'} ${memory.status || '?'} ${memory.canonicalKey || '?'}`,
|
|
857
|
+
`Scope: ${memory.scope?.kind || '?'}/${memory.scope?.key || '?'}`,
|
|
858
|
+
`Visibility: bootstrap=${visibility.bootstrap === true} recall=${visibility.recall === true}`,
|
|
859
|
+
`Feedback: issues=${review.issueCount || 0}/${review.feedbackCount || 0} ${formatFeedbackCounts(review.feedbackCounts)}`,
|
|
860
|
+
`Latest resolution: ${formatReviewResolution(review.latestResolution)}`,
|
|
861
|
+
];
|
|
862
|
+
const events = Array.isArray(review.recentFeedback) ? review.recentFeedback : [];
|
|
863
|
+
if (events.length > 0) {
|
|
864
|
+
lines.push('Recent feedback:');
|
|
865
|
+
for (const event of events) {
|
|
866
|
+
const actor = event.actorId ? `${event.actorKind || 'actor'}:${event.actorId}` : (event.actorKind || 'actor');
|
|
867
|
+
lines.push(` #${event.id} ${event.feedbackType} ${actor} at=${formatTimestamp(event.createdAt)}`);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return lines.join('\n');
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function formatReviewResolve(result = {}) {
|
|
874
|
+
const resolution = result.resolution || {};
|
|
875
|
+
const memory = result.memory || {};
|
|
876
|
+
const issueCount = result.review?.issueFeedbackCount || 0;
|
|
877
|
+
return [
|
|
878
|
+
`Review resolution recorded: ${resolution.resolution || '?'} for memory #${memory.memoryId || resolution.memoryId || '?'}`,
|
|
879
|
+
`Canonical key: ${memory.canonicalKey || resolution.canonicalKey || '?'}`,
|
|
880
|
+
`Issue feedback covered: ${issueCount} ${formatFeedbackCounts(result.review?.issueFeedbackCounts)}`,
|
|
881
|
+
`Memory mutated: ${result.memoryMutated === true}`,
|
|
882
|
+
].join('\n');
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
async function cmdReview(aquifer, args) {
|
|
886
|
+
const verb = args._[1] || 'queue';
|
|
887
|
+
if (verb === 'queue') {
|
|
888
|
+
const activeScopePath = parseScopePath(args.flags['active-scope-path']);
|
|
889
|
+
const activeScopeKey = args.flags['active-scope-key']
|
|
890
|
+
|| args.flags['scope-key']
|
|
891
|
+
|| (activeScopePath ? activeScopePath[activeScopePath.length - 1] : undefined);
|
|
892
|
+
const result = await aquifer.review.queue({
|
|
893
|
+
feedbackType: args.flags['feedback-type'] || undefined,
|
|
894
|
+
memoryType: args.flags['memory-type'] || undefined,
|
|
895
|
+
visibility: args.flags.visibility || undefined,
|
|
896
|
+
activeScopeKey,
|
|
897
|
+
activeScopePath,
|
|
898
|
+
scopeKey: args.flags['scope-key'] || activeScopeKey,
|
|
899
|
+
canonicalKey: args.flags['canonical-key'] || undefined,
|
|
900
|
+
dateFrom: args.flags['date-from'] || undefined,
|
|
901
|
+
dateTo: args.flags['date-to'] || undefined,
|
|
902
|
+
asOf: args.flags['as-of'] || args.flags['snapshot-as-of'] || undefined,
|
|
903
|
+
includeResolved: args.flags['include-resolved'] === true,
|
|
904
|
+
limit: parsePositiveInt(args.flags.limit, 20),
|
|
905
|
+
});
|
|
906
|
+
if (args.flags.json) {
|
|
907
|
+
console.log(JSON.stringify(result, null, 2));
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
console.log(formatReviewQueue(result));
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (verb === 'inspect') {
|
|
915
|
+
const activeScopePath = parseScopePath(args.flags['active-scope-path']);
|
|
916
|
+
const activeScopeKey = args.flags['active-scope-key']
|
|
917
|
+
|| args.flags['scope-key']
|
|
918
|
+
|| (activeScopePath ? activeScopePath[activeScopePath.length - 1] : undefined);
|
|
919
|
+
const memoryId = args.flags['memory-id'] || args.flags.id || args._[2] || undefined;
|
|
920
|
+
const canonicalKey = args.flags['canonical-key'] || undefined;
|
|
921
|
+
if (memoryId && canonicalKey) {
|
|
922
|
+
console.error('Usage: --memory-id/--id is mutually exclusive with --canonical-key.');
|
|
923
|
+
process.exit(1);
|
|
924
|
+
}
|
|
925
|
+
if (!memoryId && !canonicalKey) {
|
|
926
|
+
console.error('Usage: aquifer review inspect (--memory-id ID | --canonical-key KEY) [--json]');
|
|
927
|
+
process.exit(1);
|
|
928
|
+
}
|
|
929
|
+
const result = await aquifer.review.inspect({
|
|
930
|
+
memoryId,
|
|
931
|
+
canonicalKey,
|
|
932
|
+
activeScopeKey,
|
|
933
|
+
activeScopePath,
|
|
934
|
+
scopeKey: args.flags['scope-key'] || activeScopeKey,
|
|
935
|
+
visibility: args.flags.visibility || undefined,
|
|
936
|
+
feedbackType: args.flags['feedback-type'] || undefined,
|
|
937
|
+
dateFrom: args.flags['date-from'] || undefined,
|
|
938
|
+
dateTo: args.flags['date-to'] || undefined,
|
|
939
|
+
asOf: args.flags['as-of'] || args.flags['snapshot-as-of'] || undefined,
|
|
940
|
+
limit: parsePositiveInt(args.flags.limit, 20),
|
|
941
|
+
});
|
|
942
|
+
if (args.flags.json) {
|
|
943
|
+
console.log(JSON.stringify(result, null, 2));
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
console.log(formatReviewInspect(result));
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if (verb === 'resolve') {
|
|
951
|
+
const activeScopePath = parseScopePath(args.flags['active-scope-path']);
|
|
952
|
+
const activeScopeKey = args.flags['active-scope-key']
|
|
953
|
+
|| args.flags['scope-key']
|
|
954
|
+
|| (activeScopePath ? activeScopePath[activeScopePath.length - 1] : undefined);
|
|
955
|
+
const memoryId = args.flags['memory-id'] || args.flags.id || undefined;
|
|
956
|
+
const canonicalKey = args.flags['canonical-key'] || undefined;
|
|
957
|
+
if (memoryId && canonicalKey) {
|
|
958
|
+
console.error('Usage: --memory-id/--id is mutually exclusive with --canonical-key.');
|
|
959
|
+
process.exit(1);
|
|
960
|
+
}
|
|
961
|
+
if (!memoryId && !canonicalKey) {
|
|
962
|
+
console.error('Usage: aquifer review resolve (--memory-id ID | --canonical-key KEY) --resolution resolved|ignored|deferred [--json]');
|
|
963
|
+
process.exit(1);
|
|
964
|
+
}
|
|
965
|
+
if (!args.flags['expected-latest-issue-feedback-id'] && !args.flags['expected-latest-issue-feedback-at']) {
|
|
966
|
+
console.error('Usage: aquifer review resolve requires --expected-latest-issue-feedback-id or --expected-latest-issue-feedback-at.');
|
|
967
|
+
process.exit(1);
|
|
968
|
+
}
|
|
969
|
+
const result = await aquifer.review.resolve({
|
|
970
|
+
memoryId,
|
|
971
|
+
canonicalKey,
|
|
972
|
+
resolution: args.flags.resolution || undefined,
|
|
973
|
+
reason: args.flags.reason || args.flags.note || undefined,
|
|
974
|
+
actorKind: args.flags['actor-kind'] || undefined,
|
|
975
|
+
actorId: args.flags['actor-id'] || args.flags['agent-id'] || undefined,
|
|
976
|
+
deferUntil: args.flags['defer-until'] || undefined,
|
|
977
|
+
expectedLatestIssueFeedbackId: args.flags['expected-latest-issue-feedback-id'] || undefined,
|
|
978
|
+
expectedLatestIssueFeedbackAt: args.flags['expected-latest-issue-feedback-at'] || undefined,
|
|
979
|
+
activeScopeKey,
|
|
980
|
+
activeScopePath,
|
|
981
|
+
scopeKey: args.flags['scope-key'] || activeScopeKey,
|
|
982
|
+
visibility: args.flags.visibility || undefined,
|
|
983
|
+
asOf: args.flags['as-of'] || args.flags['snapshot-as-of'] || undefined,
|
|
984
|
+
});
|
|
985
|
+
if (args.flags.json) {
|
|
986
|
+
console.log(JSON.stringify(result, null, 2));
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
console.log(formatReviewResolve(result));
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
console.error('Usage: aquifer review <queue|inspect|resolve> [options]');
|
|
994
|
+
process.exit(1);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
async function cmdExplain(aquifer, args) {
|
|
998
|
+
const lane = args._[1] || 'bootstrap';
|
|
999
|
+
if (lane === 'bootstrap') {
|
|
1000
|
+
const result = await aquifer.memory.explainBootstrap({
|
|
1001
|
+
activeScopeKey: args.flags['active-scope-key'] || undefined,
|
|
1002
|
+
activeScopePath: parseScopePath(args.flags['active-scope-path']),
|
|
1003
|
+
limit: parsePositiveInt(args.flags.limit, 5),
|
|
1004
|
+
maxChars: parsePositiveInt(args.flags['max-chars'], 4000),
|
|
1005
|
+
});
|
|
1006
|
+
if (args.flags.json) {
|
|
1007
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
console.log(formatExplain(result));
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
if (lane === 'memory') {
|
|
1014
|
+
const query = args.flags.query || args._.slice(2).join(' ');
|
|
1015
|
+
if (!query) {
|
|
1016
|
+
console.error('Usage: aquifer explain memory --query TEXT --active-scope-key KEY [--limit N] [--json]');
|
|
1017
|
+
process.exit(1);
|
|
1018
|
+
}
|
|
1019
|
+
const result = await aquifer.memory.explainCurrent(query, {
|
|
1020
|
+
activeScopeKey: args.flags['active-scope-key'] || undefined,
|
|
1021
|
+
activeScopePath: parseScopePath(args.flags['active-scope-path']),
|
|
1022
|
+
limit: parsePositiveInt(args.flags.limit, 5),
|
|
1023
|
+
});
|
|
1024
|
+
if (args.flags.json) {
|
|
1025
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
console.log(formatExplain(result));
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
console.error('Usage: aquifer explain <bootstrap|memory> [options]');
|
|
1032
|
+
process.exit(1);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function formatOperatorStatus(result = {}) {
|
|
1036
|
+
const compaction = result.compaction || {};
|
|
1037
|
+
const checkpoint = result.checkpoint || {};
|
|
1038
|
+
const lines = [
|
|
1039
|
+
`Compaction: ${compaction.available === false ? 'unavailable' : 'available'} statuses=${JSON.stringify(compaction.statusCounts || {})}`,
|
|
1040
|
+
`Checkpoint: ${checkpoint.available === false ? 'unavailable' : 'available'} statuses=${JSON.stringify(checkpoint.statusCounts || {})}`,
|
|
1041
|
+
];
|
|
1042
|
+
if (Array.isArray(compaction.staleClaims) && compaction.staleClaims.length > 0) {
|
|
1043
|
+
lines.push(`Stale compaction claims: ${compaction.staleClaims.map(run => `#${run.id}/${run.cadence}`).join(', ')}`);
|
|
1044
|
+
}
|
|
1045
|
+
if (Array.isArray(compaction.latest) && compaction.latest.length > 0) {
|
|
1046
|
+
lines.push('Latest compaction: ' + compaction.latest.map(run => `#${run.id} ${run.status} ${run.cadence}`).join(', '));
|
|
1047
|
+
}
|
|
1048
|
+
if (Array.isArray(checkpoint.latest) && checkpoint.latest.length > 0) {
|
|
1049
|
+
lines.push('Latest checkpoint: ' + checkpoint.latest.map(run => `#${run.id} ${run.status} sources=${run.sourceCount}`).join(', '));
|
|
1050
|
+
}
|
|
1051
|
+
return lines.join('\n');
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function formatOperatorInspect(result = {}) {
|
|
1055
|
+
const run = result.run || {};
|
|
1056
|
+
const lines = [
|
|
1057
|
+
`${run.kind || 'operator'} run #${run.id || '?'} status=${run.status || '?'}`,
|
|
1058
|
+
];
|
|
1059
|
+
if (run.kind === 'compaction') {
|
|
1060
|
+
lines.push(`Window: ${run.cadence || '?'} ${run.periodStart || '?'} -> ${run.periodEnd || '?'}`);
|
|
1061
|
+
lines.push(`Plan: candidates=${run.candidateCount || 0} statusUpdates=${run.statusUpdateCount || 0}`);
|
|
1062
|
+
if (run.leaseExpiresAt) lines.push(`Claim: worker=${run.workerId || '?'} leaseExpiresAt=${run.leaseExpiresAt}`);
|
|
1063
|
+
} else if (run.kind === 'checkpoint') {
|
|
1064
|
+
lines.push(`Checkpoint: scope=${run.scopeId || '?'} key=${run.checkpointKey || '?'}`);
|
|
1065
|
+
lines.push(`Range: ${run.fromFinalizationIdExclusive || 0} -> ${run.toFinalizationIdInclusive || '?'}`);
|
|
1066
|
+
lines.push(`Sources: ${run.sourceCount || 0}`);
|
|
1067
|
+
}
|
|
1068
|
+
if (run.error) lines.push(`Error: ${run.error}`);
|
|
1069
|
+
if (Array.isArray(result.sources) && result.sources.length > 0) {
|
|
1070
|
+
lines.push('Sources:');
|
|
1071
|
+
for (const source of result.sources) {
|
|
1072
|
+
lines.push(` [${source.sourceIndex}] finalization=${source.finalizationId} ${source.source || '?'}/${source.agentId || '?'}/${source.sessionId || '?'}`);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
return lines.join('\n');
|
|
1076
|
+
}
|
|
1077
|
+
|
|
625
1078
|
async function cmdOperator(aquifer, args) {
|
|
626
1079
|
const operatorVerb = args._[1] || 'compaction';
|
|
627
1080
|
const cadenceVerbs = new Set(['manual', 'daily', 'weekly', 'monthly']);
|
|
628
1081
|
|
|
1082
|
+
if (operatorVerb === 'status') {
|
|
1083
|
+
const result = await aquifer.operator.status({
|
|
1084
|
+
limit: parsePositiveInt(args.flags.limit, 10),
|
|
1085
|
+
});
|
|
1086
|
+
if (args.flags.json) {
|
|
1087
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
console.log(formatOperatorStatus(result));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (operatorVerb === 'inspect') {
|
|
1095
|
+
const runId = args.flags['run-id'] || args.flags.id || args._[2];
|
|
1096
|
+
if (!runId) {
|
|
1097
|
+
console.error('Usage: aquifer operator inspect --run-id ID [--kind compaction|checkpoint] [--json]');
|
|
1098
|
+
process.exit(1);
|
|
1099
|
+
}
|
|
1100
|
+
const result = await aquifer.operator.inspect({
|
|
1101
|
+
runId,
|
|
1102
|
+
kind: args.flags.kind || 'compaction',
|
|
1103
|
+
});
|
|
1104
|
+
if (args.flags.json) {
|
|
1105
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
console.log(formatOperatorInspect(result));
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
629
1112
|
if (operatorVerb === 'checkpoint') {
|
|
630
1113
|
const synthesisSummary = readSynthesisSummaryFromFlags(args.flags);
|
|
631
1114
|
const result = await aquifer.checkpoints.runProducer({
|
|
@@ -808,13 +1291,18 @@ Commands:
|
|
|
808
1291
|
quickstart Verify end-to-end setup (migrate → commit → enrich → recall)
|
|
809
1292
|
migrate Run database migrations
|
|
810
1293
|
backend-info Show selected backend capabilities without connecting to a database
|
|
1294
|
+
install-openclaw Install/update OpenClaw MCP + extension wiring
|
|
1295
|
+
doctor Run read-only health diagnostics
|
|
811
1296
|
recall <query> Search sessions (requires embed config)
|
|
812
1297
|
evidence-recall <query> Search legacy session/evidence plane explicitly
|
|
813
1298
|
feedback Record trust feedback on a session
|
|
814
1299
|
memory-feedback Record curated memory feedback
|
|
815
1300
|
feedback-stats Show trust feedback statistics and coverage
|
|
1301
|
+
review ... Inspect or resolve memory feedback review queue
|
|
816
1302
|
backfill Enrich pending sessions
|
|
817
1303
|
operator ... Run operator-safe consolidation jobs
|
|
1304
|
+
finalization ... Inspect read-only finalization ledger
|
|
1305
|
+
explain ... Explain current-memory serving decisions
|
|
818
1306
|
compact Plan or apply curated memory compaction
|
|
819
1307
|
stats Show database statistics
|
|
820
1308
|
export Export sessions as JSONL
|
|
@@ -831,11 +1319,32 @@ Options:
|
|
|
831
1319
|
--date-to YYYY-MM-DD End date
|
|
832
1320
|
--entities A,B,C Entity names (comma-separated, recall)
|
|
833
1321
|
--entity-mode any|all Entity match mode (recall, default: any)
|
|
1322
|
+
--id ID Finalization row id
|
|
1323
|
+
--host HOST Finalization host filter
|
|
1324
|
+
--status STATUS Finalization status filter
|
|
1325
|
+
--phase PHASE Finalization phase filter
|
|
1326
|
+
--transcript-hash HASH Finalization transcript hash filter
|
|
1327
|
+
--run-id ID Operator run id
|
|
1328
|
+
--kind KIND Operator inspect kind: compaction|checkpoint
|
|
1329
|
+
--query TEXT Explain memory query
|
|
834
1330
|
--session-id ID Session ID (feedback)
|
|
835
|
-
--memory-id ID Curated memory record ID (memory-feedback)
|
|
836
|
-
--canonical-key KEY Active curated memory canonical key (memory-feedback)
|
|
1331
|
+
--memory-id ID Curated memory record ID (memory-feedback, review inspect/resolve)
|
|
1332
|
+
--canonical-key KEY Active curated memory canonical key (memory-feedback, review queue/inspect/resolve)
|
|
837
1333
|
--verdict helpful|unhelpful Feedback verdict (feedback)
|
|
838
1334
|
--feedback-type TYPE Curated memory feedback type
|
|
1335
|
+
--memory-type TYPE Curated memory type filter (review queue)
|
|
1336
|
+
--visibility MODE Review visibility filter: either|both|bootstrap|recall
|
|
1337
|
+
--as-of ISO Review queue memory validity instant
|
|
1338
|
+
--include-resolved Include currently resolved review items in review queue
|
|
1339
|
+
--resolution ACTION Review resolution: resolved|ignored|deferred
|
|
1340
|
+
--reason TEXT Operator reason for review resolution
|
|
1341
|
+
--actor-kind KIND Review resolution actor kind: user|agent|system|curator
|
|
1342
|
+
--actor-id ID Review resolution actor id
|
|
1343
|
+
--defer-until ISO Re-open deferred review item after this instant
|
|
1344
|
+
--expected-latest-issue-feedback-id ID
|
|
1345
|
+
Reject review resolution if newer issue feedback exists
|
|
1346
|
+
--expected-latest-issue-feedback-at ISO
|
|
1347
|
+
Timestamp guard for review resolution snapshots
|
|
839
1348
|
--note TEXT Feedback note (feedback)
|
|
840
1349
|
--explain Show score breakdown per result (recall)
|
|
841
1350
|
--allow-unsafe-debug Allow broad evidence-recall without audit boundary
|
|
@@ -843,6 +1352,11 @@ Options:
|
|
|
843
1352
|
--dry-run Preview only (backfill)
|
|
844
1353
|
--output PATH Output file (export)
|
|
845
1354
|
--config PATH Config file path
|
|
1355
|
+
--openclaw-home PATH OpenClaw home path (install-openclaw)
|
|
1356
|
+
--skip-extension Do not wire the OpenClaw extension/plugin (install-openclaw)
|
|
1357
|
+
--skip-mcp Do not update mcp.servers.aquifer (install-openclaw)
|
|
1358
|
+
--force Move aside existing extension path if needed (install-openclaw)
|
|
1359
|
+
--link-current-package Development only: wire the current checkout instead of OpenClaw node_modules
|
|
846
1360
|
--lookback-days N How far back in days (bootstrap, default: 14)
|
|
847
1361
|
--max-chars N Max output characters (bootstrap, default: 4000)
|
|
848
1362
|
--active-scope-key KEY Active curated memory scope key
|
|
@@ -868,6 +1382,17 @@ Options:
|
|
|
868
1382
|
--min-messages N Min user messages to ingest (ingest-opencode, default: 3)
|
|
869
1383
|
|
|
870
1384
|
Operator examples:
|
|
1385
|
+
aquifer doctor --json
|
|
1386
|
+
aquifer finalization list --status finalized --json
|
|
1387
|
+
aquifer finalization inspect --id 42 --json
|
|
1388
|
+
aquifer finalization inspect --session-id SESSION --agent-id main --source codex --transcript-hash HASH
|
|
1389
|
+
aquifer explain bootstrap --active-scope-key project:aquifer --json
|
|
1390
|
+
aquifer explain memory --query "serving contract" --active-scope-key project:aquifer --json
|
|
1391
|
+
aquifer review queue --scope-key project:aquifer --feedback-type incorrect --json
|
|
1392
|
+
aquifer review inspect --memory-id 42 --json
|
|
1393
|
+
aquifer review resolve --memory-id 42 --resolution resolved --reason "verified current" --json
|
|
1394
|
+
aquifer operator status --json
|
|
1395
|
+
aquifer operator inspect --run-id 42 --kind compaction --json
|
|
871
1396
|
aquifer operator compaction daily --json
|
|
872
1397
|
aquifer operator compaction daily --include-synthesis-prompt --json
|
|
873
1398
|
aquifer operator compaction manual --period-start 2026-04-27T00:00:00Z --period-end 2026-04-28T00:00:00Z --apply
|
|
@@ -876,7 +1401,9 @@ Operator examples:
|
|
|
876
1401
|
aquifer operator checkpoint --scope-id 7 --synthesis-summary-file /tmp/checkpoint-summary.json --apply --finalize --json
|
|
877
1402
|
AQUIFER_BACKEND=local aquifer backend-info --json
|
|
878
1403
|
aquifer codex-recovery checkpoint-heartbeat --hook-stdin --scope-key project:aquifer
|
|
1404
|
+
aquifer codex-recovery checkpoint-spool-status --json --limit 10
|
|
879
1405
|
aquifer codex-recovery checkpoint-heartbeat-hook --scope-key project:aquifer --hooks-path "$CODEX_HOME/hooks.json" --json
|
|
1406
|
+
aquifer install-openclaw --openclaw-home "$OPENCLAW_HOME"
|
|
880
1407
|
aquifer operator archive-distill --input /tmp/archive-snapshot.json --json`);
|
|
881
1408
|
process.exit(0);
|
|
882
1409
|
}
|
|
@@ -917,6 +1444,11 @@ Operator examples:
|
|
|
917
1444
|
return;
|
|
918
1445
|
}
|
|
919
1446
|
|
|
1447
|
+
if (command === 'install-openclaw') {
|
|
1448
|
+
await require('./openclaw-install').cmdInstallOpenClaw(args);
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
920
1452
|
// All other commands need an Aquifer instance
|
|
921
1453
|
const configOverrides = {};
|
|
922
1454
|
if (args.flags.config) {
|
|
@@ -962,7 +1494,10 @@ Operator examples:
|
|
|
962
1494
|
await cmdQuickstart(aquifer);
|
|
963
1495
|
break;
|
|
964
1496
|
case 'migrate':
|
|
965
|
-
await cmdMigrate(aquifer);
|
|
1497
|
+
await cmdMigrate(aquifer, args);
|
|
1498
|
+
break;
|
|
1499
|
+
case 'doctor':
|
|
1500
|
+
await cmdDoctor(aquifer, args);
|
|
966
1501
|
break;
|
|
967
1502
|
case 'recall':
|
|
968
1503
|
await cmdRecall(aquifer, args);
|
|
@@ -979,12 +1514,21 @@ Operator examples:
|
|
|
979
1514
|
case 'feedback-stats':
|
|
980
1515
|
await cmdFeedbackStats(aquifer, args);
|
|
981
1516
|
break;
|
|
1517
|
+
case 'review':
|
|
1518
|
+
await cmdReview(aquifer, args);
|
|
1519
|
+
break;
|
|
982
1520
|
case 'backfill':
|
|
983
1521
|
await cmdBackfill(aquifer, args);
|
|
984
1522
|
break;
|
|
985
1523
|
case 'operator':
|
|
986
1524
|
await cmdOperator(aquifer, args);
|
|
987
1525
|
break;
|
|
1526
|
+
case 'finalization':
|
|
1527
|
+
await cmdFinalization(aquifer, args);
|
|
1528
|
+
break;
|
|
1529
|
+
case 'explain':
|
|
1530
|
+
await cmdExplain(aquifer, args);
|
|
1531
|
+
break;
|
|
988
1532
|
case 'compact':
|
|
989
1533
|
await cmdCompact(aquifer, args);
|
|
990
1534
|
break;
|
|
@@ -1016,8 +1560,20 @@ module.exports = {
|
|
|
1016
1560
|
parseArgs,
|
|
1017
1561
|
selectedBackendInfo,
|
|
1018
1562
|
cmdBackendInfo,
|
|
1563
|
+
cmdMigrate,
|
|
1564
|
+
cmdDoctor,
|
|
1019
1565
|
cmdLocalQuickstart,
|
|
1020
1566
|
cmdOperator,
|
|
1567
|
+
cmdFinalization,
|
|
1568
|
+
cmdExplain,
|
|
1569
|
+
cmdReview,
|
|
1570
|
+
formatDoctor,
|
|
1571
|
+
formatFinalizationList,
|
|
1572
|
+
formatFinalizationInspect,
|
|
1573
|
+
formatExplain,
|
|
1574
|
+
formatReviewQueue,
|
|
1575
|
+
formatReviewInspect,
|
|
1576
|
+
cmdInstallOpenClaw: require('./openclaw-install').cmdInstallOpenClaw,
|
|
1021
1577
|
readSynthesisSummaryFromFlags,
|
|
1022
1578
|
};
|
|
1023
1579
|
|