@shadowforge0/aquifer-memory 1.6.0 → 1.8.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 +8 -0
- package/README.md +72 -0
- package/README_CN.md +17 -0
- package/README_TW.md +4 -0
- package/aquifer.config.example.json +19 -0
- package/consumers/cli.js +259 -12
- package/consumers/codex-active-checkpoint.js +186 -0
- package/consumers/codex-current-memory.js +106 -0
- package/consumers/codex-handoff.js +551 -6
- package/consumers/codex.js +209 -25
- package/consumers/mcp.js +144 -6
- package/consumers/shared/config.js +60 -1
- package/consumers/shared/factory.js +10 -3
- package/core/aquifer.js +357 -838
- package/core/backends/capabilities.js +89 -0
- package/core/backends/local.js +430 -0
- package/core/legacy-bootstrap.js +140 -0
- package/core/mcp-manifest.js +66 -2
- package/core/memory-bootstrap.js +20 -8
- package/core/memory-consolidation.js +365 -11
- package/core/memory-promotion.js +157 -26
- package/core/memory-recall.js +341 -22
- package/core/memory-records.js +347 -11
- package/core/memory-serving.js +132 -0
- package/core/postgres-migrations.js +533 -0
- package/core/public-session-filter.js +40 -0
- package/core/recall-runtime.js +115 -0
- package/core/scope-attribution.js +279 -0
- package/core/session-checkpoint-producer.js +412 -0
- package/core/session-checkpoints.js +432 -0
- package/core/session-finalization.js +98 -2
- package/core/storage-checkpoints.js +546 -0
- package/core/storage.js +121 -8
- package/docs/getting-started.md +6 -0
- package/docs/setup.md +66 -3
- package/package.json +8 -4
- package/schema/014-v1-checkpoint-runs.sql +349 -0
- package/schema/015-v1-evidence-items.sql +92 -0
- package/schema/016-v1-evidence-ref-multi-item.sql +19 -0
- package/schema/017-v1-memory-record-embeddings.sql +25 -0
- package/schema/018-v1-finalization-candidate-envelope.sql +39 -0
- package/scripts/codex-checkpoint-commands.js +464 -0
- package/scripts/codex-checkpoint-runtime.js +520 -0
- package/scripts/codex-recovery.js +246 -1
|
@@ -7,6 +7,36 @@ const path = require('path');
|
|
|
7
7
|
|
|
8
8
|
const { createAquiferFromConfig } = require('../consumers/shared/factory');
|
|
9
9
|
const codex = require('../consumers/codex');
|
|
10
|
+
const {
|
|
11
|
+
cmdCheckpointHeartbeat,
|
|
12
|
+
cmdCheckpointHeartbeatHook,
|
|
13
|
+
cmdCheckpointPrompt,
|
|
14
|
+
cmdCheckpointTick,
|
|
15
|
+
} = require('./codex-checkpoint-commands');
|
|
16
|
+
const {
|
|
17
|
+
acquireHeartbeatClaim,
|
|
18
|
+
checkpointCheckIntervalMs,
|
|
19
|
+
checkpointClaimDir,
|
|
20
|
+
checkpointClaimTtlMs,
|
|
21
|
+
checkpointDueFromMarker,
|
|
22
|
+
checkpointEveryMessages,
|
|
23
|
+
checkpointEveryUserMessages,
|
|
24
|
+
checkpointHeartbeatCommand,
|
|
25
|
+
checkpointMarkerDir,
|
|
26
|
+
checkpointQuietMs,
|
|
27
|
+
checkpointSchedulerDir,
|
|
28
|
+
checkpointSpoolDir,
|
|
29
|
+
defaultHooksPath,
|
|
30
|
+
findNewestJsonlFile,
|
|
31
|
+
inspectCheckpointHeartbeatHook,
|
|
32
|
+
loadRuntimeConfig,
|
|
33
|
+
mergeCheckpointHeartbeatHook,
|
|
34
|
+
readCheckpointMarker,
|
|
35
|
+
readSchedulerMarker,
|
|
36
|
+
releaseHeartbeatClaim,
|
|
37
|
+
writeCheckpointMarker,
|
|
38
|
+
writeSchedulerMarker,
|
|
39
|
+
} = require('./codex-checkpoint-runtime');
|
|
10
40
|
const DB_ENV_KEYS = new Set(['DATABASE_URL', 'AQUIFER_DB_URL', 'AQUIFER_SCHEMA', 'AQUIFER_TENANT_ID']);
|
|
11
41
|
|
|
12
42
|
const VALUE_FLAGS = new Set([
|
|
@@ -16,7 +46,23 @@ const VALUE_FLAGS = new Set([
|
|
|
16
46
|
'except-session-id',
|
|
17
47
|
'file-path',
|
|
18
48
|
'finalizer-model',
|
|
49
|
+
'checkpoint-every-messages',
|
|
50
|
+
'checkpoint-every-user-messages',
|
|
51
|
+
'checkpoint-check-interval-ms',
|
|
52
|
+
'checkpoint-check-interval-minutes',
|
|
53
|
+
'checkpoint-claim-ttl-ms',
|
|
54
|
+
'checkpoint-claim-dir',
|
|
55
|
+
'checkpoint-marker-dir',
|
|
56
|
+
'checkpoint-scheduler-dir',
|
|
57
|
+
'checkpoint-spool-dir',
|
|
58
|
+
'checkpoint-quiet-ms',
|
|
59
|
+
'hook-event-name',
|
|
60
|
+
'hooks-path',
|
|
19
61
|
'idle-ms',
|
|
62
|
+
'max-checkpoint-bytes',
|
|
63
|
+
'max-checkpoint-chars',
|
|
64
|
+
'max-checkpoint-messages',
|
|
65
|
+
'max-checkpoint-prompt-tokens',
|
|
20
66
|
'max-candidates',
|
|
21
67
|
'max-recovery-bytes',
|
|
22
68
|
'max-recovery-chars',
|
|
@@ -27,6 +73,8 @@ const VALUE_FLAGS = new Set([
|
|
|
27
73
|
'reason',
|
|
28
74
|
'scope-kind',
|
|
29
75
|
'scope-key',
|
|
76
|
+
'active-scope-key',
|
|
77
|
+
'active-scope-path',
|
|
30
78
|
'session-id',
|
|
31
79
|
'session-key',
|
|
32
80
|
'sessions-dir',
|
|
@@ -36,6 +84,11 @@ const VALUE_FLAGS = new Set([
|
|
|
36
84
|
'summary-json',
|
|
37
85
|
'summary-text',
|
|
38
86
|
'verdict',
|
|
87
|
+
'workspace',
|
|
88
|
+
'workspace-path',
|
|
89
|
+
'project',
|
|
90
|
+
'project-key',
|
|
91
|
+
'repo-path',
|
|
39
92
|
]);
|
|
40
93
|
|
|
41
94
|
function parseArgs(argv) {
|
|
@@ -109,7 +162,11 @@ function buildRecoveryOptions(flags = {}, env = process.env) {
|
|
|
109
162
|
agentId: flags['agent-id'] || envDefault(env, 'CODEX_AQUIFER_AGENT_ID', 'AQUIFER_AGENT_ID') || 'main',
|
|
110
163
|
source: flags.source || envDefault(env, 'CODEX_AQUIFER_SOURCE', 'AQUIFER_SOURCE') || 'codex',
|
|
111
164
|
sessionKey: flags['session-key'] || envDefault(env, 'CODEX_AQUIFER_SESSION_KEY') || 'codex:cli',
|
|
165
|
+
workspace: flags.workspace || flags['workspace-path'] || envDefault(env, 'CODEX_AQUIFER_WORKSPACE', 'CODEX_WORKSPACE') || undefined,
|
|
166
|
+
project: flags.project || flags['project-key'] || envDefault(env, 'CODEX_AQUIFER_PROJECT', 'CODEX_PROJECT') || undefined,
|
|
167
|
+
repoPath: flags['repo-path'] || envDefault(env, 'CODEX_AQUIFER_REPO_PATH', 'CODEX_REPO_PATH') || undefined,
|
|
112
168
|
codexHome: flags['codex-home'] || envDefault(env, 'CODEX_HOME') || undefined,
|
|
169
|
+
hooksPath: flags['hooks-path'] || undefined,
|
|
113
170
|
stateDir: flags['state-dir'] || undefined,
|
|
114
171
|
sessionsDir: flags['sessions-dir'] || undefined,
|
|
115
172
|
maxRecoveryCandidates: parseIntFlag(flags['max-candidates'], 1),
|
|
@@ -122,6 +179,7 @@ function buildRecoveryOptions(flags = {}, env = process.env) {
|
|
|
122
179
|
includeJsonlPreviews: flags['include-jsonl-previews'] === true,
|
|
123
180
|
includeDeferredRecovery: flags['include-deferred'] === true,
|
|
124
181
|
excludeNewest: flags['include-current'] === true ? false : true,
|
|
182
|
+
strictWrapperEnv: flags['strict-wrapper-env'] === true,
|
|
125
183
|
};
|
|
126
184
|
for (const [key, value] of Object.entries(opts)) {
|
|
127
185
|
if (value === undefined) delete opts[key];
|
|
@@ -129,6 +187,10 @@ function buildRecoveryOptions(flags = {}, env = process.env) {
|
|
|
129
187
|
return opts;
|
|
130
188
|
}
|
|
131
189
|
|
|
190
|
+
function addDoctorCheck(checks, name, status, detail, extra = {}) {
|
|
191
|
+
checks.push({ name, status, detail, ...extra });
|
|
192
|
+
}
|
|
193
|
+
|
|
132
194
|
function shellQuote(value) {
|
|
133
195
|
return `'${String(value || '').replace(/'/g, `'\\''`)}'`;
|
|
134
196
|
}
|
|
@@ -266,6 +328,91 @@ function compactCandidate(candidate = {}) {
|
|
|
266
328
|
};
|
|
267
329
|
}
|
|
268
330
|
|
|
331
|
+
function compactDoctorOptions(opts = {}) {
|
|
332
|
+
return {
|
|
333
|
+
agentId: opts.agentId || 'main',
|
|
334
|
+
source: opts.source || 'codex',
|
|
335
|
+
sessionKey: opts.sessionKey || 'codex:cli',
|
|
336
|
+
workspace: opts.workspace || null,
|
|
337
|
+
project: opts.project || null,
|
|
338
|
+
repoPath: opts.repoPath || null,
|
|
339
|
+
codexHome: opts.codexHome || null,
|
|
340
|
+
hooksPath: opts.hooksPath || null,
|
|
341
|
+
sessionsDir: opts.sessionsDir || null,
|
|
342
|
+
stateDir: opts.stateDir || null,
|
|
343
|
+
excludeNewest: opts.excludeNewest !== false,
|
|
344
|
+
includeDeferredRecovery: opts.includeDeferredRecovery === true,
|
|
345
|
+
maxRecoveryCandidates: opts.maxRecoveryCandidates || null,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function buildDoctorReport(aquifer, opts = {}, env = process.env) {
|
|
350
|
+
const checks = [];
|
|
351
|
+
const hasWrapperEnv = Boolean(
|
|
352
|
+
env.CODEX_AQUIFER_AGENT_ID
|
|
353
|
+
|| env.CODEX_AQUIFER_SOURCE
|
|
354
|
+
|| env.CODEX_AQUIFER_SESSION_KEY
|
|
355
|
+
|| env.CODEX_HOME
|
|
356
|
+
|| env.CODEX_ENV_PATH,
|
|
357
|
+
);
|
|
358
|
+
if (hasWrapperEnv) {
|
|
359
|
+
addDoctorCheck(checks, 'wrapper_env', 'ok', 'Codex wrapper env is present.');
|
|
360
|
+
} else if (opts.strictWrapperEnv) {
|
|
361
|
+
addDoctorCheck(checks, 'wrapper_env', 'fail', 'Strict wrapper env requested, but no CODEX_AQUIFER_* or CODEX_HOME env was found.');
|
|
362
|
+
} else {
|
|
363
|
+
addDoctorCheck(checks, 'wrapper_env', 'warn', 'Using CLI defaults; pass --strict-wrapper-env for live wrapper deployment checks.');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (opts.excludeNewest === false) {
|
|
367
|
+
addDoctorCheck(checks, 'current_transcript_guard', 'fail', 'Current/newest transcript exclusion is disabled.');
|
|
368
|
+
} else {
|
|
369
|
+
addDoctorCheck(checks, 'current_transcript_guard', 'ok', 'Newest transcript exclusion is enabled.');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const heartbeatHook = inspectCheckpointHeartbeatHook(opts);
|
|
373
|
+
addDoctorCheck(
|
|
374
|
+
checks,
|
|
375
|
+
'checkpoint_heartbeat_hook',
|
|
376
|
+
heartbeatHook.status,
|
|
377
|
+
heartbeatHook.detail,
|
|
378
|
+
{ hooksPath: heartbeatHook.hooksPath, installed: heartbeatHook.installed },
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
let candidates = [];
|
|
382
|
+
try {
|
|
383
|
+
candidates = await listDbEligibleCandidates(aquifer, {
|
|
384
|
+
...opts,
|
|
385
|
+
idleMs: opts.idleMs ?? 0,
|
|
386
|
+
includeJsonlPreviews: true,
|
|
387
|
+
maxRecoveryCandidates: opts.maxRecoveryCandidates || 1,
|
|
388
|
+
});
|
|
389
|
+
addDoctorCheck(
|
|
390
|
+
checks,
|
|
391
|
+
'sessionstart_preflight',
|
|
392
|
+
'ok',
|
|
393
|
+
`Metadata-only recovery scan completed; eligibleCandidates=${candidates.length}.`,
|
|
394
|
+
{ eligibleCandidates: candidates.length },
|
|
395
|
+
);
|
|
396
|
+
} catch (err) {
|
|
397
|
+
addDoctorCheck(
|
|
398
|
+
checks,
|
|
399
|
+
'sessionstart_preflight',
|
|
400
|
+
'fail',
|
|
401
|
+
err && err.message ? err.message : String(err),
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const status = checks.some(check => check.status === 'fail')
|
|
406
|
+
? 'fail'
|
|
407
|
+
: checks.some(check => check.status === 'warn') ? 'warn' : 'ok';
|
|
408
|
+
return {
|
|
409
|
+
status,
|
|
410
|
+
checks,
|
|
411
|
+
options: compactDoctorOptions(opts),
|
|
412
|
+
candidates: candidates.map(compactCandidate),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
269
416
|
function parseIdList(value) {
|
|
270
417
|
if (!value || value === true) return new Set();
|
|
271
418
|
return new Set(String(value).split(',').map(part => part.trim()).filter(Boolean));
|
|
@@ -467,6 +614,42 @@ async function cmdDecision(aquifer, flags, opts) {
|
|
|
467
614
|
console.log(`Recovery ${verdict}: ${candidate.sessionId}`);
|
|
468
615
|
}
|
|
469
616
|
|
|
617
|
+
async function cmdDoctor(aquifer, flags, opts, env = process.env) {
|
|
618
|
+
const report = await buildDoctorReport(aquifer, opts, env);
|
|
619
|
+
printDoctorReport(report, flags);
|
|
620
|
+
return report;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function printDoctorReport(report = {}, flags = {}) {
|
|
624
|
+
if (flags.json) {
|
|
625
|
+
console.log(JSON.stringify(report, null, 2));
|
|
626
|
+
} else {
|
|
627
|
+
console.log(`Codex recovery doctor: ${report.status}`);
|
|
628
|
+
for (const check of report.checks || []) {
|
|
629
|
+
console.log(`- ${check.status} ${check.name}: ${check.detail}`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (report.status === 'fail') process.exitCode = 1;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
async function cmdDoctorInitFailure(flags, opts, err, env = process.env) {
|
|
636
|
+
let report = await buildDoctorReport(null, opts, env);
|
|
637
|
+
report = {
|
|
638
|
+
...report,
|
|
639
|
+
status: 'fail',
|
|
640
|
+
checks: [
|
|
641
|
+
{
|
|
642
|
+
name: 'aquifer_init',
|
|
643
|
+
status: 'fail',
|
|
644
|
+
detail: err && err.message ? err.message : String(err),
|
|
645
|
+
},
|
|
646
|
+
...(report.checks || []),
|
|
647
|
+
],
|
|
648
|
+
};
|
|
649
|
+
printDoctorReport(report, flags);
|
|
650
|
+
return report;
|
|
651
|
+
}
|
|
652
|
+
|
|
470
653
|
async function main(argv = process.argv.slice(2)) {
|
|
471
654
|
const args = parseArgs(argv);
|
|
472
655
|
const command = args._[0] || 'help';
|
|
@@ -478,9 +661,35 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
478
661
|
node scripts/codex-recovery.js hook-context [options]
|
|
479
662
|
node scripts/codex-recovery.js preview [options]
|
|
480
663
|
node scripts/codex-recovery.js prompt --session-id ID [options]
|
|
664
|
+
node scripts/codex-recovery.js checkpoint-prompt --file-path FILE --scope-key KEY [options]
|
|
665
|
+
node scripts/codex-recovery.js checkpoint-tick --scope-key KEY [--file-path FILE|--sessions-dir DIR] [options]
|
|
666
|
+
node scripts/codex-recovery.js checkpoint-heartbeat --hook-stdin --scope-key KEY [options]
|
|
667
|
+
node scripts/codex-recovery.js checkpoint-heartbeat-hook --scope-key KEY [--hooks-path FILE] [--apply]
|
|
481
668
|
node scripts/codex-recovery.js finalize --session-id ID --summary-stdin [options]
|
|
482
669
|
node scripts/codex-recovery.js decision --session-id ID --verdict declined|deferred [options]
|
|
483
|
-
node scripts/codex-recovery.js decision --all --verdict declined|deferred [options]
|
|
670
|
+
node scripts/codex-recovery.js decision --all --verdict declined|deferred [options]
|
|
671
|
+
node scripts/codex-recovery.js doctor [--strict-wrapper-env] [--json]`);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (command === 'doctor') {
|
|
676
|
+
try {
|
|
677
|
+
await withAquifer(async (aquifer) => {
|
|
678
|
+
await cmdDoctor(aquifer, args.flags, opts);
|
|
679
|
+
});
|
|
680
|
+
} catch (err) {
|
|
681
|
+
await cmdDoctorInitFailure(args.flags, opts, err);
|
|
682
|
+
}
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (command === 'checkpoint-heartbeat') {
|
|
687
|
+
await cmdCheckpointHeartbeat(null, args.flags, opts);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (command === 'checkpoint-heartbeat-hook') {
|
|
692
|
+
await cmdCheckpointHeartbeatHook(args.flags, opts);
|
|
484
693
|
return;
|
|
485
694
|
}
|
|
486
695
|
|
|
@@ -495,6 +704,12 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
495
704
|
case 'prompt':
|
|
496
705
|
await cmdPrompt(aquifer, args.flags, opts);
|
|
497
706
|
break;
|
|
707
|
+
case 'checkpoint-prompt':
|
|
708
|
+
await cmdCheckpointPrompt(aquifer, args.flags, opts);
|
|
709
|
+
break;
|
|
710
|
+
case 'checkpoint-tick':
|
|
711
|
+
await cmdCheckpointTick(aquifer, args.flags, opts);
|
|
712
|
+
break;
|
|
498
713
|
case 'finalize':
|
|
499
714
|
await cmdFinalize(aquifer, args.flags, opts);
|
|
500
715
|
break;
|
|
@@ -508,16 +723,46 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
508
723
|
}
|
|
509
724
|
|
|
510
725
|
module.exports = {
|
|
726
|
+
buildDoctorReport,
|
|
511
727
|
buildRecoveryOptions,
|
|
512
728
|
cmdDecision,
|
|
729
|
+
cmdDoctor,
|
|
730
|
+
cmdDoctorInitFailure,
|
|
513
731
|
cmdFinalize,
|
|
514
732
|
cmdHookContext,
|
|
733
|
+
cmdCheckpointHeartbeat,
|
|
734
|
+
cmdCheckpointHeartbeatHook,
|
|
735
|
+
cmdCheckpointPrompt,
|
|
736
|
+
cmdCheckpointTick,
|
|
515
737
|
cmdPrompt,
|
|
738
|
+
acquireHeartbeatClaim,
|
|
739
|
+
checkpointDueFromMarker,
|
|
740
|
+
checkpointHeartbeatCommand,
|
|
741
|
+
checkpointCheckIntervalMs,
|
|
742
|
+
checkpointEveryMessages,
|
|
743
|
+
checkpointEveryUserMessages,
|
|
744
|
+
checkpointQuietMs,
|
|
745
|
+
checkpointClaimDir,
|
|
746
|
+
checkpointClaimTtlMs,
|
|
747
|
+
checkpointMarkerDir,
|
|
748
|
+
checkpointSchedulerDir,
|
|
749
|
+
checkpointSpoolDir,
|
|
750
|
+
defaultHooksPath,
|
|
751
|
+
findNewestJsonlFile,
|
|
752
|
+
inspectCheckpointHeartbeatHook,
|
|
753
|
+
loadRuntimeConfig,
|
|
516
754
|
loadCodexEnv,
|
|
755
|
+
main,
|
|
756
|
+
mergeCheckpointHeartbeatHook,
|
|
517
757
|
parseArgs,
|
|
758
|
+
readCheckpointMarker,
|
|
759
|
+
readSchedulerMarker,
|
|
760
|
+
releaseHeartbeatClaim,
|
|
518
761
|
renderFinalizeCommand,
|
|
519
762
|
renderHookContext,
|
|
520
763
|
selectCandidate,
|
|
764
|
+
writeCheckpointMarker,
|
|
765
|
+
writeSchedulerMarker,
|
|
521
766
|
};
|
|
522
767
|
|
|
523
768
|
if (require.main === module) {
|