recallnest 2.6.1 → 3.0.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/CHANGELOG.md +292 -0
- package/README.md +280 -195
- package/README_CN.md +266 -199
- package/config.json.example +8 -1
- package/docs/api-reference.md +1 -1
- package/integrations/README.md +1 -1
- package/integrations/agy/README.md +73 -0
- package/integrations/agy/setup.sh +75 -0
- package/integrations/setup-all.sh +1 -1
- package/package.json +7 -5
- package/src/access-tracker.ts +10 -1
- package/src/activity-counter.ts +69 -11
- package/src/age-label.ts +29 -0
- package/src/audit-log.ts +35 -0
- package/src/auto-gc.ts +97 -30
- package/src/capture-engine.ts +26 -4
- package/src/cli.ts +224 -10
- package/src/consolidation-engine.ts +317 -123
- package/src/context-composer-stable.ts +5 -1
- package/src/data-checkup.ts +17 -3
- package/src/dream-pipeline.ts +90 -12
- package/src/embedder.ts +44 -5
- package/src/env-config.ts +71 -0
- package/src/forget-engine.ts +8 -2
- package/src/gateway-server.ts +272 -0
- package/src/ingest.ts +248 -22
- package/src/kg-extractor.ts +17 -0
- package/src/llm-client.ts +86 -25
- package/src/mcp-server.ts +2 -1
- package/src/mcp-tools-advanced.ts +7 -2
- package/src/mcp-tools-core.ts +11 -1
- package/src/mcp-tools-governance.ts +45 -3
- package/src/memory-evolution.ts +16 -0
- package/src/memory-output.ts +47 -8
- package/src/memory-promotion.ts +280 -0
- package/src/memory-store-port.ts +32 -3
- package/src/memory-utility.ts +314 -0
- package/src/pivot-apply.ts +52 -1
- package/src/pivot-evidence.ts +128 -0
- package/src/ppr-traversal.ts +25 -1
- package/src/recall-ledger.ts +76 -0
- package/src/retriever.ts +82 -9
- package/src/runtime-config.ts +9 -0
- package/src/scope-policy.ts +22 -1
- package/src/session-engine.ts +11 -4
- package/src/session-output.ts +13 -4
- package/src/session-store.ts +2 -3
- package/src/skill-promotion.ts +218 -1
- package/src/store.ts +231 -71
- package/src/synthesis-contract.ts +286 -0
- package/src/version-manager.ts +83 -0
- package/src/workflow-observation-schema.ts +26 -0
- package/integrations/gemini-cli/README.md +0 -57
- package/integrations/gemini-cli/setup.sh +0 -64
- package/src/__tests__/access-tracker-novelty.test.ts +0 -304
- package/src/__tests__/activity-counter.test.ts +0 -125
- package/src/__tests__/adaptive-fidelity.test.ts +0 -102
- package/src/__tests__/admission-control.test.ts +0 -206
- package/src/__tests__/anchor-generator.test.ts +0 -67
- package/src/__tests__/api-server-security.test.ts +0 -102
- package/src/__tests__/auto-consolidation.test.ts +0 -168
- package/src/__tests__/auto-recall.test.ts +0 -131
- package/src/__tests__/babel-integration.test.ts +0 -144
- package/src/__tests__/batch-dedup.test.ts +0 -65
- package/src/__tests__/belief-history.test.ts +0 -220
- package/src/__tests__/canary-eval.test.ts +0 -114
- package/src/__tests__/capture-engine.test.ts +0 -1328
- package/src/__tests__/capture-heuristic.test.ts +0 -171
- package/src/__tests__/cascade-forget.test.ts +0 -210
- package/src/__tests__/category-min-scores.test.ts +0 -116
- package/src/__tests__/cc8-cc1-essential-context.test.ts +0 -298
- package/src/__tests__/circuit-breaker.test.ts +0 -99
- package/src/__tests__/cluster-consolidation.test.ts +0 -945
- package/src/__tests__/confidence-metadata.test.ts +0 -125
- package/src/__tests__/confidence-tracker.test.ts +0 -261
- package/src/__tests__/conflict-advisor.test.ts +0 -206
- package/src/__tests__/conflict-escalation.test.ts +0 -149
- package/src/__tests__/conflict-lifecycle.test.ts +0 -83
- package/src/__tests__/conflict-output.test.ts +0 -85
- package/src/__tests__/conflict-store.test.ts +0 -418
- package/src/__tests__/connector-v1.test.ts +0 -181
- package/src/__tests__/consolidation-engine.test.ts +0 -430
- package/src/__tests__/constructive-retrieval.test.ts +0 -509
- package/src/__tests__/context-collapse-renderer.test.ts +0 -214
- package/src/__tests__/context-composer-pins.test.ts +0 -64
- package/src/__tests__/context-composer-task-selection.test.ts +0 -24
- package/src/__tests__/context-reconstructor.test.ts +0 -65
- package/src/__tests__/context-renderer.test.ts +0 -91
- package/src/__tests__/continuity-eval.test.ts +0 -275
- package/src/__tests__/continuity-schema.test.ts +0 -97
- package/src/__tests__/contracts.test.ts +0 -262
- package/src/__tests__/conversation-importer.test.ts +0 -692
- package/src/__tests__/core-recall-eval.test.ts +0 -235
- package/src/__tests__/core-summary.test.ts +0 -109
- package/src/__tests__/data-checkup.test.ts +0 -276
- package/src/__tests__/decay-exemption.test.ts +0 -129
- package/src/__tests__/dedup-actions.test.ts +0 -58
- package/src/__tests__/dedup-guard.test.ts +0 -39
- package/src/__tests__/deterministic-id.test.ts +0 -39
- package/src/__tests__/distill-lock.test.ts +0 -305
- package/src/__tests__/distill-session-argshape.test.ts +0 -79
- package/src/__tests__/doctor.test.ts +0 -319
- package/src/__tests__/dream-pipeline.test.ts +0 -780
- package/src/__tests__/drill-down.test.ts +0 -108
- package/src/__tests__/embedder.test.ts +0 -122
- package/src/__tests__/emotion-decay.test.ts +0 -46
- package/src/__tests__/emotion-detector.test.ts +0 -56
- package/src/__tests__/emotion-phase1-integration.test.ts +0 -153
- package/src/__tests__/emotion-retrieval.test.ts +0 -56
- package/src/__tests__/entity-resolver.test.ts +0 -58
- package/src/__tests__/env-config.test.ts +0 -151
- package/src/__tests__/error-signature-boost.test.ts +0 -87
- package/src/__tests__/error-signature.test.ts +0 -95
- package/src/__tests__/error-taxonomy.test.ts +0 -129
- package/src/__tests__/ethics-layer.test.ts +0 -476
- package/src/__tests__/event-segmenter.test.ts +0 -153
- package/src/__tests__/feature-flags-integration.test.ts +0 -54
- package/src/__tests__/frequency-tracker.test.ts +0 -113
- package/src/__tests__/freshness.test.ts +0 -186
- package/src/__tests__/graph-export.test.ts +0 -489
- package/src/__tests__/hotness-blend.test.ts +0 -283
- package/src/__tests__/ingest-chunking.test.ts +0 -44
- package/src/__tests__/ingest.test.ts +0 -360
- package/src/__tests__/interference-detector.test.ts +0 -203
- package/src/__tests__/kg-extractor.test.ts +0 -192
- package/src/__tests__/kg-store.test.ts +0 -300
- package/src/__tests__/llm-client-pivot.test.ts +0 -628
- package/src/__tests__/llm-consolidation.test.ts +0 -226
- package/src/__tests__/maintenance-locks.test.ts +0 -189
- package/src/__tests__/mcp-efficiency.test.ts +0 -288
- package/src/__tests__/mcp-related-scopes.test.ts +0 -151
- package/src/__tests__/memory-boundaries.test.ts +0 -282
- package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +0 -345
- package/src/__tests__/memory-evolution.test.ts +0 -271
- package/src/__tests__/memory-health-rebalance.test.ts +0 -144
- package/src/__tests__/memory-lint.test.ts +0 -421
- package/src/__tests__/memory-output.test.ts +0 -215
- package/src/__tests__/memory-promotion.test.ts +0 -315
- package/src/__tests__/migrate-dedup-ids.test.ts +0 -65
- package/src/__tests__/multi-vector.test.ts +0 -242
- package/src/__tests__/narrative-architecture.test.ts +0 -456
- package/src/__tests__/noise-filter.test.ts +0 -205
- package/src/__tests__/noise-prototype-bank.test.ts +0 -280
- package/src/__tests__/obsidian-connector.test.ts +0 -363
- package/src/__tests__/omitted-reporting.test.ts +0 -98
- package/src/__tests__/pending-review.test.ts +0 -77
- package/src/__tests__/pii-redact.test.ts +0 -333
- package/src/__tests__/pivot-apply.test.ts +0 -373
- package/src/__tests__/pivot-distill-supervisor.test.ts +0 -457
- package/src/__tests__/pivot-distill.test.ts +0 -1178
- package/src/__tests__/post-retrieval-filter.test.ts +0 -178
- package/src/__tests__/ppr-traversal.test.ts +0 -256
- package/src/__tests__/prediction-engine.test.ts +0 -349
- package/src/__tests__/predictive-prospective-memory.test.ts +0 -416
- package/src/__tests__/preference-guard-smoke.test.ts +0 -211
- package/src/__tests__/preference-matcher.test.ts +0 -282
- package/src/__tests__/preference-slots.test.ts +0 -205
- package/src/__tests__/prospective-memory.test.ts +0 -283
- package/src/__tests__/pull-from-macbook-sync-only.test.ts +0 -679
- package/src/__tests__/query-entity-detector.test.ts +0 -171
- package/src/__tests__/query-expander-alias.test.ts +0 -146
- package/src/__tests__/query-expander.test.ts +0 -34
- package/src/__tests__/recall-gate.test.ts +0 -266
- package/src/__tests__/recall-governor.test.ts +0 -227
- package/src/__tests__/recall-mode.test.ts +0 -64
- package/src/__tests__/resolve-db-path.test.ts +0 -130
- package/src/__tests__/resolve-tier.test.ts +0 -64
- package/src/__tests__/result-synthesizer.test.ts +0 -198
- package/src/__tests__/resume-context.test.ts +0 -4898
- package/src/__tests__/retrieval-eval.test.ts +0 -163
- package/src/__tests__/retrieval-trace.test.ts +0 -213
- package/src/__tests__/retriever-boundary-weight.test.ts +0 -136
- package/src/__tests__/retriever-lazy-init-dispatch.test.ts +0 -123
- package/src/__tests__/retroactive-boost.test.ts +0 -223
- package/src/__tests__/salience-scoring.test.ts +0 -106
- package/src/__tests__/scope-inventory.test.ts +0 -200
- package/src/__tests__/scope-policy.test.ts +0 -69
- package/src/__tests__/scope-suggester.test.ts +0 -80
- package/src/__tests__/session-checkpoint.test.ts +0 -291
- package/src/__tests__/session-distiller.test.ts +0 -640
- package/src/__tests__/session-gc.test.ts +0 -215
- package/src/__tests__/skill-engine.test.ts +0 -552
- package/src/__tests__/skill-promotion.test.ts +0 -654
- package/src/__tests__/skill-verifier.test.ts +0 -122
- package/src/__tests__/source-heartbeat.test.ts +0 -250
- package/src/__tests__/stable-context-length-contract.test.ts +0 -62
- package/src/__tests__/store-patch-metadata.test.ts +0 -205
- package/src/__tests__/store-read-consistency.test.ts +0 -122
- package/src/__tests__/store-schema-migration.test.ts +0 -87
- package/src/__tests__/store-scope-audit.test.ts +0 -81
- package/src/__tests__/store-upsert.test.ts +0 -201
- package/src/__tests__/supersede-chain.test.ts +0 -154
- package/src/__tests__/temporal-parser.test.ts +0 -241
- package/src/__tests__/temporal-validity.test.ts +0 -160
- package/src/__tests__/term-registry-terminal-roster.test.ts +0 -127
- package/src/__tests__/term-registry.test.ts +0 -121
- package/src/__tests__/tool-error-surfacing.test.ts +0 -98
- package/src/__tests__/tool-output-compressor.test.ts +0 -160
- package/src/__tests__/topic-tag.test.ts +0 -113
- package/src/__tests__/trusted-memory.test.ts +0 -839
- package/src/__tests__/ui-server-security.test.ts +0 -93
- package/src/__tests__/usage-tracker.test.ts +0 -287
- package/src/__tests__/version-manager.test.ts +0 -213
- package/src/__tests__/workflow-observation-skill-outcome.test.ts +0 -352
- package/src/__tests__/workflow-observation.test.ts +0 -433
- package/src/__tests__/write-verifier.test.ts +0 -122
- /package/integrations/{gemini-cli/gemini-md-snippet.md → agy/agy-md-snippet.md} +0 -0
package/src/capture-engine.ts
CHANGED
|
@@ -461,6 +461,15 @@ export async function writeDurableEntry(
|
|
|
461
461
|
entry: MemoryEntry;
|
|
462
462
|
disposition: "stored" | "updated" | "deduped" | "promoted" | "conflict";
|
|
463
463
|
conflictId?: string;
|
|
464
|
+
/**
|
|
465
|
+
* True when this write REPLACED the content of an existing canonical row
|
|
466
|
+
* (a belief version was archived). Two different paths can do that — the
|
|
467
|
+
* latest-wins branch reports `updated`, while the deterministic-id branch
|
|
468
|
+
* below still reports `stored` — so disposition alone cannot tell them
|
|
469
|
+
* apart. Consumers that mirror memory content elsewhere (KG triples) must
|
|
470
|
+
* drop the old projection when this is set.
|
|
471
|
+
*/
|
|
472
|
+
supersededPrevious?: boolean;
|
|
464
473
|
}> {
|
|
465
474
|
const matches = await findCanonicalMatches(deps.store, params.canonicalKey);
|
|
466
475
|
const categoryMatches = matches.filter((entry) => entry.category === params.category);
|
|
@@ -605,6 +614,7 @@ export async function writeDurableEntry(
|
|
|
605
614
|
return {
|
|
606
615
|
entry: updated,
|
|
607
616
|
disposition: params.promotedFrom ? "promoted" : "updated",
|
|
617
|
+
supersededPrevious: true,
|
|
608
618
|
};
|
|
609
619
|
}
|
|
610
620
|
|
|
@@ -617,6 +627,7 @@ export async function writeDurableEntry(
|
|
|
617
627
|
? deterministicId(params.scope, params.canonicalKey)
|
|
618
628
|
: undefined;
|
|
619
629
|
let metadataForStore = params.metadata;
|
|
630
|
+
let supersededPrevious = false;
|
|
620
631
|
|
|
621
632
|
if (targetId && deps.store.getById) {
|
|
622
633
|
const rowAtTargetId = await deps.store.getById(targetId);
|
|
@@ -629,6 +640,7 @@ export async function writeDurableEntry(
|
|
|
629
640
|
{ now },
|
|
630
641
|
);
|
|
631
642
|
metadataForStore = buildSupersedingBeliefMetadata(params.metadata, previousEvo, historyId, now);
|
|
643
|
+
supersededPrevious = true;
|
|
632
644
|
}
|
|
633
645
|
}
|
|
634
646
|
|
|
@@ -647,6 +659,7 @@ export async function writeDurableEntry(
|
|
|
647
659
|
return {
|
|
648
660
|
entry: stored,
|
|
649
661
|
disposition: params.promotedFrom ? "promoted" : "stored",
|
|
662
|
+
supersededPrevious,
|
|
650
663
|
};
|
|
651
664
|
}
|
|
652
665
|
|
|
@@ -1106,7 +1119,7 @@ export async function persistMemory(
|
|
|
1106
1119
|
metadata = JSON.stringify(parsed);
|
|
1107
1120
|
}
|
|
1108
1121
|
|
|
1109
|
-
const { entry, disposition, conflictId } = await writeDurableEntry(deps, {
|
|
1122
|
+
const { entry, disposition, conflictId, supersededPrevious } = await writeDurableEntry(deps, {
|
|
1110
1123
|
text: input.text,
|
|
1111
1124
|
vector,
|
|
1112
1125
|
category: input.category,
|
|
@@ -1124,9 +1137,18 @@ export async function persistMemory(
|
|
|
1124
1137
|
const entryPrivacyTier = parsePrivacyTier(metadata);
|
|
1125
1138
|
const kgAllowed = entryPrivacyTier !== "ephemeral" && entryPrivacyTier !== "private";
|
|
1126
1139
|
if (deps.kgExtractor && disposition !== "deduped" && kgAllowed) {
|
|
1127
|
-
deps.kgExtractor
|
|
1128
|
-
|
|
1129
|
-
|
|
1140
|
+
const kgExtractor = deps.kgExtractor;
|
|
1141
|
+
// When this write replaced existing content, the edges extracted from the OLD
|
|
1142
|
+
// text are still in the graph: triple ids are content-derived, so re-extracting
|
|
1143
|
+
// merely adds new rows beside the stale ones and both compete during traversal.
|
|
1144
|
+
// Purge first, then re-extract — strictly ordered, or the purge would race ahead
|
|
1145
|
+
// and delete what we just wrote.
|
|
1146
|
+
const purged = supersededPrevious
|
|
1147
|
+
? kgExtractor.purgeForMemory(entry.id)
|
|
1148
|
+
: Promise.resolve();
|
|
1149
|
+
purged
|
|
1150
|
+
.then(() => kgExtractor.extractAndStore(input.text, entry.id, resolvedScope))
|
|
1151
|
+
.catch(() => {}); // Silently ignore — KG upkeep must never block memory writes
|
|
1130
1152
|
}
|
|
1131
1153
|
|
|
1132
1154
|
// F-1: Audit log — record store operation (non-blocking, silent on failure)
|
package/src/cli.ts
CHANGED
|
@@ -38,9 +38,18 @@ import {
|
|
|
38
38
|
} from "./ingest.js";
|
|
39
39
|
import { runDoctor, formatDoctorResults } from "./doctor.js";
|
|
40
40
|
import { persistCaseMemory, persistMemory, persistWorkflowPattern } from "./capture-engine.js";
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
scanMemoryPromotions,
|
|
43
|
+
scanSynthesisPromotions,
|
|
44
|
+
buildPromoteScanDeps,
|
|
45
|
+
formatPromoteScanResult,
|
|
46
|
+
formatSynthesisPromoteResult,
|
|
47
|
+
} from "./memory-promotion.js";
|
|
42
48
|
import { runDream, formatDreamResult, formatDreamMetrics, DEFAULT_DREAM_CONFIG, classifyDreamFailure, shouldBlockDreamRun, partitionAutoDreamScopes } from "./dream-pipeline.js";
|
|
43
|
-
import {
|
|
49
|
+
import { dreamBudgetMs } from "./env-config.js";
|
|
50
|
+
import { listScopesAboveThreshold, pruneWriteCounts } from "./activity-counter.js";
|
|
51
|
+
import { isTranscriptScope } from "./memory-boundaries.js";
|
|
52
|
+
import { maybeRunGc } from "./auto-gc.js";
|
|
44
53
|
import {
|
|
45
54
|
CaseMemoryInputSchema,
|
|
46
55
|
type CaseMemoryInput,
|
|
@@ -62,6 +71,7 @@ import { WorkflowObservationOutcomeSchema } from "./workflow-observation-schema.
|
|
|
62
71
|
import { buildWorkflowEvidence, buildWorkflowObservationRecord, inspectWorkflowDashboard, inspectWorkflowHealth } from "./workflow-observation-engine.js";
|
|
63
72
|
import { formatWorkflowEvidencePack, formatWorkflowHealthDashboard, formatWorkflowHealthReport, formatWorkflowObservationSaved } from "./workflow-observation-output.js";
|
|
64
73
|
import { WorkflowObservationStore } from "./workflow-observation-store.js";
|
|
74
|
+
import { applyUtilityToMeta, computeMemoryUtility, formatMemoryUtility } from "./memory-utility.js";
|
|
65
75
|
import {
|
|
66
76
|
applyWorkflowObservationScopeSuggestions,
|
|
67
77
|
formatWorkflowObservationScopeReview,
|
|
@@ -702,6 +712,28 @@ program
|
|
|
702
712
|
console.log(formatPromoteScanResult(result));
|
|
703
713
|
});
|
|
704
714
|
|
|
715
|
+
program
|
|
716
|
+
.command("promote-synthesis")
|
|
717
|
+
.description("扫描 dream 合成出的结论(evidence 层),把证据充分的那些晋升出一份 durable 副本")
|
|
718
|
+
.requiredOption("--scope <scope>", "要扫描的 scope,如 memory")
|
|
719
|
+
.option("--apply", "执行晋升;默认 dry-run 只预览")
|
|
720
|
+
.option("--min-evidence <n>", "最少几条仍存活的不同证据(默认 2)", "2")
|
|
721
|
+
.option("--min-importance <n>", "合成条目自身的最低 importance(默认 0.6)", "0.6")
|
|
722
|
+
.action(async (options) => {
|
|
723
|
+
const config = loadConfig();
|
|
724
|
+
const { store, embedder } = createComponents(config);
|
|
725
|
+
const conflictStore = new ConflictCandidateStore();
|
|
726
|
+
const deps = buildPromoteScanDeps({ store, embedder, conflictStore });
|
|
727
|
+
const minDistinctEvidence = Number(options.minEvidence);
|
|
728
|
+
const minImportance = Number(options.minImportance);
|
|
729
|
+
const result = await scanSynthesisPromotions(deps, options.scope, {
|
|
730
|
+
dryRun: !options.apply,
|
|
731
|
+
...(Number.isFinite(minDistinctEvidence) ? { minDistinctEvidence } : {}),
|
|
732
|
+
...(Number.isFinite(minImportance) ? { minImportance } : {}),
|
|
733
|
+
});
|
|
734
|
+
console.log(formatSynthesisPromoteResult(result));
|
|
735
|
+
});
|
|
736
|
+
|
|
705
737
|
program
|
|
706
738
|
.command("export <query>")
|
|
707
739
|
.description("导出检索与蒸馏结果到 markdown/json")
|
|
@@ -1360,7 +1392,7 @@ program
|
|
|
1360
1392
|
program
|
|
1361
1393
|
.command("ingest")
|
|
1362
1394
|
.description("导入对话记录到索引")
|
|
1363
|
-
.option("-s, --source <source>", "数据源: cc / codex / gemini / kimi / memory / desktop / all", "all")
|
|
1395
|
+
.option("-s, --source <source>", "数据源: cc / codex / gemini / kimi / memory / desktop / minis / all", "all")
|
|
1364
1396
|
.option("-l, --limit <n>", "限制处理文件数(调试用)")
|
|
1365
1397
|
.option("-v, --verbose", "详细输出")
|
|
1366
1398
|
.option("--no-dedup", "跳过向量去重")
|
|
@@ -1503,6 +1535,27 @@ program
|
|
|
1503
1535
|
}
|
|
1504
1536
|
}
|
|
1505
1537
|
|
|
1538
|
+
// Minis (iPhone) —— 手机 agent 自己把对话写进这个目录,ingest 顺带扫走。
|
|
1539
|
+
// 为什么是独立源而不是复用 desktop:scope 前缀决定了 memory-boundaries 怎么降权,
|
|
1540
|
+
// 混在 cc: 里的话来源在库里就分辨不出来(那正是 2026-08-12 起挂着的缺口)。
|
|
1541
|
+
if (source === "all" || source === "minis") {
|
|
1542
|
+
const minisSource = config.sources.minis;
|
|
1543
|
+
if (minisSource) {
|
|
1544
|
+
const minisPath = resolveSourcePath(minisSource.path, "minis");
|
|
1545
|
+
if (existsSync(minisPath)) {
|
|
1546
|
+
console.log("📱 导入 Minis 对话...");
|
|
1547
|
+
const r = await ingestCCTranscripts(store, embedder, minisPath, {
|
|
1548
|
+
...ingestOpts,
|
|
1549
|
+
scopePrefix: "minis",
|
|
1550
|
+
});
|
|
1551
|
+
results.push(r);
|
|
1552
|
+
console.log(` ✅ Minis: ${formatIngestSummary(r)}`);
|
|
1553
|
+
} else if (source === "minis") {
|
|
1554
|
+
console.log(`⚠️ Minis 目录不存在: ${minisPath}`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1506
1559
|
// Memory markdown files
|
|
1507
1560
|
if (source === "all" || source === "memory") {
|
|
1508
1561
|
console.log("📚 导入记忆文件...");
|
|
@@ -1587,10 +1640,11 @@ program
|
|
|
1587
1640
|
program
|
|
1588
1641
|
.command("dream")
|
|
1589
1642
|
.description("运行 dream 离线整固管线 (Orient/Gather/Consolidate/Prune)")
|
|
1590
|
-
.option("--scope <scope>", "目标 scope(
|
|
1643
|
+
.option("--scope <scope>", "目标 scope(默认前缀家族匹配;加 --exact-scope 变精确)", "cc")
|
|
1644
|
+
.option("--exact-scope", "把 --scope 当成一个具体 scope 名精确匹配,不做前缀家族展开(调度入口必带)")
|
|
1591
1645
|
.option("--auto", "扫描 activity-counter 中写计数达标的所有 scope,逐个跑 dream(单 scope 失败不阻断其他)")
|
|
1592
1646
|
.option("--force", "跳过 min-writes 门槛强制运行")
|
|
1593
|
-
.action(async (options: { scope: string; auto?: boolean; force?: boolean }) => {
|
|
1647
|
+
.action(async (options: { scope: string; auto?: boolean; force?: boolean; exactScope?: boolean }) => {
|
|
1594
1648
|
const config = loadConfig();
|
|
1595
1649
|
const { store, embedder, llm } = createComponents(config);
|
|
1596
1650
|
// Same activity-stats file the store writes per-scope counts to (beside the LanceDB dir).
|
|
@@ -1602,20 +1656,76 @@ program
|
|
|
1602
1656
|
const eligible = listScopesAboveThreshold(DEFAULT_DREAM_CONFIG.minWritesForDream, { statsPath: activityStatsPath });
|
|
1603
1657
|
// 巨型 scope 交给专用 schedule,不占日常轮次的 wall-clock 预算(理由见
|
|
1604
1658
|
// DreamConfig.autoExcludeScopes:07-30 那轮 6h 只跑了 15/507,而 memory 独占 79% 的条数)。
|
|
1605
|
-
const { run:
|
|
1659
|
+
const { run: nonMemoryScopes, deferred } = partitionAutoDreamScopes(eligible, DEFAULT_DREAM_CONFIG.autoExcludeScopes);
|
|
1606
1660
|
if (deferred.length > 0) {
|
|
1607
1661
|
// 不静默:被挪走的 scope 必须在日志里看得见,否则"今天怎么没跑 memory"只能靠翻代码回答。
|
|
1608
1662
|
// 前缀刻意用 `[dream]` 而不是 `dream --auto:`——后者是 dream-checkup.sh 抓「达标 scope 数」
|
|
1609
1663
|
// 的锚点,多一行同前缀的会混进它的取数。
|
|
1610
1664
|
console.log(`[dream] ${deferred.length} 个 scope 交给专用 schedule,本轮不跑 → ${deferred.join(", ")}`);
|
|
1611
1665
|
}
|
|
1666
|
+
|
|
1667
|
+
// 2026-08-16 策略排除(互审 C2/K5):与上面的 deferred **语义不同,别合并**——
|
|
1668
|
+
// deferred 是"别处会跑",这里是"没有任何 schedule 会跑"。日志措辞必须分开,
|
|
1669
|
+
// 否则将来查"pivot 怎么没巩固"会被误导去找那个并不存在的专用 job。
|
|
1670
|
+
// 同时清写计数:runDream 对它返回 skipped 不会 resetWriteCount,不清则计数无限
|
|
1671
|
+
// 累积、每天都进达标队列白跑一次(这正是 07-02 那个"处理完不出队"bug 的形状)。
|
|
1672
|
+
const neverDream = DEFAULT_DREAM_CONFIG.neverDreamScopes;
|
|
1673
|
+
const policyExcluded = nonMemoryScopes.filter(s => neverDream.includes(s));
|
|
1674
|
+
const afterPolicy = nonMemoryScopes.filter(s => !neverDream.includes(s));
|
|
1675
|
+
if (policyExcluded.length > 0) {
|
|
1676
|
+
const prunedPolicy = await pruneWriteCounts(s => neverDream.includes(s), { statsPath: activityStatsPath });
|
|
1677
|
+
console.log(
|
|
1678
|
+
`[dream] ${policyExcluded.length} 个 scope 按策略不参与巩固(neverDreamScopes,无专用 schedule)` +
|
|
1679
|
+
` → ${policyExcluded.join(", ")};已清写计数 ${prunedPolicy.length} 个`,
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// 2026-08-14 transcript scope 整体出队(dream 写爆库根治的准入侧):
|
|
1684
|
+
// transcript 是原文层(shared-behaviors §5.3——原文归 Deja/ingest 管,提炼归
|
|
1685
|
+
// pivot 池),历史会话被 ingest 批量灌入就把写计数打过门槛,2026-08 实测达标
|
|
1686
|
+
// 队列 95% 是 codex/cc 会话 scope、dream 派生产物 96.6% 落在不参与日常检索的
|
|
1687
|
+
// transcript scope 上——巩固算力几乎全部空转,还把 _versions 写到 7.7GB。
|
|
1688
|
+
// 出队同时清扫计数(含未达标条目——历史会话只增不减,不清则 stats 文件无限膨胀;
|
|
1689
|
+
// ingest 白天会再写回、次日 04:00 再清,稳态保持小)。判定用 memory-boundaries
|
|
1690
|
+
// 的 isTranscriptScope(接新端时那边本来就要求同步前缀清单)。
|
|
1691
|
+
const scopes = afterPolicy.filter(s => !isTranscriptScope(s));
|
|
1692
|
+
const transcriptEligible = afterPolicy.length - scopes.length;
|
|
1693
|
+
const prunedTranscripts = await pruneWriteCounts(isTranscriptScope, { statsPath: activityStatsPath });
|
|
1694
|
+
if (prunedTranscripts.length > 0 || transcriptEligible > 0) {
|
|
1695
|
+
console.log(
|
|
1696
|
+
`[dream] transcript 原文层不参与巩固(§5.3):清扫 ${prunedTranscripts.length} 个 scope 的写计数` +
|
|
1697
|
+
`(其中达标 ${transcriptEligible} 个)→ 样例: ${prunedTranscripts.slice(0, 5).join(", ")}`,
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// 2026-08-14 全库 GC 兜底(互审 C5-P0):dream 是 auto-gc 的唯一生产触发点,而
|
|
1702
|
+
// maybeRunGc 藏在 runDreamInner 里 —— transcript 出队后 durable 队列可为空,若在
|
|
1703
|
+
// 进循环前就 exit,全库 GC 检查会静默消失、归档管线停转。这里无条件补调一次:
|
|
1704
|
+
// GC 自带 24h stamp + 跨进程锁,循环内已触发过则本次 too_soon 跳过,幂等零成本。
|
|
1705
|
+
// 失败只记日志不并入 blocked:GC 是 best-effort 维护,明天还有机会。
|
|
1706
|
+
const runGcBackstop = async (): Promise<void> => {
|
|
1707
|
+
try {
|
|
1708
|
+
const gc = await maybeRunGc(store, DEFAULT_DREAM_CONFIG.gc);
|
|
1709
|
+
console.log(
|
|
1710
|
+
`[dream] 全库 GC 兜底: ${gc.triggered
|
|
1711
|
+
? `archived ${gc.archivedCount}, dissolved version groups ${gc.dissolvedVersionGroups}`
|
|
1712
|
+
: gc.dissolvedVersionGroups > 0
|
|
1713
|
+
? `dissolved version groups ${gc.dissolvedVersionGroups}; archive skipped — ${gc.reason}`
|
|
1714
|
+
: `skipped — ${gc.reason}`}`,
|
|
1715
|
+
);
|
|
1716
|
+
} catch (err) {
|
|
1717
|
+
console.error(`[dream] 全库 GC 兜底失败(不阻断本轮判定): ${err instanceof Error ? err.message : String(err)}`);
|
|
1718
|
+
}
|
|
1719
|
+
};
|
|
1720
|
+
|
|
1612
1721
|
if (scopes.length === 0) {
|
|
1613
|
-
//
|
|
1722
|
+
// 三种空分开说:真没人达标 / 达标的全被挪走或全是 transcript——混成一句会让排障跑偏。
|
|
1614
1723
|
console.log(
|
|
1615
1724
|
eligible.length === 0
|
|
1616
1725
|
? "dream --auto: 无达标 scope(写计数均低于门槛)"
|
|
1617
|
-
: "dream --auto: 达标 scope
|
|
1726
|
+
: "dream --auto: 达标 scope 均为 transcript(已清扫)、已交专用 schedule 或按策略排除,本轮无事可做",
|
|
1618
1727
|
);
|
|
1728
|
+
await runGcBackstop();
|
|
1619
1729
|
console.log("[[DREAM_STATUS]] skip");
|
|
1620
1730
|
process.exit(0);
|
|
1621
1731
|
}
|
|
@@ -1625,7 +1735,7 @@ program
|
|
|
1625
1735
|
// 把 07-25~27 三天的调度全堵死(上一次没结束,launchd 起不了新的)。
|
|
1626
1736
|
// 天花板见 DreamConfig.autoRunBudgetMs 的注释——截断会让靠后 scope 系统性饿死,
|
|
1627
1737
|
// 真解是 session scope TTL,不是把预算调大。
|
|
1628
|
-
const budgetMs =
|
|
1738
|
+
const budgetMs = dreamBudgetMs(DEFAULT_DREAM_CONFIG.autoRunBudgetMs);
|
|
1629
1739
|
const deadline = Date.now() + budgetMs;
|
|
1630
1740
|
|
|
1631
1741
|
let fatalFailures = 0;
|
|
@@ -1647,7 +1757,11 @@ program
|
|
|
1647
1757
|
}
|
|
1648
1758
|
processed++;
|
|
1649
1759
|
try {
|
|
1650
|
-
|
|
1760
|
+
// scopeMatch: "exact" —— 这些 scope 名直接来自 activity-stats 的键,是具体
|
|
1761
|
+
// scope 不是 family selector。无冒号的键(实测 11 个,含 `cc` / `global` /
|
|
1762
|
+
// `memory`)走前缀会把整个家族卷进来:`cc` 一旦达标就以 `LIKE 'cc%'` 把 08-14
|
|
1763
|
+
// 刚出队的 transcript 原文层整批捞回(2026-08-16 互审 K4)。
|
|
1764
|
+
const result = await runDream({ store, llm, embedder, scope, force: Boolean(options.force), activityStatsPath, kgStore, scopeMatch: "exact" });
|
|
1651
1765
|
tally[result.output.kind]++;
|
|
1652
1766
|
effectsTotal += result.output.effectsWritten;
|
|
1653
1767
|
if (result.output.degraded) degradedScopes.push(`${scope}=${result.output.degraded}`);
|
|
@@ -1665,6 +1779,10 @@ program
|
|
|
1665
1779
|
}
|
|
1666
1780
|
}
|
|
1667
1781
|
|
|
1782
|
+
// 全库 GC 兜底:循环内的 per-scope GC 若已触发过,这里 too_soon 幂等跳过;
|
|
1783
|
+
// 若全部 scope 都 skip/失败没走到 prune 阶段,这里补上当天唯一一次 GC 检查。
|
|
1784
|
+
await runGcBackstop();
|
|
1785
|
+
|
|
1668
1786
|
// 旧语义是「任一 scope 抛异常即 blocked」,在 475 个 scope 的规模下等于「有一个撞上
|
|
1669
1787
|
// store-write 锁就整轮红」,而整轮红会让 shell 脚本重试 → 重跑全量。改为:代码/数据
|
|
1670
1788
|
// 缺陷一票否决,环境性失败要成规模才算整轮失败。比例按已处理数算,不按总数。
|
|
@@ -1702,6 +1820,9 @@ program
|
|
|
1702
1820
|
force: Boolean(options.force),
|
|
1703
1821
|
activityStatsPath,
|
|
1704
1822
|
kgStore,
|
|
1823
|
+
// 手动调用默认保留 family(`--scope cc` 扫 `cc:*` 是既有用法);
|
|
1824
|
+
// 调度入口(weekly job)必须显式带 --exact-scope。
|
|
1825
|
+
scopeMatch: options.exactScope ? "exact" : "family",
|
|
1705
1826
|
});
|
|
1706
1827
|
|
|
1707
1828
|
console.log(formatDreamResult(result));
|
|
@@ -1936,6 +2057,92 @@ program
|
|
|
1936
2057
|
|
|
1937
2058
|
// ─── doctor ──────────────────────────────────────────────────────────────────
|
|
1938
2059
|
|
|
2060
|
+
program
|
|
2061
|
+
.command("memory-utility")
|
|
2062
|
+
.description("价值回溯:查一条 memory 参与过的任务成功率与 utility(不传 id 时按 scope 批量)")
|
|
2063
|
+
.argument("[memoryId]", "memory id(完整 UUID 或前缀)")
|
|
2064
|
+
.option("--scope <scope>", "批量模式:扫这个 scope 下的条目")
|
|
2065
|
+
.option("--category <category>", "批量模式:只看这个 category")
|
|
2066
|
+
.option("--limit <n>", "批量模式扫描上限", "500")
|
|
2067
|
+
.option("--min-samples <n>", "批量模式只列出有效样本量 ≥ 此值的条目", "0")
|
|
2068
|
+
.option("--links <n>", "展示最近 N 条参与记录", "5")
|
|
2069
|
+
.option("--apply", "把算出的 utility 写回 metadata(默认只读,不写)")
|
|
2070
|
+
.option("--observations-dir <dir>", "从指定目录读 workflow observations(默认生产目录)")
|
|
2071
|
+
.option("--json", "输出 JSON")
|
|
2072
|
+
.action(async (memoryId, options) => {
|
|
2073
|
+
const { store } = createComponents(loadConfig());
|
|
2074
|
+
const observationStore = options.observationsDir
|
|
2075
|
+
? new WorkflowObservationStore(expandHome(options.observationsDir))
|
|
2076
|
+
: new WorkflowObservationStore();
|
|
2077
|
+
// 全量拉 observation:join 要看的是历史全集,不是最近 200 条。
|
|
2078
|
+
const observations = await observationStore.listRecent({ limit: 100_000 });
|
|
2079
|
+
const showLinks = Number(options.links) || 0;
|
|
2080
|
+
|
|
2081
|
+
if (memoryId) {
|
|
2082
|
+
const entry = await store.getById(memoryId);
|
|
2083
|
+
if (!entry) {
|
|
2084
|
+
console.error(`未找到 memory: ${memoryId}`);
|
|
2085
|
+
process.exitCode = 1;
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
const summary = computeMemoryUtility(entry, observations);
|
|
2089
|
+
if (options.apply) {
|
|
2090
|
+
await store.patchMetadata(entry.id, (meta) => applyUtilityToMeta(meta, summary));
|
|
2091
|
+
}
|
|
2092
|
+
if (options.json) {
|
|
2093
|
+
console.log(JSON.stringify({ ...summary, applied: Boolean(options.apply) }, null, 2));
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
console.log(formatMemoryUtility(summary, { showLinks }));
|
|
2097
|
+
console.log(` observations 池: ${observations.length} 条`);
|
|
2098
|
+
if (options.apply) console.log(" 已写回 metadata.utility");
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
if (!options.scope) {
|
|
2103
|
+
console.error("需要 <memoryId> 或 --scope");
|
|
2104
|
+
process.exitCode = 1;
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
const limit = Number(options.limit) || 500;
|
|
2109
|
+
const minSamples = Number(options.minSamples) || 0;
|
|
2110
|
+
const entries = await store.listPage({
|
|
2111
|
+
scopeFilter: [options.scope],
|
|
2112
|
+
category: options.category,
|
|
2113
|
+
limit,
|
|
2114
|
+
offset: 0,
|
|
2115
|
+
});
|
|
2116
|
+
|
|
2117
|
+
const summaries = entries
|
|
2118
|
+
.map((entry) => computeMemoryUtility(entry, observations))
|
|
2119
|
+
.filter((s) => s.effectiveSamples >= minSamples && s.total > 0)
|
|
2120
|
+
.sort((a, b) => (b.utility ?? 0) - (a.utility ?? 0));
|
|
2121
|
+
|
|
2122
|
+
if (options.apply) {
|
|
2123
|
+
for (const summary of summaries) {
|
|
2124
|
+
await store.patchMetadata(summary.memoryId, (meta) => applyUtilityToMeta(meta, summary));
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
if (options.json) {
|
|
2129
|
+
console.log(JSON.stringify({ scanned: entries.length, matched: summaries.length, summaries }, null, 2));
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
console.log(`scope=${options.scope} 扫描 ${entries.length} 条,其中 ${summaries.length} 条有参与记录`);
|
|
2134
|
+
console.log(`observations 池: ${observations.length} 条`);
|
|
2135
|
+
for (const summary of summaries.slice(0, 20)) {
|
|
2136
|
+
const utility = summary.utility === null ? " null" : summary.utility.toFixed(4).padStart(7);
|
|
2137
|
+
console.log(
|
|
2138
|
+
`${utility} samples=${summary.effectiveSamples.toFixed(2).padStart(6)}`
|
|
2139
|
+
+ ` exact=${String(summary.exactCount).padStart(3)} session=${String(summary.sessionCount).padStart(3)}`
|
|
2140
|
+
+ ` ${summary.memoryId.slice(0, 8)}`,
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
if (options.apply) console.log(`已写回 ${summaries.length} 条的 metadata.utility`);
|
|
2144
|
+
});
|
|
2145
|
+
|
|
1939
2146
|
program
|
|
1940
2147
|
.command("workflow-observe")
|
|
1941
2148
|
.description("记录一条 workflow primitive observation,作为自进化观测层的 append-only 输入")
|
|
@@ -1949,9 +2156,14 @@ program
|
|
|
1949
2156
|
.option("--tags <tags>", "逗号分隔 tags")
|
|
1950
2157
|
.option("--tools <tools>", "逗号分隔 tools")
|
|
1951
2158
|
.option("--idempotency-key <key>", "同一请求重试时用于替换旧 observation 的稳定键")
|
|
2159
|
+
.option("--reader-id <id>", "P0 join key:读者身份(默认本进程)。CLI 每次是新进程,跨会话关联要显式传")
|
|
2160
|
+
.option("--recalled-ids <ids>", "P0 join key:逗号分隔的 memory id,本次任务实际用到的记忆")
|
|
1952
2161
|
.option("--json", "输出 JSON")
|
|
1953
2162
|
.action(async (workflowId, summary, options) => {
|
|
1954
2163
|
const store = new WorkflowObservationStore();
|
|
2164
|
+
// CLI 是一次性进程,检索账本必然是空的(这次进程没检索过),所以不做自动补,
|
|
2165
|
+
// 只认显式传入——自动补在这里只会写出一个恒空的字段,看着像记了其实没记。
|
|
2166
|
+
const recalledIds = splitCsvOption(options.recalledIds);
|
|
1955
2167
|
const record = buildWorkflowObservationRecord({
|
|
1956
2168
|
workflowId,
|
|
1957
2169
|
summary,
|
|
@@ -1963,6 +2175,8 @@ program
|
|
|
1963
2175
|
tags: splitCsvOption(options.tags),
|
|
1964
2176
|
tools: splitCsvOption(options.tools),
|
|
1965
2177
|
idempotencyKey: options.idempotencyKey,
|
|
2178
|
+
readerId: options.readerId,
|
|
2179
|
+
...(recalledIds.length > 0 ? { recalledIds } : {}),
|
|
1966
2180
|
});
|
|
1967
2181
|
const stored = await store.save(record);
|
|
1968
2182
|
if (options.json) {
|