@shadowforge0/aquifer-memory 1.5.9 → 1.6.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.
Files changed (65) hide show
  1. package/.env.example +23 -0
  2. package/README.md +96 -73
  3. package/README_CN.md +659 -0
  4. package/README_TW.md +680 -0
  5. package/aquifer.config.example.json +34 -0
  6. package/consumers/claude-code.js +11 -11
  7. package/consumers/cli.js +374 -39
  8. package/consumers/codex-handoff.js +152 -0
  9. package/consumers/codex.js +1549 -0
  10. package/consumers/default/daily-entries.js +23 -4
  11. package/consumers/default/index.js +2 -2
  12. package/consumers/default/prompts/summary.js +6 -6
  13. package/consumers/mcp.js +131 -7
  14. package/consumers/openclaw-ext/index.js +0 -1
  15. package/consumers/openclaw-plugin.js +44 -4
  16. package/consumers/shared/config.js +28 -0
  17. package/consumers/shared/factory.js +2 -0
  18. package/consumers/shared/ingest.js +1 -1
  19. package/consumers/shared/normalize.js +14 -3
  20. package/consumers/shared/recall-format.js +53 -0
  21. package/consumers/shared/summary-parser.js +151 -0
  22. package/core/aquifer.js +384 -18
  23. package/core/finalization-review.js +319 -0
  24. package/core/insights.js +210 -58
  25. package/core/mcp-manifest.js +69 -2
  26. package/core/memory-bootstrap.js +188 -0
  27. package/core/memory-consolidation.js +1236 -0
  28. package/core/memory-promotion.js +544 -0
  29. package/core/memory-recall.js +247 -0
  30. package/core/memory-records.js +581 -0
  31. package/core/memory-safety-gate.js +224 -0
  32. package/core/session-finalization.js +350 -0
  33. package/core/storage.js +456 -2
  34. package/docs/getting-started.md +99 -0
  35. package/docs/postprocess-contract.md +2 -2
  36. package/docs/setup.md +51 -2
  37. package/package.json +31 -9
  38. package/pipeline/normalize/adapters/codex.js +106 -0
  39. package/pipeline/normalize/detect.js +3 -2
  40. package/schema/001-base.sql +3 -0
  41. package/schema/007-v1-foundation.sql +273 -0
  42. package/schema/008-session-finalizations.sql +50 -0
  43. package/schema/009-v1-assertion-plane.sql +193 -0
  44. package/schema/010-v1-finalization-review.sql +160 -0
  45. package/schema/011-v1-compaction-claim.sql +46 -0
  46. package/schema/012-v1-compaction-lease.sql +39 -0
  47. package/schema/013-v1-compaction-lineage.sql +193 -0
  48. package/scripts/backfill-canonical-key.js +250 -0
  49. package/scripts/codex-recovery.js +532 -0
  50. package/consumers/miranda/context-inject.js +0 -119
  51. package/consumers/miranda/daily-entries.js +0 -224
  52. package/consumers/miranda/index.js +0 -364
  53. package/consumers/miranda/instance.js +0 -55
  54. package/consumers/miranda/llm.js +0 -99
  55. package/consumers/miranda/profile.json +0 -145
  56. package/consumers/miranda/prompts/summary.js +0 -303
  57. package/consumers/miranda/recall-format.js +0 -76
  58. package/consumers/miranda/render-daily-md.js +0 -186
  59. package/consumers/miranda/workspace-files.js +0 -91
  60. package/scripts/drop-entity-state-history.sql +0 -17
  61. package/scripts/drop-insights.sql +0 -12
  62. package/scripts/install-openclaw.sh +0 -59
  63. package/scripts/queries.json +0 -45
  64. package/scripts/retro-recall-bench.js +0 -409
  65. package/scripts/sample-bench-queries.sql +0 -75
