@remnic/core 9.3.629 → 9.3.631
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/dist/access-cli.js +15 -13
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +5 -4
- package/dist/access-http.js +7 -6
- package/dist/access-mcp.d.ts +5 -4
- package/dist/access-mcp.js +6 -5
- package/dist/{access-service-BdThkfIE.d.ts → access-service-C9_EpVHd.d.ts} +2 -2
- package/dist/access-service.d.ts +5 -4
- package/dist/access-service.js +5 -4
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/auto-sync-RFADEHIQ.js +75 -0
- package/dist/auto-sync-RFADEHIQ.js.map +1 -0
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -3
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -2
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +4 -3
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-532VCWYW.js → chunk-242XFZ36.js} +2 -2
- package/dist/{chunk-XXO5TI3B.js → chunk-32U3N7H5.js} +3 -3
- package/dist/{chunk-KB4MFBF5.js → chunk-3RDYU3JS.js} +3 -3
- package/dist/{chunk-57QXN2CS.js → chunk-4S3N6HFG.js} +2 -2
- package/dist/{chunk-OLNNOHBC.js → chunk-5PT5I6JQ.js} +20 -14
- package/dist/{chunk-OLNNOHBC.js.map → chunk-5PT5I6JQ.js.map} +1 -1
- package/dist/{chunk-GE7Q7KXP.js → chunk-7A2QKUUA.js} +2 -2
- package/dist/{chunk-KKTXCFD7.js → chunk-7H5WCPBS.js} +95 -11
- package/dist/{chunk-KKTXCFD7.js.map → chunk-7H5WCPBS.js.map} +1 -1
- package/dist/{chunk-3MNBW7R7.js → chunk-C4KKM62E.js} +2 -2
- package/dist/{chunk-NKCW223V.js → chunk-CMN5AWAZ.js} +2 -2
- package/dist/{chunk-JXHMAQYT.js → chunk-DOBJH4I6.js} +4 -4
- package/dist/{chunk-TZDSNIRO.js → chunk-IFVFQRZ2.js} +5 -5
- package/dist/{chunk-LQYTQCXM.js → chunk-JCLECECB.js} +2 -2
- package/dist/chunk-KVDUDYEN.js +1164 -0
- package/dist/chunk-KVDUDYEN.js.map +1 -0
- package/dist/{chunk-QDV6VAD4.js → chunk-LEG7XWS2.js} +2 -2
- package/dist/chunk-M7XQSUBB.js +280 -0
- package/dist/chunk-M7XQSUBB.js.map +1 -0
- package/dist/{chunk-N5RGXWLQ.js → chunk-PUEAEQSN.js} +2 -2
- package/dist/{chunk-UGHUNQ74.js → chunk-QYGIQ5NM.js} +212 -417
- package/dist/chunk-QYGIQ5NM.js.map +1 -0
- package/dist/{chunk-JKCDQBDW.js → chunk-UXFOGILU.js} +2 -2
- package/dist/{chunk-MVQN73GT.js → chunk-VTR3MNYF.js} +2 -2
- package/dist/{chunk-KVFYTRMV.js → chunk-W25I7G6U.js} +2 -2
- package/dist/{chunk-3GLCUPXP.js → chunk-WLZBVYC6.js} +192 -889
- package/dist/chunk-WLZBVYC6.js.map +1 -0
- package/dist/{chunk-3R2UZV3U.js → chunk-X7EJF46S.js} +2 -2
- package/dist/{chunk-54KDA6UK.js → chunk-XG4NAWAV.js} +3 -3
- package/dist/{chunk-P2D2MM47.js → chunk-YROCXMCK.js} +2 -2
- package/dist/{cli-DAsHklrf.d.ts → cli-CuVEQWKr.d.ts} +3 -3
- package/dist/cli.d.ts +6 -5
- package/dist/cli.js +18 -17
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -2
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -2
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +3 -2
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/contradiction/index.js +4 -4
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +3 -2
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -3
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +49 -45
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +4 -3
- package/dist/mcp-memory-inspector-app.d.ts +5 -4
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +4 -3
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +3 -2
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +7 -6
- package/dist/{orchestrator-BexeSJ2j.d.ts → orchestrator-CoqytbK_.d.ts} +102 -10
- package/dist/orchestrator.d.ts +4 -3
- package/dist/orchestrator.js +12 -10
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-PwkzNfdK.d.ts → semantic-consolidation-BPs6BURk.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -3
- package/dist/semantic-rule-promotion.js +3 -2
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +38 -2
- package/dist/storage.js +6 -3
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/{types-BCF2wqKa.d.ts → types-CpMPD8xl.d.ts} +59 -11
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -2
- package/package.json +1 -1
- package/src/orchestrator.ts +74 -0
- package/src/storage.ts +100 -0
- package/src/wearables/auto-sync.test.ts +181 -0
- package/src/wearables/auto-sync.ts +129 -0
- package/src/wearables/cli.ts +6 -0
- package/src/wearables/config.test.ts +90 -11
- package/src/wearables/config.ts +113 -11
- package/src/wearables/memory-gen.test.ts +416 -1
- package/src/wearables/memory-gen.ts +381 -23
- package/src/wearables/pipeline.test.ts +396 -5
- package/src/wearables/pipeline.ts +174 -22
- package/src/wearables/service.test.ts +172 -0
- package/src/wearables/service.ts +84 -3
- package/src/wearables/storage-io.test.ts +81 -0
- package/src/wearables/trust.test.ts +123 -0
- package/src/wearables/trust.ts +168 -0
- package/src/wearables/types.ts +57 -9
- package/dist/chunk-3GLCUPXP.js.map +0 -1
- package/dist/chunk-UGHUNQ74.js.map +0 -1
- /package/dist/{chunk-532VCWYW.js.map → chunk-242XFZ36.js.map} +0 -0
- /package/dist/{chunk-XXO5TI3B.js.map → chunk-32U3N7H5.js.map} +0 -0
- /package/dist/{chunk-KB4MFBF5.js.map → chunk-3RDYU3JS.js.map} +0 -0
- /package/dist/{chunk-57QXN2CS.js.map → chunk-4S3N6HFG.js.map} +0 -0
- /package/dist/{chunk-GE7Q7KXP.js.map → chunk-7A2QKUUA.js.map} +0 -0
- /package/dist/{chunk-3MNBW7R7.js.map → chunk-C4KKM62E.js.map} +0 -0
- /package/dist/{chunk-NKCW223V.js.map → chunk-CMN5AWAZ.js.map} +0 -0
- /package/dist/{chunk-JXHMAQYT.js.map → chunk-DOBJH4I6.js.map} +0 -0
- /package/dist/{chunk-TZDSNIRO.js.map → chunk-IFVFQRZ2.js.map} +0 -0
- /package/dist/{chunk-LQYTQCXM.js.map → chunk-JCLECECB.js.map} +0 -0
- /package/dist/{chunk-QDV6VAD4.js.map → chunk-LEG7XWS2.js.map} +0 -0
- /package/dist/{chunk-N5RGXWLQ.js.map → chunk-PUEAEQSN.js.map} +0 -0
- /package/dist/{chunk-JKCDQBDW.js.map → chunk-UXFOGILU.js.map} +0 -0
- /package/dist/{chunk-MVQN73GT.js.map → chunk-VTR3MNYF.js.map} +0 -0
- /package/dist/{chunk-KVFYTRMV.js.map → chunk-W25I7G6U.js.map} +0 -0
- /package/dist/{chunk-3R2UZV3U.js.map → chunk-X7EJF46S.js.map} +0 -0
- /package/dist/{chunk-54KDA6UK.js.map → chunk-XG4NAWAV.js.map} +0 -0
- /package/dist/{chunk-P2D2MM47.js.map → chunk-YROCXMCK.js.map} +0 -0
|
@@ -34,6 +34,10 @@ function config(overrides: Partial<WearablesConfig> = {}): WearablesConfig {
|
|
|
34
34
|
...defaultWearablesConfig(),
|
|
35
35
|
enabled: true,
|
|
36
36
|
timezone: "UTC",
|
|
37
|
+
// Pinned off so per-stage tests stay focused; defaults are covered
|
|
38
|
+
// in config.test.ts and the dedicated digest test below.
|
|
39
|
+
digestEnabled: false,
|
|
40
|
+
offTheRecordEnabled: false,
|
|
37
41
|
...overrides,
|
|
38
42
|
};
|
|
39
43
|
}
|
|
@@ -119,7 +123,7 @@ function makeDeps(memoryDir: string): {
|
|
|
119
123
|
files.set(`${sourceId}/${date}`, serialized);
|
|
120
124
|
written.push({ source: sourceId, date, serialized });
|
|
121
125
|
},
|
|
122
|
-
async
|
|
126
|
+
async afterWrites() {
|
|
123
127
|
reindexes.count += 1;
|
|
124
128
|
},
|
|
125
129
|
memoryGen: {
|
|
@@ -540,14 +544,15 @@ test("zero provider conversations never clobber an existing transcript", async (
|
|
|
540
544
|
}
|
|
541
545
|
});
|
|
542
546
|
|
|
543
|
-
test("
|
|
547
|
+
test("looping pagination cursors stop with a partial marker and keep warning", async () => {
|
|
544
548
|
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
545
549
|
try {
|
|
546
550
|
const conversation = makeConversation("c1", "2026-06-11", [
|
|
547
551
|
{ speaker: "user", isWearer: true, text: "First chunk of a very long recorded day of conversations." },
|
|
548
552
|
{ speaker: "Speaker 2", text: "Indeed, the recordings just keep going on and on today." },
|
|
549
553
|
]);
|
|
550
|
-
// A connector
|
|
554
|
+
// A connector whose cursor loops forever (pathological) — cycle
|
|
555
|
+
// detection must stop it; no page-count cap truncates real data.
|
|
551
556
|
const endlessConnector = {
|
|
552
557
|
id: "testsource",
|
|
553
558
|
displayName: "Test Source",
|
|
@@ -563,14 +568,400 @@ test("page-capped days carry a visible partial marker and keep warning", async (
|
|
|
563
568
|
};
|
|
564
569
|
const { deps, written } = makeDeps(memoryDir);
|
|
565
570
|
const first = await syncWearableSource(endlessConnector, settings(), config(), { days: 1 }, deps);
|
|
566
|
-
assert.ok(first.warnings.some((warning) => warning.includes("
|
|
571
|
+
assert.ok(first.warnings.some((warning) => warning.includes("repeated cursor")));
|
|
567
572
|
assert.equal(written.length, 1);
|
|
568
573
|
assert.match(written[0].serialized, /pagination safety cap reached/);
|
|
569
574
|
|
|
570
575
|
// Identical second sync: file unchanged (no rewrite), warning persists.
|
|
571
576
|
const second = await syncWearableSource(endlessConnector, settings(), config(), { days: 1 }, deps);
|
|
572
577
|
assert.equal(second.transcriptsWritten.length, 0);
|
|
573
|
-
assert.ok(second.warnings.some((warning) => warning.includes("
|
|
578
|
+
assert.ok(second.warnings.some((warning) => warning.includes("repeated cursor")));
|
|
579
|
+
} finally {
|
|
580
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
test("looping providers that re-serve rows never duplicate conversations", async () => {
|
|
585
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
586
|
+
try {
|
|
587
|
+
const conversation = makeConversation("c1", "2026-06-11", [
|
|
588
|
+
{ speaker: "user", isWearer: true, text: "A conversation the provider keeps re-serving on every page." },
|
|
589
|
+
{ speaker: "Speaker 2", text: "The looping pagination should still store this exactly once." },
|
|
590
|
+
]);
|
|
591
|
+
// Pathological: every page returns the SAME row and loops the cursor.
|
|
592
|
+
const reServingConnector = {
|
|
593
|
+
id: "testsource",
|
|
594
|
+
displayName: "Test Source",
|
|
595
|
+
async verifyAuth() {
|
|
596
|
+
return { ok: true };
|
|
597
|
+
},
|
|
598
|
+
async fetchConversations() {
|
|
599
|
+
return { conversations: [conversation], nextCursor: "loop" };
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
const { deps, written } = makeDeps(memoryDir);
|
|
603
|
+
const summary = await syncWearableSource(
|
|
604
|
+
reServingConnector,
|
|
605
|
+
settings(),
|
|
606
|
+
config(),
|
|
607
|
+
{ days: 1 },
|
|
608
|
+
deps,
|
|
609
|
+
);
|
|
610
|
+
assert.equal(summary.conversations, 1, "re-served rows collapse by conversation id");
|
|
611
|
+
assert.ok(summary.warnings.some((warning) => warning.includes("repeated cursor")));
|
|
612
|
+
assert.equal(written.length, 1);
|
|
613
|
+
const occurrences = written[0].serialized.split("conversation c1").length - 1;
|
|
614
|
+
assert.equal(occurrences, 1, "day file stores the conversation exactly once");
|
|
615
|
+
} finally {
|
|
616
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
test("long provider days paginate fully — no page-count truncation", async () => {
|
|
621
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
622
|
+
try {
|
|
623
|
+
// 60 pages (beyond the old 50-page cap), each with one conversation.
|
|
624
|
+
const PAGES = 60;
|
|
625
|
+
const longDayConnector = {
|
|
626
|
+
id: "testsource",
|
|
627
|
+
displayName: "Test Source",
|
|
628
|
+
async verifyAuth() {
|
|
629
|
+
return { ok: true };
|
|
630
|
+
},
|
|
631
|
+
async fetchConversations(opts: { cursor?: string | null }) {
|
|
632
|
+
const page = opts.cursor ? Number(opts.cursor) : 0;
|
|
633
|
+
return {
|
|
634
|
+
conversations: [
|
|
635
|
+
makeConversation(`c${page + 1}`, "2026-06-11", [
|
|
636
|
+
{ speaker: "user", isWearer: true, text: `Recorded conversation segment number ${page + 1} from the long day.` },
|
|
637
|
+
{ speaker: "Speaker 2", text: "Acknowledged, that part of the day was captured as well." },
|
|
638
|
+
]),
|
|
639
|
+
],
|
|
640
|
+
nextCursor: page + 1 < PAGES ? String(page + 1) : null,
|
|
641
|
+
};
|
|
642
|
+
},
|
|
643
|
+
};
|
|
644
|
+
const { deps } = makeDeps(memoryDir);
|
|
645
|
+
const summary = await syncWearableSource(
|
|
646
|
+
longDayConnector,
|
|
647
|
+
settings(),
|
|
648
|
+
config(),
|
|
649
|
+
{ days: 1 },
|
|
650
|
+
deps,
|
|
651
|
+
);
|
|
652
|
+
assert.equal(summary.conversations, PAGES, "every page's conversation synced");
|
|
653
|
+
assert.equal(
|
|
654
|
+
summary.warnings.filter(
|
|
655
|
+
(warning) =>
|
|
656
|
+
warning.includes("stopped paginating") || warning.includes("repeated cursor"),
|
|
657
|
+
).length,
|
|
658
|
+
0,
|
|
659
|
+
"a real long day is never truncated or warned",
|
|
660
|
+
);
|
|
661
|
+
} finally {
|
|
662
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
test("a judge outage does not re-run the memory pass forever", async () => {
|
|
667
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
668
|
+
try {
|
|
669
|
+
const byDate = {
|
|
670
|
+
"2026-06-11": [
|
|
671
|
+
makeConversation("c1", "2026-06-11", [
|
|
672
|
+
{ speaker: "user", isWearer: true, text: "The vendor contract was renewed for another year today." },
|
|
673
|
+
{ speaker: "Speaker 2", text: "Renewal confirmed, the paperwork went through this afternoon." },
|
|
674
|
+
]),
|
|
675
|
+
],
|
|
676
|
+
};
|
|
677
|
+
const { deps, memoryWrites } = makeDeps(memoryDir);
|
|
678
|
+
assert.ok(deps.memoryGen);
|
|
679
|
+
let extractCalls = 0;
|
|
680
|
+
const baseExtract = deps.memoryGen.extract;
|
|
681
|
+
deps.memoryGen.extract = async (turns) => {
|
|
682
|
+
extractCalls += 1;
|
|
683
|
+
return baseExtract(turns);
|
|
684
|
+
};
|
|
685
|
+
deps.memoryGen.judgeFacts = async () => {
|
|
686
|
+
throw new Error("judge backend down");
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const first = await syncWearableSource(
|
|
690
|
+
fakeConnector(byDate),
|
|
691
|
+
settings({ memoryMode: "smart" }),
|
|
692
|
+
config(),
|
|
693
|
+
{ days: 1 },
|
|
694
|
+
deps,
|
|
695
|
+
);
|
|
696
|
+
assert.ok(first.warnings.some((warning) => warning.includes("judge unavailable")));
|
|
697
|
+
assert.equal(first.memoriesCreated, 1, "degraded pass still writes");
|
|
698
|
+
const callsAfterFirst = extractCalls;
|
|
699
|
+
|
|
700
|
+
// Unchanged day, judge still down: the degraded-but-complete pass
|
|
701
|
+
// must have recorded completion — no re-extraction.
|
|
702
|
+
const second = await syncWearableSource(
|
|
703
|
+
fakeConnector(byDate),
|
|
704
|
+
settings({ memoryMode: "smart" }),
|
|
705
|
+
config(),
|
|
706
|
+
{ days: 1 },
|
|
707
|
+
deps,
|
|
708
|
+
);
|
|
709
|
+
assert.equal(extractCalls, callsAfterFirst, "no repeat extraction for an unchanged day");
|
|
710
|
+
assert.equal(second.memoriesCreated, 0);
|
|
711
|
+
assert.equal(memoryWrites.length, 1);
|
|
712
|
+
} finally {
|
|
713
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
test("smart mode: another device's stored day transcript corroborates borderline facts", async () => {
|
|
718
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
719
|
+
try {
|
|
720
|
+
const byDate = {
|
|
721
|
+
"2026-06-11": [
|
|
722
|
+
makeConversation("c1", "2026-06-11", [
|
|
723
|
+
{ speaker: "user", isWearer: true, text: "We are moving the launch to September twelfth after the vendor call." },
|
|
724
|
+
{ speaker: "Speaker 2", text: "September twelfth works for everyone on the vendor side too." },
|
|
725
|
+
]),
|
|
726
|
+
],
|
|
727
|
+
};
|
|
728
|
+
const { deps, memoryWrites } = makeDeps(memoryDir);
|
|
729
|
+
assert.ok(deps.memoryGen);
|
|
730
|
+
// Borderline extraction: 0.75 * 0.8 = 0.6 — review band unless corroborated.
|
|
731
|
+
deps.memoryGen.extract = async () => ({
|
|
732
|
+
facts: [
|
|
733
|
+
{
|
|
734
|
+
category: "fact",
|
|
735
|
+
content: "The launch moved to September twelfth after the vendor call.",
|
|
736
|
+
confidence: 0.75,
|
|
737
|
+
tags: [],
|
|
738
|
+
},
|
|
739
|
+
],
|
|
740
|
+
profileUpdates: [],
|
|
741
|
+
entities: [],
|
|
742
|
+
questions: [],
|
|
743
|
+
});
|
|
744
|
+
// A second device already stored a transcript covering the same day.
|
|
745
|
+
deps.readOtherSourceDayBodies = async (date, excludeSource) => {
|
|
746
|
+
assert.equal(date, "2026-06-11");
|
|
747
|
+
assert.equal(excludeSource, "testsource");
|
|
748
|
+
return new Map([
|
|
749
|
+
["bee", "They said the launch moves to September twelfth right after that vendor call wrapped."],
|
|
750
|
+
]);
|
|
751
|
+
};
|
|
752
|
+
deps.listSupportMemories = async () => [];
|
|
753
|
+
|
|
754
|
+
const summary = await syncWearableSource(
|
|
755
|
+
fakeConnector(byDate),
|
|
756
|
+
settings({ memoryMode: "smart" }),
|
|
757
|
+
config(),
|
|
758
|
+
{ days: 1 },
|
|
759
|
+
deps,
|
|
760
|
+
);
|
|
761
|
+
assert.equal(summary.memoriesCreated, 1);
|
|
762
|
+
assert.equal(memoryWrites[0].options.status, "active", "corroboration lifts review -> active");
|
|
763
|
+
const attrs = memoryWrites[0].options.structuredAttributes as Record<string, string>;
|
|
764
|
+
assert.equal(attrs.corroboratedBySources, "bee");
|
|
765
|
+
assert.equal(attrs.trustDecision, "auto-approved");
|
|
766
|
+
} finally {
|
|
767
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
test("smart native import never uses day-scoped cross-source corroboration", async () => {
|
|
772
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
773
|
+
try {
|
|
774
|
+
const { deps, memoryWrites } = makeDeps(memoryDir);
|
|
775
|
+
assert.ok(deps.memoryGen);
|
|
776
|
+
let dayBodiesRequested = 0;
|
|
777
|
+
deps.readOtherSourceDayBodies = async () => {
|
|
778
|
+
dayBodiesRequested += 1;
|
|
779
|
+
return new Map([["bee", "the launch moves to september twelfth after that vendor call"]]);
|
|
780
|
+
};
|
|
781
|
+
deps.listSupportMemories = async () => [
|
|
782
|
+
{ id: "fact-9", content: "User volunteers at the food bank every month with the team." },
|
|
783
|
+
];
|
|
784
|
+
const summary = await syncWearableSource(
|
|
785
|
+
fakeConnector({}, [
|
|
786
|
+
// No day attached — must not be scored against any day's tokens,
|
|
787
|
+
// but corpus support still applies: 0.7*(0.8*0.9)+0.10 = 0.604 -> review.
|
|
788
|
+
{ id: "nat-1", content: "User volunteers at the food bank every month with the team." },
|
|
789
|
+
]),
|
|
790
|
+
settings({ memoryMode: "smart", importNativeMemories: "smart" }),
|
|
791
|
+
config(),
|
|
792
|
+
{ days: 1 },
|
|
793
|
+
deps,
|
|
794
|
+
);
|
|
795
|
+
assert.equal(dayBodiesRequested, 0, "native import must not read day bodies");
|
|
796
|
+
assert.equal(summary.nativeMemoriesImported, 1);
|
|
797
|
+
const attrs = memoryWrites[0].options.structuredAttributes as Record<string, string>;
|
|
798
|
+
assert.equal(attrs.corroboratedBySources, undefined, "no cross-source evidence without a day");
|
|
799
|
+
assert.equal(attrs.supportingMemoryId, "fact-9", "corpus support still applies");
|
|
800
|
+
} finally {
|
|
801
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
test("facts written earlier in a multi-day backfill support later days in the same run", async () => {
|
|
806
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
807
|
+
try {
|
|
808
|
+
const byDate = {
|
|
809
|
+
"2026-06-10": [
|
|
810
|
+
makeConversation("c1", "2026-06-10", [
|
|
811
|
+
{ speaker: "user", isWearer: true, text: "The launch moved to September twelfth after the vendor call." },
|
|
812
|
+
{ speaker: "Speaker 2", text: "September twelfth confirmed with the vendor on the call." },
|
|
813
|
+
]),
|
|
814
|
+
],
|
|
815
|
+
"2026-06-11": [
|
|
816
|
+
makeConversation("c2", "2026-06-11", [
|
|
817
|
+
{ speaker: "user", isWearer: true, text: "Reminder that the launch moved to September twelfth after the vendor call." },
|
|
818
|
+
{ speaker: "Speaker 2", text: "Yes, the September date is locked in now." },
|
|
819
|
+
]),
|
|
820
|
+
],
|
|
821
|
+
};
|
|
822
|
+
const { deps, memoryWrites } = makeDeps(memoryDir);
|
|
823
|
+
assert.ok(deps.memoryGen);
|
|
824
|
+
// Borderline confidence so day 2 only crosses the auto threshold
|
|
825
|
+
// with the +0.10 corpus-support boost from day 1's write.
|
|
826
|
+
let call = 0;
|
|
827
|
+
deps.memoryGen.extract = async () => {
|
|
828
|
+
call += 1;
|
|
829
|
+
return {
|
|
830
|
+
facts: [
|
|
831
|
+
{
|
|
832
|
+
category: "fact",
|
|
833
|
+
content:
|
|
834
|
+
call === 1
|
|
835
|
+
? "The launch moved to September twelfth after the vendor call."
|
|
836
|
+
: "Launch moved to September twelfth after the vendor call.",
|
|
837
|
+
confidence: 0.75,
|
|
838
|
+
tags: [],
|
|
839
|
+
},
|
|
840
|
+
],
|
|
841
|
+
profileUpdates: [],
|
|
842
|
+
entities: [],
|
|
843
|
+
questions: [],
|
|
844
|
+
};
|
|
845
|
+
};
|
|
846
|
+
// listSupportMemories reflects what this run has written so far —
|
|
847
|
+
// mirroring storage, whose readAllMemories cache invalidates on
|
|
848
|
+
// every write.
|
|
849
|
+
deps.listSupportMemories = async () =>
|
|
850
|
+
memoryWrites.map((write, index) => ({ id: `mem-${index + 1}`, content: write.content }));
|
|
851
|
+
|
|
852
|
+
const summary = await syncWearableSource(
|
|
853
|
+
fakeConnector(byDate),
|
|
854
|
+
settings({ memoryMode: "smart" }),
|
|
855
|
+
config(),
|
|
856
|
+
{ days: 2 },
|
|
857
|
+
deps,
|
|
858
|
+
);
|
|
859
|
+
assert.equal(summary.memoriesCreated, 2);
|
|
860
|
+
assert.equal(memoryWrites[0].options.status, "pending_review", "day 1 borderline stays in review");
|
|
861
|
+
assert.equal(memoryWrites[1].options.status, "active", "day 2 lifted by day 1's write");
|
|
862
|
+
const attrs = memoryWrites[1].options.structuredAttributes as Record<string, string>;
|
|
863
|
+
assert.equal(attrs.supportingMemoryId, "mem-1");
|
|
864
|
+
} finally {
|
|
865
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test("a second device's day write invalidates the first device's memory-pass completion", async () => {
|
|
870
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
871
|
+
try {
|
|
872
|
+
const day = "2026-06-11";
|
|
873
|
+
const conversationFor = (source: string) =>
|
|
874
|
+
makeConversation(`${source}-c1`, day, [
|
|
875
|
+
{ speaker: "user", isWearer: true, text: "We are moving the launch to September twelfth after the vendor call today." },
|
|
876
|
+
{ speaker: "Speaker 2", text: "The vendor confirmed the September launch date on the call." },
|
|
877
|
+
]);
|
|
878
|
+
const { deps } = makeDeps(memoryDir);
|
|
879
|
+
|
|
880
|
+
// Device A syncs first and completes its memory pass for the day.
|
|
881
|
+
const connectorA = { ...fakeConnector({ [day]: [conversationFor("a")] }), id: "sourcea" };
|
|
882
|
+
await syncWearableSource(connectorA, settings({ memoryMode: "smart" }), config(), { days: 1 }, deps);
|
|
883
|
+
let state = await loadSyncState(memoryDir);
|
|
884
|
+
assert.ok(state.sources.sourcea.memoryDayHashes?.[day], "A's pass recorded");
|
|
885
|
+
|
|
886
|
+
// Device B then writes a transcript for the same day: A's
|
|
887
|
+
// completion record for that day must be invalidated so A's next
|
|
888
|
+
// sync re-scores with B's evidence available.
|
|
889
|
+
const connectorB = { ...fakeConnector({ [day]: [conversationFor("b")] }), id: "sourceb" };
|
|
890
|
+
await syncWearableSource(connectorB, settings({ memoryMode: "smart" }), config(), { days: 1 }, deps);
|
|
891
|
+
state = await loadSyncState(memoryDir);
|
|
892
|
+
assert.equal(
|
|
893
|
+
state.sources.sourcea.memoryDayHashes?.[day],
|
|
894
|
+
undefined,
|
|
895
|
+
"A's completion cleared by B's new same-day evidence",
|
|
896
|
+
);
|
|
897
|
+
assert.ok(state.sources.sourceb.memoryDayHashes?.[day], "B's own pass recorded");
|
|
898
|
+
} finally {
|
|
899
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
test("a promotion-only re-pass still fires the reindex hook", async () => {
|
|
904
|
+
const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
|
|
905
|
+
try {
|
|
906
|
+
const day = "2026-06-11";
|
|
907
|
+
const byDate = {
|
|
908
|
+
[day]: [
|
|
909
|
+
makeConversation("c1", day, [
|
|
910
|
+
{ speaker: "user", isWearer: true, text: "We are moving the launch to September twelfth after the vendor call today." },
|
|
911
|
+
{ speaker: "Speaker 2", text: "The vendor confirmed the September launch date on the call." },
|
|
912
|
+
]),
|
|
913
|
+
],
|
|
914
|
+
};
|
|
915
|
+
const { deps, reindexes, memoryWrites } = makeDeps(memoryDir);
|
|
916
|
+
assert.ok(deps.memoryGen);
|
|
917
|
+
const borderline = "The launch moved to September twelfth after the vendor call.";
|
|
918
|
+
deps.memoryGen.extract = async () => ({
|
|
919
|
+
facts: [{ category: "fact", content: borderline, confidence: 0.75, tags: [] }],
|
|
920
|
+
profileUpdates: [],
|
|
921
|
+
entities: [],
|
|
922
|
+
questions: [],
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
// Sync 1: borderline fact lands in review. Reindex fires (a memory
|
|
926
|
+
// was created).
|
|
927
|
+
await syncWearableSource(fakeConnector(byDate), settings({ memoryMode: "smart" }), config(), { days: 1 }, deps);
|
|
928
|
+
assert.equal(memoryWrites[0].options.status, "pending_review");
|
|
929
|
+
const reindexesAfterFirst = reindexes.count;
|
|
930
|
+
|
|
931
|
+
// Another source's same-day transcript arrives, then sync 1's
|
|
932
|
+
// source re-passes: unchanged transcript, duplicate fact — but now
|
|
933
|
+
// corroborated, so it PROMOTES. The reindex hook must still fire.
|
|
934
|
+
const written: string[] = [];
|
|
935
|
+
deps.memoryGen.writer.findWearableMemoryByContent = async (content) =>
|
|
936
|
+
content.trim() === borderline ? { id: "mem-1", status: "pending_review" } : null;
|
|
937
|
+
deps.memoryGen.writer.promoteWearableMemory = async (id) => {
|
|
938
|
+
written.push(id);
|
|
939
|
+
return true;
|
|
940
|
+
};
|
|
941
|
+
deps.memoryGen.writer.hasFactContentHash = async (content) =>
|
|
942
|
+
content.trim() === borderline;
|
|
943
|
+
deps.readOtherSourceDayBodies = async () =>
|
|
944
|
+
new Map([["bee", "They said the launch moves to September twelfth right after that vendor call wrapped."]]);
|
|
945
|
+
// Clear the completion record the way a sibling-source write would.
|
|
946
|
+
const { loadSyncState: load, saveSyncState: save } = await import("./sync-state.js");
|
|
947
|
+
const state = await load(memoryDir);
|
|
948
|
+
delete state.sources.testsource.memoryDayHashes?.[day];
|
|
949
|
+
await save(memoryDir, state);
|
|
950
|
+
|
|
951
|
+
const second = await syncWearableSource(
|
|
952
|
+
fakeConnector(byDate),
|
|
953
|
+
settings({ memoryMode: "smart" }),
|
|
954
|
+
config(),
|
|
955
|
+
{ days: 1 },
|
|
956
|
+
deps,
|
|
957
|
+
);
|
|
958
|
+
assert.equal(second.transcriptsWritten.length, 0, "no transcript write");
|
|
959
|
+
assert.equal(second.memoriesPromoted, 1, "promotion happened");
|
|
960
|
+
assert.deepEqual(written, ["mem-1"]);
|
|
961
|
+
assert.ok(
|
|
962
|
+
reindexes.count > reindexesAfterFirst,
|
|
963
|
+
"reindex fired for the promotion-only run",
|
|
964
|
+
);
|
|
574
965
|
} finally {
|
|
575
966
|
rmSync(memoryDir, { recursive: true, force: true });
|
|
576
967
|
}
|