@shadowforge0/aquifer-memory 1.9.0 → 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/README.md +33 -4
- package/README_CN.md +9 -1
- package/README_TW.md +5 -2
- package/consumers/cli.js +55 -34
- package/consumers/codex-active-checkpoint.js +3 -1
- package/consumers/codex-current-memory.js +10 -6
- package/consumers/codex.js +5 -2
- 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 -49
- package/consumers/openclaw-ext/index.js +1 -1
- package/consumers/openclaw-ext/openclaw.plugin.json +1 -1
- package/consumers/openclaw-ext/package.json +1 -1
- package/consumers/openclaw-plugin.js +66 -23
- package/consumers/shared/compat-recall.js +101 -0
- package/consumers/shared/openclaw-product-tools.js +130 -0
- package/consumers/shared/recall-format.js +2 -2
- package/core/aquifer.js +385 -20
- package/core/backends/local.js +60 -1
- 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 +20 -51
- package/core/memory-promotion.js +392 -55
- package/core/memory-recall.js +26 -48
- package/core/memory-records.js +91 -103
- package/core/memory-type-policy.js +298 -0
- package/core/postgres-migrations.js +9 -0
- package/core/session-checkpoint-producer.js +3 -1
- package/core/session-checkpoints.js +1 -1
- package/core/session-finalization.js +2 -2
- package/docs/getting-started.md +16 -3
- package/docs/setup.md +61 -2
- package/package.json +2 -2
- package/schema/004-completion.sql +4 -4
- package/schema/010-v1-finalization-review.sql +72 -0
- package/schema/020-v1-assistant-shaping-memory.sql +30 -0
- package/scripts/backfill-canonical-key.js +1 -1
- package/scripts/diagnose-fts-zh.js +1 -1
- package/scripts/extract-insights-from-recent-sessions.js +4 -4
package/README.md
CHANGED
|
@@ -93,12 +93,41 @@ Curated serving is scope-bound. `AQUIFER_MEMORY_ACTIVE_SCOPE_PATH` is the ordere
|
|
|
93
93
|
| Heartbeat-check an active Codex session for checkpoint work | `npx aquifer codex-recovery checkpoint-heartbeat --hook-stdin --scope-key project:aquifer` |
|
|
94
94
|
| Inspect pending Codex checkpoint spool files | `npx aquifer codex-recovery checkpoint-spool-status --json --limit 10` |
|
|
95
95
|
| Preview a Codex UserPromptSubmit heartbeat hook install | `npx aquifer codex-recovery checkpoint-heartbeat-hook --scope-key project:aquifer --hooks-path "$CODEX_HOME/hooks.json" --json` |
|
|
96
|
-
|
|
|
96
|
+
| Check memory readiness | `npx aquifer stats` |
|
|
97
|
+
| Check saved-content preparation | `npx aquifer backlog --json` |
|
|
98
|
+
| Dry-run a saved-content policy decision | `npx aquifer backlog --plan skip --status pending --source openclaw-mcp` |
|
|
97
99
|
| Enrich pending sessions | `npx aquifer backfill` |
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
`stats`, `backlog`, MCP `memory_stats`, and MCP `memory_pending` default to the
|
|
102
|
+
same public status surface: `Aquifer status` or `Saved content status`, plus
|
|
103
|
+
`Available`, `Attention`, and `Action` where relevant. Use `stats --diagnostics`,
|
|
104
|
+
`backlog --diagnostics`, or MCP `diagnostics: true` for raw counters, buckets,
|
|
105
|
+
guidance, and samples.
|
|
106
|
+
|
|
107
|
+
Reviewed timer synthesis is gated before promotion. Candidate items must carry
|
|
108
|
+
`mergeKey`, `scopeClass`, `durability`, `promotionTarget`, and
|
|
109
|
+
`sourceCanonicalKeys` that reference `sourceCurrentMemory`; runtime state also
|
|
110
|
+
needs `staleAfter` or `validTo`. The temporal distillation gate rejects
|
|
111
|
+
workspace/operator policy, transient material, unsupported promotion targets,
|
|
112
|
+
invalid or missing source lineage, and duplicate merge keys before current
|
|
113
|
+
memory promotion. `assistant_shaping` may target `assistant_behavior_memory`
|
|
114
|
+
only when the item carries behavior-level abstraction metadata:
|
|
115
|
+
`languageLevel=user_behavior`, `appliesBeyondSource=true`, `sourceBound=false`,
|
|
116
|
+
and a `principle`. Project-specific source wording stays lineage/context
|
|
117
|
+
material; serving memory uses the behavior principle.
|
|
118
|
+
|
|
119
|
+
To serve user-level assistant behavior alongside a project, include the user
|
|
120
|
+
scope in the active path, for example
|
|
121
|
+
`AQUIFER_MEMORY_ACTIVE_SCOPE_PATH=global,user:mk,project:aquifer` with matching
|
|
122
|
+
allowed scope keys. Applicable `assistant_shaping` records are pinned ahead of
|
|
123
|
+
ordinary project current memory in bootstrap.
|
|
124
|
+
|
|
125
|
+
Timer synthesis output is candidate material until an operator applies a
|
|
126
|
+
reviewed synthesis summary with `--promote-candidates`; it does not become
|
|
127
|
+
active curated memory from the prompt or summary file alone. The deterministic
|
|
128
|
+
daily/weekly/monthly aggregate proposals in a dry-run are source-rollup
|
|
129
|
+
material for review and ledger lineage only, and are blocked from normal active
|
|
130
|
+
promotion unless a reviewed synthesis summary is attached.
|
|
102
131
|
|
|
103
132
|
Checkpoint output follows the same boundary. `operator checkpoint` plans from
|
|
104
133
|
finalized session summaries and only writes `checkpoint_runs` when you pass an
|
package/README_CN.md
CHANGED
|
@@ -126,9 +126,17 @@ Claude Code、Claude Desktop 或任何支持 MCP 的 client——放进 `.mcp.js
|
|
|
126
126
|
| Generate a timer synthesis prompt | `npx aquifer operator compaction daily --include-synthesis-prompt --json` |
|
|
127
127
|
| Apply reviewed timer synthesis candidates | `npx aquifer operator compaction daily --synthesis-summary-file /tmp/timer-summary.json --apply --promote-candidates --json` |
|
|
128
128
|
| Inspect pending Codex checkpoint spool files | `npx aquifer codex-recovery checkpoint-spool-status --json --limit 10` |
|
|
129
|
-
|
|
|
129
|
+
| Check memory readiness | `npx aquifer stats` |
|
|
130
|
+
| Check saved-content preparation | `npx aquifer backlog --json` |
|
|
131
|
+
| Dry-run a saved-content policy decision | `npx aquifer backlog --plan skip --status pending --source openclaw-mcp` |
|
|
130
132
|
| Enrich pending sessions | `npx aquifer backfill` |
|
|
131
133
|
|
|
134
|
+
`stats`, `backlog`, MCP `memory_stats`, and MCP `memory_pending` default to the
|
|
135
|
+
same public status surface: `Aquifer status` or `Saved content status`, plus
|
|
136
|
+
`Available`, `Attention`, and `Action` where relevant. Use CLI `--diagnostics`
|
|
137
|
+
or MCP `diagnostics: true` when a host needs raw counters, buckets, guidance,
|
|
138
|
+
or samples.
|
|
139
|
+
|
|
132
140
|
Timer synthesis output is candidate material until an operator applies it with
|
|
133
141
|
`--promote-candidates`; it does not become active curated memory from the
|
|
134
142
|
prompt or summary file alone.
|
package/README_TW.md
CHANGED
|
@@ -86,8 +86,11 @@ Curated serving 受 scope 邊界保護。`AQUIFER_MEMORY_ACTIVE_SCOPE_PATH` 是
|
|
|
86
86
|
| 產生 timer synthesis prompt | `npx aquifer operator compaction daily --include-synthesis-prompt --json` |
|
|
87
87
|
| 套用已審核的 timer synthesis candidates | `npx aquifer operator compaction daily --synthesis-summary-file /tmp/timer-summary.json --apply --promote-candidates --json` |
|
|
88
88
|
| 檢查 Codex checkpoint spool 待處理檔 | `npx aquifer codex-recovery checkpoint-spool-status --json --limit 10` |
|
|
89
|
-
|
|
|
90
|
-
|
|
|
89
|
+
| 看記憶是否可用 | `npx aquifer stats` |
|
|
90
|
+
| 檢查已儲存內容準備狀態 | `npx aquifer backlog --json` |
|
|
91
|
+
| 準備已儲存內容 | `npx aquifer backfill` |
|
|
92
|
+
|
|
93
|
+
`stats`、`backlog`、MCP `memory_stats`、MCP `memory_pending` 預設共用同一個 public status surface:`Aquifer status` 或 `Saved content status`,必要時再加 `Available`、`Attention`、`Action`。需要 raw counters、source / agent / status 分桶、guidance 或 samples 時,CLI 用 `--diagnostics`,MCP 傳 `diagnostics: true`。
|
|
91
94
|
|
|
92
95
|
Timer synthesis output 在 operator 用 `--promote-candidates` apply 前都只是 candidate material;光有 prompt 或 summary file 不會變成 active curated memory。
|
|
93
96
|
`codex-recovery checkpoint-spool-status --json` 只列出本機 spool 檔的 session、coverage、大小與修改時間,不會印出 checkpoint prompt 內容。
|
package/consumers/cli.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* aquifer migrate Run database migrations
|
|
10
10
|
* aquifer recall <query> [options] Search sessions
|
|
11
11
|
* aquifer backfill [options] Enrich pending sessions
|
|
12
|
-
* aquifer
|
|
12
|
+
* aquifer backlog [options] Explain saved-content preparation work
|
|
13
|
+
* aquifer stats [options] Check memory readiness and diagnostics
|
|
13
14
|
* aquifer backend-info [--json] Show selected backend capabilities
|
|
14
15
|
* aquifer install-openclaw Install/update OpenClaw MCP + extension wiring
|
|
15
16
|
* aquifer finalization ... Inspect read-only finalization ledger
|
|
@@ -24,12 +25,12 @@ const { loadConfig } = require('./shared/config');
|
|
|
24
25
|
const { formatRecallResults } = require('./shared/recall-format');
|
|
25
26
|
const { backendCapabilities } = require('../core/backends/capabilities');
|
|
26
27
|
const { summarizeFinalizationListRow } = require('../core/finalization-inspector');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
28
|
+
const {
|
|
29
|
+
buildBacklogEnvelope,
|
|
30
|
+
buildStatsEnvelope,
|
|
31
|
+
formatMemoryStatsInterface,
|
|
32
|
+
formatPendingWorkInterface,
|
|
33
|
+
} = require('../core/interface');
|
|
33
34
|
|
|
34
35
|
function quoteIdentifier(identifier) {
|
|
35
36
|
if (!/^[a-zA-Z_]\w{0,62}$/.test(identifier)) {
|
|
@@ -45,6 +46,12 @@ function parsePositiveInt(value, fallback) {
|
|
|
45
46
|
return Math.max(1, parsed);
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
function printTextBlock(text) {
|
|
50
|
+
for (const line of String(text).split(/\r?\n/)) {
|
|
51
|
+
console.log(line);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
function parseScopePath(value) {
|
|
49
56
|
if (!value) return undefined;
|
|
50
57
|
const parts = String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
@@ -180,6 +187,7 @@ function parseArgs(argv) {
|
|
|
180
187
|
// Flags that take a value (not boolean)
|
|
181
188
|
const VALUE_FLAGS = new Set([
|
|
182
189
|
'limit', 'agent-id', 'source', 'date-from', 'date-to', 'output', 'format', 'config', 'status',
|
|
190
|
+
'plan', 'action',
|
|
183
191
|
'concurrency', 'entities', 'entity-mode', 'mode', 'session-id', 'memory-id', 'canonical-key',
|
|
184
192
|
'verdict', 'feedback-type', 'note', 'db', 'since', 'min-messages', 'lookback-days', 'max-chars',
|
|
185
193
|
'out', 'active-scope-key', 'active-scope-path', 'cadence', 'period-start', 'period-end',
|
|
@@ -408,37 +416,42 @@ async function cmdBackfill(aquifer, args) {
|
|
|
408
416
|
if (failed > 0) process.exitCode = 2;
|
|
409
417
|
}
|
|
410
418
|
|
|
419
|
+
async function cmdBacklog(aquifer, args) {
|
|
420
|
+
const action = args.flags.plan || args.flags.action || 'inspect';
|
|
421
|
+
const opts = {
|
|
422
|
+
limit: parsePositiveInt(args.flags.limit, 50),
|
|
423
|
+
source: args.flags.source || undefined,
|
|
424
|
+
agentId: args.flags['agent-id'] || undefined,
|
|
425
|
+
status: args.flags.status || undefined,
|
|
426
|
+
action,
|
|
427
|
+
};
|
|
428
|
+
const report = await aquifer.getPendingWork(opts);
|
|
429
|
+
|
|
430
|
+
if (args.flags.json) {
|
|
431
|
+
console.log(JSON.stringify(buildBacklogEnvelope(report, {
|
|
432
|
+
diagnostics: args.flags.diagnostics === true,
|
|
433
|
+
includePlan: true,
|
|
434
|
+
}), null, 2));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
printTextBlock(formatPendingWorkInterface(report, {
|
|
439
|
+
diagnostics: args.flags.diagnostics === true,
|
|
440
|
+
includePlan: true,
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
|
|
411
444
|
async function cmdStats(aquifer, args) {
|
|
412
445
|
const stats = await aquifer.getStats();
|
|
413
446
|
|
|
414
447
|
if (args.flags.json) {
|
|
415
|
-
console.log(JSON.stringify(stats,
|
|
448
|
+
console.log(JSON.stringify(buildStatsEnvelope(stats, {
|
|
449
|
+
diagnostics: args.flags.diagnostics === true,
|
|
450
|
+
}), null, 2));
|
|
416
451
|
} else {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
}
|
|
424
|
-
console.log(`Summaries: ${stats.summaries}`);
|
|
425
|
-
console.log(`Turn embeddings: ${stats.turnEmbeddings}`);
|
|
426
|
-
console.log(`Entities: ${stats.entities}`);
|
|
427
|
-
if (stats.memoryRecords) {
|
|
428
|
-
console.log(`Memory records: ${stats.memoryRecords.total} (${stats.memoryRecords.active} active, ${stats.memoryRecords.visibleInRecall} recall-visible, ${stats.memoryRecords.visibleInBootstrap} bootstrap-visible)`);
|
|
429
|
-
if (stats.memoryRecords.latest) console.log(`Memory record range: ${formatDate(stats.memoryRecords.earliest, '?')} — ${formatDate(stats.memoryRecords.latest, '?')}`);
|
|
430
|
-
}
|
|
431
|
-
if (stats.sessionFinalizations?.available) {
|
|
432
|
-
const statusText = Object.entries(stats.sessionFinalizations.statuses || {})
|
|
433
|
-
.map(([status, count]) => `${status}: ${count}`)
|
|
434
|
-
.join(', ') || 'none';
|
|
435
|
-
console.log(`Session finalizations: ${stats.sessionFinalizations.total} (${statusText})`);
|
|
436
|
-
if (stats.sessionFinalizations.latestFinalizedAt) console.log(`Latest finalization: ${formatDate(stats.sessionFinalizations.latestFinalizedAt, '?')}`);
|
|
437
|
-
}
|
|
438
|
-
if (stats.earliest) console.log(`Range: ${formatDate(stats.earliest, '?')} — ${formatDate(stats.latest, '?')}`);
|
|
439
|
-
if ((stats.serving?.mode || 'legacy') !== 'curated') {
|
|
440
|
-
console.log('Warning: legacy serving returns session/evidence material; configure curated serving with an active scope for current-memory answers.');
|
|
441
|
-
}
|
|
452
|
+
printTextBlock(formatMemoryStatsInterface(stats, {
|
|
453
|
+
diagnostics: args.flags.diagnostics === true,
|
|
454
|
+
}));
|
|
442
455
|
}
|
|
443
456
|
}
|
|
444
457
|
|
|
@@ -1300,11 +1313,12 @@ Commands:
|
|
|
1300
1313
|
feedback-stats Show trust feedback statistics and coverage
|
|
1301
1314
|
review ... Inspect or resolve memory feedback review queue
|
|
1302
1315
|
backfill Enrich pending sessions
|
|
1316
|
+
backlog Explain and plan saved-content preparation work
|
|
1303
1317
|
operator ... Run operator-safe consolidation jobs
|
|
1304
1318
|
finalization ... Inspect read-only finalization ledger
|
|
1305
1319
|
explain ... Explain current-memory serving decisions
|
|
1306
1320
|
compact Plan or apply curated memory compaction
|
|
1307
|
-
stats
|
|
1321
|
+
stats Check memory readiness
|
|
1308
1322
|
export Export sessions as JSONL
|
|
1309
1323
|
bootstrap Show recent session context (for new session start)
|
|
1310
1324
|
codex-recovery ... Inspect or run Codex recovery/checkpoint flows
|
|
@@ -1322,6 +1336,9 @@ Options:
|
|
|
1322
1336
|
--id ID Finalization row id
|
|
1323
1337
|
--host HOST Finalization host filter
|
|
1324
1338
|
--status STATUS Finalization status filter
|
|
1339
|
+
--plan inspect|backfill|skip
|
|
1340
|
+
Saved-content dry-run action plan
|
|
1341
|
+
--diagnostics Include raw counters, buckets, samples, and serving diagnostics
|
|
1325
1342
|
--phase PHASE Finalization phase filter
|
|
1326
1343
|
--transcript-hash HASH Finalization transcript hash filter
|
|
1327
1344
|
--run-id ID Operator run id
|
|
@@ -1520,6 +1537,9 @@ Operator examples:
|
|
|
1520
1537
|
case 'backfill':
|
|
1521
1538
|
await cmdBackfill(aquifer, args);
|
|
1522
1539
|
break;
|
|
1540
|
+
case 'backlog':
|
|
1541
|
+
await cmdBacklog(aquifer, args);
|
|
1542
|
+
break;
|
|
1523
1543
|
case 'operator':
|
|
1524
1544
|
await cmdOperator(aquifer, args);
|
|
1525
1545
|
break;
|
|
@@ -1564,6 +1584,7 @@ module.exports = {
|
|
|
1564
1584
|
cmdDoctor,
|
|
1565
1585
|
cmdLocalQuickstart,
|
|
1566
1586
|
cmdOperator,
|
|
1587
|
+
cmdBacklog,
|
|
1567
1588
|
cmdFinalization,
|
|
1568
1589
|
cmdExplain,
|
|
1569
1590
|
cmdReview,
|
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
promptSafeSynthesisInput,
|
|
7
7
|
stableJson,
|
|
8
8
|
} = require('../core/session-checkpoint-producer');
|
|
9
|
+
const { assistantShapingPromptLines } = require('../core/memory-type-policy');
|
|
9
10
|
const { compactCurrentMemorySnapshot } = require('./codex-current-memory');
|
|
10
11
|
|
|
11
12
|
function positiveInt(value, fallback, max = 100000) {
|
|
@@ -122,8 +123,9 @@ function buildActiveSessionCheckpointPrompt(checkpointInput = {}, opts = {}) {
|
|
|
122
123
|
'Use only the <active_checkpoint_input> block. Do not use hidden tool output, injected context, or debug material.',
|
|
123
124
|
'This checkpoint is process material for later handoff. It is not active current memory and must not be treated as final truth.',
|
|
124
125
|
'Return compact JSON with this shape:',
|
|
125
|
-
'{"summaryText":"...","structuredSummary":{"facts":[],"decisions":[],"open_loops":[],"preferences":[],"constraints":[],"conclusions":[],"entity_notes":[],"states":[]},"coverage":{"coordinateSystem":"codex_sanitized_view_v1","coveredUntilMessageIndex":0,"coveredUntilChar":0}}',
|
|
126
|
+
'{"summaryText":"...","structuredSummary":{"assistant_shaping":[],"facts":[],"decisions":[],"open_loops":[],"preferences":[],"constraints":[],"conclusions":[],"entity_notes":[],"states":[]},"coverage":{"coordinateSystem":"codex_sanitized_view_v1","coveredUntilMessageIndex":0,"coveredUntilChar":0}}',
|
|
126
127
|
`Keep facts/decisions/open_loops concrete and scoped. Use at most ${maxFacts} facts.`,
|
|
128
|
+
...assistantShapingPromptLines(),
|
|
127
129
|
'Preserve the coverage object so a later handoff can skip the already-covered transcript prefix.',
|
|
128
130
|
'',
|
|
129
131
|
'<active_checkpoint_input>',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { formatRuntimeMemoryLine } = require('../core/memory-type-policy');
|
|
4
|
+
|
|
3
5
|
function compactCurrentMemoryRow(row = {}) {
|
|
4
6
|
const payload = row.payload && typeof row.payload === 'object' ? row.payload : {};
|
|
5
7
|
const confidence = payload.confidence || payload.currentMemoryConfidence || null;
|
|
@@ -10,6 +12,9 @@ function compactCurrentMemoryRow(row = {}) {
|
|
|
10
12
|
summary: String(row.summary || row.title || '').replace(/\s+/g, ' ').trim(),
|
|
11
13
|
authority: row.authority || null,
|
|
12
14
|
confidence,
|
|
15
|
+
policyKey: payload.policyKey || payload.policy_key || row.policyKey || row.policy_key || null,
|
|
16
|
+
shapingKind: payload.shapingKind || payload.shaping_kind || payload.kind || payload.category || row.shapingKind || row.shaping_kind || null,
|
|
17
|
+
servingImpact: payload.servingImpact || payload.serving_impact || payload.impact || row.servingImpact || row.serving_impact || null,
|
|
13
18
|
};
|
|
14
19
|
}
|
|
15
20
|
|
|
@@ -35,12 +40,11 @@ function formatCurrentMemoryPromptBlock(currentMemory = null, opts = {}) {
|
|
|
35
40
|
`truncated="${Boolean(meta.truncated || rows.length > compactRows.length)}"`,
|
|
36
41
|
`degraded="${Boolean(meta.degraded || currentMemory?.error)}"`,
|
|
37
42
|
];
|
|
38
|
-
const lines = compactRows.map(row => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
43
|
+
const lines = compactRows.map(row => formatRuntimeMemoryLine(row, {
|
|
44
|
+
includeScope: true,
|
|
45
|
+
includeAuthority: true,
|
|
46
|
+
includeConfidence: true,
|
|
47
|
+
}));
|
|
44
48
|
if (currentMemory && currentMemory.error && lines.length === 0) {
|
|
45
49
|
lines.push(`- degraded: ${String(currentMemory.error).replace(/\s+/g, ' ').trim()}`);
|
|
46
50
|
}
|
package/consumers/codex.js
CHANGED
|
@@ -22,6 +22,7 @@ const crypto = require('crypto');
|
|
|
22
22
|
const DEFAULT_CODEX_HOME = path.join(os.homedir(), '.codex');
|
|
23
23
|
const { normalizeMessages } = require('./shared/normalize');
|
|
24
24
|
const { applyEnrichSafetyGate } = require('../core/memory-safety-gate');
|
|
25
|
+
const { assistantShapingPromptLines } = require('../core/memory-type-policy');
|
|
25
26
|
const {
|
|
26
27
|
buildActiveSessionCheckpointInput,
|
|
27
28
|
buildActiveSessionCheckpointPrompt,
|
|
@@ -1335,8 +1336,9 @@ function buildFinalizationPrompt(view = {}, opts = {}) {
|
|
|
1335
1336
|
'You are finalizing an Aquifer memory session for Codex.',
|
|
1336
1337
|
'Use only the sanitized transcript below. Do not infer from hidden tool output or injected context.',
|
|
1337
1338
|
'Return compact JSON with this shape:',
|
|
1338
|
-
'{"summaryText":"...","structuredSummary":{"facts":[],"decisions":[],"open_loops":[],"preferences":[],"constraints":[],"conclusions":[],"entity_notes":[],"states":[]}}',
|
|
1339
|
+
'{"summaryText":"...","structuredSummary":{"assistant_shaping":[],"facts":[],"decisions":[],"open_loops":[],"preferences":[],"constraints":[],"conclusions":[],"entity_notes":[],"states":[]}}',
|
|
1339
1340
|
`Keep facts/decisions/open_loops concrete and scoped. Use at most ${maxFacts} facts.`,
|
|
1341
|
+
...assistantShapingPromptLines(),
|
|
1340
1342
|
'',
|
|
1341
1343
|
`sessionId: ${view.sessionId}`,
|
|
1342
1344
|
`transcriptHash: ${view.transcriptHash}`,
|
|
@@ -1352,7 +1354,8 @@ function buildFinalizationPrompt(view = {}, opts = {}) {
|
|
|
1352
1354
|
0,
|
|
1353
1355
|
'Use current_memory as the already-committed current state. Reconcile the transcript against it: keep valid state, supersede stale state, and mark uncertain items explicitly.',
|
|
1354
1356
|
);
|
|
1355
|
-
lines.
|
|
1357
|
+
const transcriptIndex = lines.indexOf('<sanitized_transcript>');
|
|
1358
|
+
lines.splice(transcriptIndex >= 0 ? transcriptIndex : lines.length, 0, formatCurrentMemoryPromptBlock(opts.currentMemory, opts), '');
|
|
1356
1359
|
}
|
|
1357
1360
|
return lines.join('\n');
|
|
1358
1361
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// Aquifer default persona — parameterized daily_entries writer.
|
|
4
|
-
// Schema matches
|
|
4
|
+
// Schema matches the generic daily_entries shape (id / event_at / source / tag / text /
|
|
5
5
|
// agent_id / session_id / metadata / dedupe_key) — hosts clone that DDL into
|
|
6
6
|
// their own schema and set persona.dailyTable = '<schema>.daily_entries'.
|
|
7
7
|
//
|
|
8
8
|
// Host must create the table before use:
|
|
9
|
-
// CREATE TABLE
|
|
9
|
+
// CREATE TABLE app_memory.daily_entries (...);
|
|
10
10
|
|
|
11
11
|
const crypto = require('crypto');
|
|
12
12
|
const { parseHandoffSection } = require('../shared/summary-parser');
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
// Entry point:
|
|
7
7
|
// const persona = require('@shadowforge0/aquifer-memory/consumers/default')
|
|
8
8
|
// .createPersona({
|
|
9
|
-
// agentName: '
|
|
10
|
-
// observedOwner: '
|
|
11
|
-
// schema: '
|
|
12
|
-
// scope: '
|
|
13
|
-
// dailyTable: '
|
|
9
|
+
// agentName: 'Assistant',
|
|
10
|
+
// observedOwner: 'operator',
|
|
11
|
+
// schema: 'app_memory',
|
|
12
|
+
// scope: 'app_memory',
|
|
13
|
+
// dailyTable: 'app_memory.daily_entries', // or null to skip daily writes
|
|
14
14
|
// language: 'zh-TW', // or 'en'
|
|
15
|
-
// briefingIntro: '
|
|
15
|
+
// briefingIntro: '以下是現況...', // optional context-inject preamble
|
|
16
16
|
// });
|
|
17
17
|
//
|
|
18
18
|
// Returns a persona module with the standard persona adapter shape — host
|
|
@@ -21,13 +21,15 @@
|
|
|
21
21
|
// .createPersona({ ... });
|
|
22
22
|
//
|
|
23
23
|
// This is intentionally a minimal persona: summary + optional daily_entries,
|
|
24
|
-
// no workspace-files, no consolidation, no
|
|
24
|
+
// no workspace-files, no consolidation, no host-specific scaffolding.
|
|
25
25
|
// Host can extend by composing with Aquifer primitives from consumers/shared.
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
28
|
const { createAquifer } = require('../../index');
|
|
29
29
|
const { runIngest } = require('../shared/ingest');
|
|
30
30
|
const { parseEntitySection } = require('../shared/entity-parser');
|
|
31
|
+
const { registerOpenClawProductStatusTools } = require('../shared/openclaw-product-tools');
|
|
32
|
+
const { buildCompatibilityRecallRequest, runCompatibilityRecall, memoryServingMode } = require('../shared/compat-recall');
|
|
31
33
|
|
|
32
34
|
const summaryModule = require('./prompts/summary');
|
|
33
35
|
const dailyEntriesModule = require('./daily-entries');
|
|
@@ -129,10 +131,10 @@ function createPersona(personaOpts = {}) {
|
|
|
129
131
|
// v1.2.0: all four are env-driven by default. Host may override any of
|
|
130
132
|
// them via opts. Aquifer core throws with clear guidance if the required
|
|
131
133
|
// env vars are missing, so we do not pre-validate here.
|
|
132
|
-
const aquifer = getAquifer(opts);
|
|
133
|
-
const pool = opts.pool || aquifer.getPool();
|
|
134
|
-
const llmFn = opts.llmFn || aquifer.getLlmFn();
|
|
135
|
-
const embedFn = opts.embedFn || aquifer.getEmbedFn();
|
|
134
|
+
const aquifer = opts.aquifer || getAquifer(opts);
|
|
135
|
+
const pool = opts.pool || (aquifer.getPool ? aquifer.getPool() : null);
|
|
136
|
+
const llmFn = opts.llmFn || (aquifer.getLlmFn ? aquifer.getLlmFn() : null);
|
|
137
|
+
const embedFn = opts.embedFn || (aquifer.getEmbedFn ? aquifer.getEmbedFn() : null);
|
|
136
138
|
return {
|
|
137
139
|
pool,
|
|
138
140
|
embedFn,
|
|
@@ -205,7 +207,16 @@ function createPersona(personaOpts = {}) {
|
|
|
205
207
|
try {
|
|
206
208
|
const agentId = ctx?.agentId || defaultAgentId;
|
|
207
209
|
if ((ctx?.sessionKey || '').includes('subagent')) return;
|
|
208
|
-
const
|
|
210
|
+
const bootstrapOpts = { limit: 5, maxChars: 2000, format: 'text' };
|
|
211
|
+
if (memoryServingMode(aquifer) === 'curated') {
|
|
212
|
+
const activeScopeKey = opts.activeScopeKey || opts.active_scope_key || ctx?.activeScopeKey || ctx?.active_scope_key;
|
|
213
|
+
const activeScopePath = opts.activeScopePath || opts.active_scope_path || ctx?.activeScopePath || ctx?.active_scope_path;
|
|
214
|
+
if (activeScopeKey) bootstrapOpts.activeScopeKey = activeScopeKey;
|
|
215
|
+
if (activeScopePath) bootstrapOpts.activeScopePath = activeScopePath;
|
|
216
|
+
} else {
|
|
217
|
+
bootstrapOpts.agentId = agentId;
|
|
218
|
+
}
|
|
219
|
+
const recalled = await aquifer.bootstrap(bootstrapOpts);
|
|
209
220
|
const context = persona.briefingIntro + (recalled ? `\n\n${recalled}` : '');
|
|
210
221
|
if (context.length > 0) return { prependSystemContext: context };
|
|
211
222
|
} catch (err) {
|
|
@@ -232,33 +243,23 @@ function createPersona(personaOpts = {}) {
|
|
|
232
243
|
source: { type: 'string' },
|
|
233
244
|
date_from: { type: 'string' },
|
|
234
245
|
date_to: { type: 'string' },
|
|
246
|
+
host: { type: 'string' },
|
|
247
|
+
session_id: { type: 'string' },
|
|
235
248
|
entities: { type: 'array', items: { type: 'string' }, description: 'Named entities (person/project/tool/file)' },
|
|
236
249
|
entity_mode: { type: 'string', enum: ['any', 'all'], description: '"any" boosts; "all" hard-filters to sessions containing every entity' },
|
|
237
250
|
mode: { type: 'string', enum: ['fts', 'hybrid', 'vector'], description: 'Recall strategy, default hybrid' },
|
|
251
|
+
active_scope_key: { type: 'string' },
|
|
252
|
+
active_scope_path: { type: 'array', items: { type: 'string' } },
|
|
238
253
|
},
|
|
239
254
|
},
|
|
240
255
|
async execute(_toolCallId, params) {
|
|
241
256
|
try {
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
agentId: params?.agent_id || ctx?.agentId || undefined,
|
|
245
|
-
source: params?.source || undefined,
|
|
246
|
-
dateFrom: params?.date_from || undefined,
|
|
247
|
-
dateTo: params?.date_to || undefined,
|
|
248
|
-
limit,
|
|
249
|
-
};
|
|
250
|
-
if (Array.isArray(params?.entities) && params.entities.length > 0) {
|
|
251
|
-
recallOpts.entities = params.entities;
|
|
252
|
-
recallOpts.entityMode = params?.entity_mode || 'any';
|
|
253
|
-
}
|
|
254
|
-
if (params?.mode === 'fts' || params?.mode === 'hybrid' || params?.mode === 'vector') {
|
|
255
|
-
recallOpts.mode = params.mode;
|
|
256
|
-
}
|
|
257
|
-
const results = await aquifer.recall(String(params?.query || ''), recallOpts);
|
|
257
|
+
const request = buildCompatibilityRecallRequest(aquifer, params || {}, ctx || {});
|
|
258
|
+
const results = await runCompatibilityRecall(aquifer, String(params?.query || ''), request);
|
|
258
259
|
const lines = results.map((r, i) =>
|
|
259
260
|
`${i+1}. ${r.structuredSummary?.title || r.summaryText?.slice(0, 80) || '(untitled)'}`
|
|
260
261
|
);
|
|
261
|
-
return { content: [{ type: 'text', text: lines.join('\n') || 'No matching sessions.' }] };
|
|
262
|
+
return { content: [{ type: 'text', text: [request.laneHeader, '', lines.join('\n') || 'No matching sessions.'].join('\n') }] };
|
|
262
263
|
} catch (err) {
|
|
263
264
|
return { content: [{ type: 'text', text: `session_recall error: ${err.message}` }], isError: true };
|
|
264
265
|
}
|
|
@@ -269,11 +270,19 @@ function createPersona(personaOpts = {}) {
|
|
|
269
270
|
return { aquifer };
|
|
270
271
|
}
|
|
271
272
|
|
|
273
|
+
function registerProductStatusTools(api, opts = {}) {
|
|
274
|
+
const aquifer = opts.aquifer || resolveCommon(opts).aquifer;
|
|
275
|
+
registerOpenClawProductStatusTools(api, aquifer);
|
|
276
|
+
api.logger.info('[default-persona] registerProductStatusTools: memory_stats + memory_pending registered');
|
|
277
|
+
return { aquifer, productStatusToolsRegistered: true };
|
|
278
|
+
}
|
|
279
|
+
|
|
272
280
|
function mountOnOpenClaw(api, opts = {}) {
|
|
273
281
|
const r = registerAfterburn(api, opts);
|
|
282
|
+
registerProductStatusTools(api, { ...opts, aquifer: r.aquifer });
|
|
274
283
|
registerContextInject(api, opts);
|
|
275
284
|
registerRecallTool(api, opts);
|
|
276
|
-
return r;
|
|
285
|
+
return { ...r, productStatusToolsRegistered: true };
|
|
277
286
|
}
|
|
278
287
|
|
|
279
288
|
return {
|
|
@@ -282,6 +291,7 @@ function createPersona(personaOpts = {}) {
|
|
|
282
291
|
registerAfterburn,
|
|
283
292
|
registerContextInject,
|
|
284
293
|
registerRecallTool,
|
|
294
|
+
registerProductStatusTools,
|
|
285
295
|
buildPostProcess,
|
|
286
296
|
buildSummaryFn,
|
|
287
297
|
buildEntityParseFn,
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
// Parameterized via personaOpts:
|
|
6
6
|
// agentName — human name/role the prompt addresses (default 'Assistant')
|
|
7
7
|
// observedOwner — if set, the prompt asks for a short observation about
|
|
8
|
-
// that person
|
|
8
|
+
// that person.
|
|
9
9
|
// null → the section is omitted entirely.
|
|
10
10
|
// language — 'en' | 'zh-TW' (default 'en')
|
|
11
11
|
//
|
|
12
|
-
// Output format mirrors
|
|
12
|
+
// Output format mirrors the generic RECAP fields so downstream daily-entries
|
|
13
13
|
// parsing works uniformly across personas.
|
|
14
14
|
|
|
15
15
|
function buildSummaryPrompt({ conversationText, agentId, now, dailyContext, persona = {} }) {
|