@@ -0,0 +1,151 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_SUMMARY_MARKERS = [
4
+ '===SESSION_ENTRIES===',
5
+ '===EMOTIONAL_STATE===',
6
+ '===RECAP===',
7
+ '===ENTITIES===',
8
+ '===WORKING_FACTS===',
9
+ '===HANDOFF===',
10
+ ];
11
+ const SUMMARY_MARKERS = DEFAULT_SUMMARY_MARKERS;
12
+
13
+ function parseSummaryOutput(output, markers = DEFAULT_SUMMARY_MARKERS) {
14
+ const sections = {};
15
+ for (let i = 0; i < markers.length; i++) {
16
+ const start = output.indexOf(markers[i]);
17
+ if (start === -1) continue;
18
+ const contentStart = start + markers[i].length;
19
+ let end = output.length;
20
+ for (let j = i + 1; j < markers.length; j++) {
21
+ const candidate = output.indexOf(markers[j], contentStart);
22
+ if (candidate !== -1) { end = candidate; break; }
23
+ }
24
+ const key = markers[i].replace(/===/g, '').toLowerCase();
25
+ sections[key] = (end > contentStart ? output.slice(contentStart, end) : output.slice(contentStart)).trim();
26
+ }
27
+ return sections;
28
+ }
29
+
30
+ function normalizeOpenOwner(raw) {
31
+ const owner = (raw || 'unknown').trim().toLowerCase();
32
+ if (['mk', 'agent', 'unknown'].includes(owner)) return owner;
33
+ if (/^[a-z][a-z0-9_-]{0,63}$/.test(owner)) return owner;
34
+ return 'unknown';
35
+ }
36
+
37
+ function parseRecapLines(text) {
38
+ const recap = {
39
+ title: '', overview: '', topics: [], decisions: [], actions_completed: [],
40
+ open_loops: [], files_mentioned: [], important_facts: [], reusable_patterns: [],
41
+ focus_decision: 'keep', focus: '', todo_new: [], todo_done: [],
42
+ };
43
+
44
+ for (const line of (text || '').split('\n')) {
45
+ const trimmed = line.trim();
46
+ if (!trimmed) continue;
47
+ const match = trimmed.match(/^([A-Z_]+):\s*(.*)/);
48
+ if (!match) continue;
49
+ const [, tag, value] = match;
50
+
51
+ switch (tag) {
52
+ case 'TITLE': recap.title = value; break;
53
+ case 'OVERVIEW': recap.overview = value; break;
54
+ case 'TOPIC': {
55
+ const p = value.split('|').map(s => s.trim());
56
+ if (p[0]) recap.topics.push({ name: p[0], summary: p[1] || '' });
57
+ break;
58
+ }
59
+ case 'DECISION': {
60
+ const p = value.split('|').map(s => s.trim());
61
+ if (p[0]) recap.decisions.push({ decision: p[0], reason: p[1] || '' });
62
+ break;
63
+ }
64
+ case 'ACTION': {
65
+ const p = value.split('|').map(s => s.trim());
66
+ if (p[0]) recap.actions_completed.push({
67
+ action: p[0],
68
+ status: (p[1] || 'done').toLowerCase() === 'partial' ? 'partial' : 'done',
69
+ });
70
+ break;
71
+ }
72
+ case 'OPEN': {
73
+ const p = value.split('|').map(s => s.trim());
74
+ if (p[0]) recap.open_loops.push({
75
+ item: p[0],
76
+ owner: normalizeOpenOwner(p[1]),
77
+ });
78
+ break;
79
+ }
80
+ case 'FACT': if (value) recap.important_facts.push(value); break;
81
+ case 'PATTERN': {
82
+ const p = value.split('|').map(s => s.trim());
83
+ if (p[0] && p[1]) recap.reusable_patterns.push({
84
+ pattern: p[0], trigger: p[1], action: p[2] || '',
85
+ durability: (p[3] || 'derived').toLowerCase() === 'invariant' ? 'invariant' : 'derived',
86
+ });
87
+ break;
88
+ }
89
+ case 'FOCUS_DECISION':
90
+ recap.focus_decision = value.toLowerCase().trim() === 'update' ? 'update' : 'keep';
91
+ break;
92
+ case 'FOCUS': recap.focus = value; break;
93
+ case 'TODO_NEW': if (value) recap.todo_new.push(value); break;
94
+ case 'TODO_DONE': if (value) recap.todo_done.push(value); break;
95
+ }
96
+ }
97
+ return recap;
98
+ }
99
+
100
+ function parseWorkingFacts(text) {
101
+ if (!text || typeof text !== 'string') return [];
102
+ const facts = [];
103
+ for (const line of text.split('\n')) {
104
+ const m = line.trim().match(/^WFACT:\s*(.+?)\s*\|\s*(.+)/);
105
+ if (!m) continue;
106
+ const subject = m[1].trim().slice(0, 100);
107
+ const statement = m[2].trim().slice(0, 500);
108
+ if (!subject || !statement) continue;
109
+ facts.push({ subject, statement });
110
+ if (facts.length >= 5) break;
111
+ }
112
+ return facts;
113
+ }
114
+
115
+ const VALID_HANDOFF_STATUS = new Set(['in_progress', 'interrupted', 'completed', 'blocked']);
116
+ const VALID_STOP_REASON = new Set(['natural', 'interrupted', 'blocked', 'context_full']);
117
+
118
+ function normalizeEnum(raw, validSet) {
119
+ const v = raw.trim().toLowerCase().replace(/-/g, '_').replace(/\s+/g, '_');
120
+ return validSet.has(v) ? v : null;
121
+ }
122
+
123
+ function parseHandoffSection(text) {
124
+ if (!text || typeof text !== 'string') return null;
125
+ const handoff = { status: 'completed', lastStep: '', next: '', stopReason: 'natural', decided: '', blocker: '' };
126
+ for (const line of text.split('\n')) {
127
+ const m = line.trim().match(/^([A-Z_]+):\s*(.*)/);
128
+ if (!m) continue;
129
+ const [, tag, value] = m;
130
+ switch (tag) {
131
+ case 'STATUS': handoff.status = normalizeEnum(value, VALID_HANDOFF_STATUS) || 'completed'; break;
132
+ case 'LAST_STEP': handoff.lastStep = value.trim().slice(0, 200); break;
133
+ case 'NEXT': handoff.next = value.trim().slice(0, 200); break;
134
+ case 'STOP_REASON': handoff.stopReason = normalizeEnum(value, VALID_STOP_REASON) || 'natural'; break;
135
+ case 'DECIDED': handoff.decided = value.trim().slice(0, 200); break;
136
+ case 'BLOCKER': handoff.blocker = value.trim().slice(0, 200); break;
137
+ }
138
+ }
139
+ if (!handoff.lastStep || !handoff.next) return null;
140
+ return handoff;
141
+ }
142
+
143
+ module.exports = {
144
+ DEFAULT_SUMMARY_MARKERS,
145
+ SUMMARY_MARKERS,
146
+ parseSummaryOutput,
147
+ parseRecapLines,
148
+ parseWorkingFacts,
149
+ parseHandoffSection,
150
+ normalizeOpenOwner,
151
+ };