@remnic/core 9.3.630 → 9.3.632

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/dist/access-cli.js +15 -13
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +4 -4
  4. package/dist/access-http.js +7 -6
  5. package/dist/access-mcp.d.ts +4 -4
  6. package/dist/access-mcp.js +6 -5
  7. package/dist/{access-service-C4v-eFjB.d.ts → access-service-C9_EpVHd.d.ts} +2 -2
  8. package/dist/access-service.d.ts +4 -4
  9. package/dist/access-service.js +5 -4
  10. package/dist/action-confidence.d.ts +1 -1
  11. package/dist/active-memory-bridge.d.ts +1 -1
  12. package/dist/active-recall.d.ts +1 -1
  13. package/dist/active-recall.js +1 -1
  14. package/dist/auto-sync-RFADEHIQ.js +75 -0
  15. package/dist/auto-sync-RFADEHIQ.js.map +1 -0
  16. package/dist/behavior-learner.d.ts +1 -1
  17. package/dist/behavior-signals.d.ts +1 -1
  18. package/dist/bootstrap.d.ts +3 -3
  19. package/dist/briefing.d.ts +1 -1
  20. package/dist/briefing.js +3 -2
  21. package/dist/buffer-surprise-report.d.ts +1 -1
  22. package/dist/buffer.d.ts +1 -1
  23. package/dist/calibration.d.ts +1 -1
  24. package/dist/causal-behavior.d.ts +1 -1
  25. package/dist/causal-consolidation.d.ts +1 -1
  26. package/dist/causal-consolidation.js +4 -3
  27. package/dist/causal-consolidation.js.map +1 -1
  28. package/dist/{chunk-BWK5EEKS.js → chunk-242XFZ36.js} +2 -2
  29. package/dist/{chunk-K3BTOW7N.js → chunk-32U3N7H5.js} +3 -3
  30. package/dist/{chunk-5S6IREG3.js → chunk-3RDYU3JS.js} +3 -3
  31. package/dist/{chunk-SACS6KE6.js → chunk-4S3N6HFG.js} +2 -2
  32. package/dist/{chunk-VUTPRX7K.js → chunk-5PT5I6JQ.js} +14 -14
  33. package/dist/{chunk-2VJ7AJFX.js → chunk-7A2QKUUA.js} +2 -2
  34. package/dist/{chunk-EORL2IDM.js → chunk-7H5WCPBS.js} +58 -5
  35. package/dist/{chunk-EORL2IDM.js.map → chunk-7H5WCPBS.js.map} +1 -1
  36. package/dist/{chunk-NRQJBK36.js → chunk-C4KKM62E.js} +2 -2
  37. package/dist/{chunk-GXWFZYSR.js → chunk-CMN5AWAZ.js} +2 -2
  38. package/dist/{chunk-F6USGHMO.js → chunk-DOBJH4I6.js} +4 -4
  39. package/dist/{chunk-ADOD7PJC.js → chunk-IFVFQRZ2.js} +5 -5
  40. package/dist/{chunk-6LBQL5US.js → chunk-JCLECECB.js} +2 -2
  41. package/dist/chunk-KVDUDYEN.js +1164 -0
  42. package/dist/chunk-KVDUDYEN.js.map +1 -0
  43. package/dist/{chunk-S5W37FPX.js → chunk-LEG7XWS2.js} +2 -2
  44. package/dist/chunk-M7XQSUBB.js +280 -0
  45. package/dist/chunk-M7XQSUBB.js.map +1 -0
  46. package/dist/{chunk-MQ24KOOR.js → chunk-PUEAEQSN.js} +2 -2
  47. package/dist/{chunk-RSKUUEBA.js → chunk-QYGIQ5NM.js} +140 -417
  48. package/dist/chunk-QYGIQ5NM.js.map +1 -0
  49. package/dist/{chunk-UE57H4MA.js → chunk-UXFOGILU.js} +2 -2
  50. package/dist/{chunk-OQMR2SDZ.js → chunk-VTR3MNYF.js} +2 -2
  51. package/dist/{chunk-4QEUKASL.js → chunk-W25I7G6U.js} +2 -2
  52. package/dist/{chunk-YJOWWRRS.js → chunk-WLZBVYC6.js} +125 -1206
  53. package/dist/chunk-WLZBVYC6.js.map +1 -0
  54. package/dist/{chunk-OOFBE62K.js → chunk-X7EJF46S.js} +2 -2
  55. package/dist/{chunk-BL33LBTN.js → chunk-XG4NAWAV.js} +3 -3
  56. package/dist/{chunk-ZZSXUZF3.js → chunk-YROCXMCK.js} +2 -2
  57. package/dist/{cli-B_6EMiQc.d.ts → cli-CuVEQWKr.d.ts} +3 -3
  58. package/dist/cli.d.ts +5 -5
  59. package/dist/cli.js +18 -17
  60. package/dist/compounding/engine.d.ts +1 -1
  61. package/dist/compounding/engine.js +3 -2
  62. package/dist/compounding/preference-consolidator.d.ts +1 -1
  63. package/dist/compression-optimizer.d.ts +1 -1
  64. package/dist/config.d.ts +1 -1
  65. package/dist/config.js +1 -1
  66. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  67. package/dist/connectors/codex-materialize-runner.js +3 -2
  68. package/dist/connectors/codex-materialize.d.ts +1 -1
  69. package/dist/connectors/index.d.ts +1 -1
  70. package/dist/connectors/index.js +3 -2
  71. package/dist/consolidation-provenance-check.d.ts +1 -1
  72. package/dist/consolidation-undo.d.ts +1 -1
  73. package/dist/contradiction/index.d.ts +1 -1
  74. package/dist/contradiction/index.js +4 -4
  75. package/dist/conversation-index/backend.d.ts +1 -1
  76. package/dist/conversation-index/chunker.d.ts +1 -1
  77. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  78. package/dist/conversation-index/indexer.d.ts +1 -1
  79. package/dist/conversation-index/search.d.ts +1 -1
  80. package/dist/day-summary.d.ts +1 -1
  81. package/dist/delinearize.d.ts +1 -1
  82. package/dist/direct-answer-wiring.d.ts +1 -1
  83. package/dist/direct-answer.d.ts +1 -1
  84. package/dist/embedding-fallback.d.ts +1 -1
  85. package/dist/enrichment/index.d.ts +1 -1
  86. package/dist/entity-retrieval.d.ts +1 -1
  87. package/dist/entity-retrieval.js +3 -2
  88. package/dist/entity-schema.d.ts +1 -1
  89. package/dist/explicit-capture.d.ts +3 -3
  90. package/dist/extraction-judge-telemetry.d.ts +1 -1
  91. package/dist/extraction-judge-training.d.ts +1 -1
  92. package/dist/extraction-judge.d.ts +1 -1
  93. package/dist/extraction.d.ts +1 -1
  94. package/dist/fallback-llm.d.ts +1 -1
  95. package/dist/identity-continuity.d.ts +1 -1
  96. package/dist/importance.d.ts +1 -1
  97. package/dist/index.d.ts +8 -8
  98. package/dist/index.js +49 -45
  99. package/dist/index.js.map +1 -1
  100. package/dist/intent.d.ts +1 -1
  101. package/dist/lcm/engine.d.ts +1 -1
  102. package/dist/lcm/index.d.ts +1 -1
  103. package/dist/lcm/tools.d.ts +1 -1
  104. package/dist/lifecycle.d.ts +1 -1
  105. package/dist/live-connectors-runner.d.ts +1 -1
  106. package/dist/local-llm.d.ts +1 -1
  107. package/dist/maintenance/memory-governance.d.ts +1 -1
  108. package/dist/maintenance/memory-governance.js +3 -2
  109. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -2
  110. package/dist/maintenance/rebuild-memory-projection.js +4 -3
  111. package/dist/mcp-memory-inspector-app.d.ts +4 -4
  112. package/dist/memory-action-policy.d.ts +1 -1
  113. package/dist/memory-cache.d.ts +1 -1
  114. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  115. package/dist/memory-projection-store.d.ts +1 -1
  116. package/dist/memory-provenance.d.ts +1 -1
  117. package/dist/memory-worth-outcomes.d.ts +1 -1
  118. package/dist/models-json.d.ts +1 -1
  119. package/dist/namespaces/migrate.d.ts +1 -1
  120. package/dist/namespaces/migrate.js +4 -3
  121. package/dist/namespaces/principal.d.ts +1 -1
  122. package/dist/namespaces/search.d.ts +1 -1
  123. package/dist/namespaces/storage.d.ts +1 -1
  124. package/dist/namespaces/storage.js +3 -2
  125. package/dist/native-knowledge.d.ts +1 -1
  126. package/dist/operator-toolkit.d.ts +1 -1
  127. package/dist/operator-toolkit.js +7 -6
  128. package/dist/{orchestrator-Dlw3ae4B.d.ts → orchestrator-CoqytbK_.d.ts} +3 -2
  129. package/dist/orchestrator.d.ts +3 -3
  130. package/dist/orchestrator.js +12 -10
  131. package/dist/patterns-cli.d.ts +1 -1
  132. package/dist/policy-runtime.d.ts +1 -1
  133. package/dist/qmd-recall-cache.d.ts +1 -1
  134. package/dist/qmd.d.ts +1 -1
  135. package/dist/recall-disclosure-escalation.d.ts +1 -1
  136. package/dist/recall-explain-renderer.d.ts +1 -1
  137. package/dist/recall-planner-llm.d.ts +1 -1
  138. package/dist/recall-state.d.ts +1 -1
  139. package/dist/recall-tag-filter.d.ts +1 -1
  140. package/dist/recall-xray-cli.d.ts +1 -1
  141. package/dist/recall-xray-renderer.d.ts +1 -1
  142. package/dist/recall-xray.d.ts +1 -1
  143. package/dist/resolve-auth-token.d.ts +1 -1
  144. package/dist/resume-bundles.js +2 -2
  145. package/dist/retrieval-agents.d.ts +1 -1
  146. package/dist/retrieval-tiers.d.ts +1 -1
  147. package/dist/routing/engine.d.ts +1 -1
  148. package/dist/routing/store.d.ts +1 -1
  149. package/dist/schemas.d.ts +22 -22
  150. package/dist/search/embed-helper.d.ts +1 -1
  151. package/dist/search/factory.d.ts +1 -1
  152. package/dist/search/index.d.ts +1 -1
  153. package/dist/search/lancedb-backend.d.ts +1 -1
  154. package/dist/search/meilisearch-backend.d.ts +1 -1
  155. package/dist/search/noop-backend.d.ts +1 -1
  156. package/dist/search/orama-backend.d.ts +1 -1
  157. package/dist/search/port.d.ts +1 -1
  158. package/dist/search/remote-backend.d.ts +1 -1
  159. package/dist/{semantic-consolidation-C4sefXEI.d.ts → semantic-consolidation-BPs6BURk.d.ts} +1 -1
  160. package/dist/semantic-consolidation.d.ts +2 -2
  161. package/dist/semantic-consolidation.js +4 -3
  162. package/dist/semantic-rule-promotion.js +3 -2
  163. package/dist/semantic-rule-verifier.d.ts +1 -1
  164. package/dist/semantic-rule-verifier.js +3 -2
  165. package/dist/session-observer-bands.d.ts +1 -1
  166. package/dist/session-observer-state.d.ts +1 -1
  167. package/dist/shared-context/manager.d.ts +1 -1
  168. package/dist/signal.d.ts +1 -1
  169. package/dist/storage.d.ts +1 -1
  170. package/dist/storage.js +2 -1
  171. package/dist/summarizer.d.ts +1 -1
  172. package/dist/summary-snapshot.d.ts +1 -1
  173. package/dist/temporal-supersession.d.ts +1 -1
  174. package/dist/temporal-validity.d.ts +1 -1
  175. package/dist/threading.d.ts +1 -1
  176. package/dist/tier-migration.d.ts +1 -1
  177. package/dist/tier-routing.d.ts +1 -1
  178. package/dist/topics.d.ts +1 -1
  179. package/dist/transcript.d.ts +1 -1
  180. package/dist/transfer/types.d.ts +12 -12
  181. package/dist/{types-2vqxmO0j.d.ts → types-CpMPD8xl.d.ts} +20 -1
  182. package/dist/types.d.ts +1 -1
  183. package/dist/utility-runtime.d.ts +1 -1
  184. package/dist/verified-recall.js +3 -2
  185. package/package.json +1 -1
  186. package/src/orchestrator.ts +58 -0
  187. package/src/wearables/auto-sync.test.ts +181 -0
  188. package/src/wearables/auto-sync.ts +129 -0
  189. package/src/wearables/config.test.ts +58 -8
  190. package/src/wearables/config.ts +75 -5
  191. package/src/wearables/pipeline.test.ts +87 -4
  192. package/src/wearables/pipeline.ts +43 -13
  193. package/src/wearables/types.ts +20 -1
  194. package/dist/chunk-RSKUUEBA.js.map +0 -1
  195. package/dist/chunk-YJOWWRRS.js.map +0 -1
  196. /package/dist/{chunk-BWK5EEKS.js.map → chunk-242XFZ36.js.map} +0 -0
  197. /package/dist/{chunk-K3BTOW7N.js.map → chunk-32U3N7H5.js.map} +0 -0
  198. /package/dist/{chunk-5S6IREG3.js.map → chunk-3RDYU3JS.js.map} +0 -0
  199. /package/dist/{chunk-SACS6KE6.js.map → chunk-4S3N6HFG.js.map} +0 -0
  200. /package/dist/{chunk-VUTPRX7K.js.map → chunk-5PT5I6JQ.js.map} +0 -0
  201. /package/dist/{chunk-2VJ7AJFX.js.map → chunk-7A2QKUUA.js.map} +0 -0
  202. /package/dist/{chunk-NRQJBK36.js.map → chunk-C4KKM62E.js.map} +0 -0
  203. /package/dist/{chunk-GXWFZYSR.js.map → chunk-CMN5AWAZ.js.map} +0 -0
  204. /package/dist/{chunk-F6USGHMO.js.map → chunk-DOBJH4I6.js.map} +0 -0
  205. /package/dist/{chunk-ADOD7PJC.js.map → chunk-IFVFQRZ2.js.map} +0 -0
  206. /package/dist/{chunk-6LBQL5US.js.map → chunk-JCLECECB.js.map} +0 -0
  207. /package/dist/{chunk-S5W37FPX.js.map → chunk-LEG7XWS2.js.map} +0 -0
  208. /package/dist/{chunk-MQ24KOOR.js.map → chunk-PUEAEQSN.js.map} +0 -0
  209. /package/dist/{chunk-UE57H4MA.js.map → chunk-UXFOGILU.js.map} +0 -0
  210. /package/dist/{chunk-OQMR2SDZ.js.map → chunk-VTR3MNYF.js.map} +0 -0
  211. /package/dist/{chunk-4QEUKASL.js.map → chunk-W25I7G6U.js.map} +0 -0
  212. /package/dist/{chunk-OOFBE62K.js.map → chunk-X7EJF46S.js.map} +0 -0
  213. /package/dist/{chunk-BL33LBTN.js.map → chunk-XG4NAWAV.js.map} +0 -0
  214. /package/dist/{chunk-ZZSXUZF3.js.map → chunk-YROCXMCK.js.map} +0 -0
@@ -544,14 +544,15 @@ test("zero provider conversations never clobber an existing transcript", async (
544
544
  }
545
545
  });
546
546
 
547
- test("page-capped days carry a visible partial marker and keep warning", async () => {
547
+ test("looping pagination cursors stop with a partial marker and keep warning", async () => {
548
548
  const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-pipeline-"));
549
549
  try {
550
550
  const conversation = makeConversation("c1", "2026-06-11", [
551
551
  { speaker: "user", isWearer: true, text: "First chunk of a very long recorded day of conversations." },
552
552
  { speaker: "Speaker 2", text: "Indeed, the recordings just keep going on and on today." },
553
553
  ]);
554
- // A connector that always reports another page (pathological).
554
+ // A connector whose cursor loops forever (pathological) — cycle
555
+ // detection must stop it; no page-count cap truncates real data.
555
556
  const endlessConnector = {
556
557
  id: "testsource",
557
558
  displayName: "Test Source",
@@ -567,14 +568,96 @@ test("page-capped days carry a visible partial marker and keep warning", async (
567
568
  };
568
569
  const { deps, written } = makeDeps(memoryDir);
569
570
  const first = await syncWearableSource(endlessConnector, settings(), config(), { days: 1 }, deps);
570
- assert.ok(first.warnings.some((warning) => warning.includes("stopped paginating")));
571
+ assert.ok(first.warnings.some((warning) => warning.includes("repeated cursor")));
571
572
  assert.equal(written.length, 1);
572
573
  assert.match(written[0].serialized, /pagination safety cap reached/);
573
574
 
574
575
  // Identical second sync: file unchanged (no rewrite), warning persists.
575
576
  const second = await syncWearableSource(endlessConnector, settings(), config(), { days: 1 }, deps);
576
577
  assert.equal(second.transcriptsWritten.length, 0);
577
- assert.ok(second.warnings.some((warning) => warning.includes("stopped paginating")));
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
+ );
578
661
  } finally {
579
662
  rmSync(memoryDir, { recursive: true, force: true });
580
663
  }
@@ -52,10 +52,13 @@ import type {
52
52
  WearablesConfig,
53
53
  } from "./types.js";
54
54
 
55
- /** Safety cap on pages fetched per day window. */
56
- const MAX_PAGES_PER_DAY = 50;
57
- /** Safety cap on native-memory pages per sync. */
58
- const MAX_NATIVE_PAGES = 20;
55
+ /**
56
+ * Pathological-provider backstop on pagination loops. Day fetches are
57
+ * intentionally UNLIMITED for real data a full day must never be
58
+ * truncated — so runaway protection comes from cursor-cycle detection
59
+ * plus this far-out-of-band ceiling, not from a content-sized cap.
60
+ */
61
+ const PAGE_SAFETY_CEILING = 10_000;
59
62
  /** Default lookback window (today + yesterday) for unscoped syncs. */
60
63
  const DEFAULT_SYNC_DAYS = 2;
61
64
  const MAX_SYNC_DAYS = 90;
@@ -186,23 +189,42 @@ async function fetchAllConversationsForDate(
186
189
  signal: AbortSignal | undefined,
187
190
  warnings: string[],
188
191
  ): Promise<{ conversations: WearableConversation[]; partial: boolean }> {
189
- const conversations: WearableConversation[] = [];
192
+ // Keyed by conversation id: a looping or overlapping provider can
193
+ // re-serve rows it already returned, and appending blindly would
194
+ // store the same conversation twice in the day file (Cursor review
195
+ // on PR #1464). Map keeps first-seen order; a re-served id replaces
196
+ // its entry in place, so the provider's LATEST version of a
197
+ // conversation wins — exactly what the current-day refresh wants.
198
+ const byId = new Map<string, WearableConversation>();
190
199
  let cursor: string | null | undefined = undefined;
191
- for (let page = 0; page < MAX_PAGES_PER_DAY; page++) {
200
+ const seenCursors = new Set<string>();
201
+ const collect = () => [...byId.values()];
202
+ for (let page = 0; page < PAGE_SAFETY_CEILING; page++) {
192
203
  const result = await connector.fetchConversations({
193
204
  date,
194
205
  timezone,
195
206
  cursor,
196
207
  signal,
197
208
  });
198
- conversations.push(...result.conversations);
199
- if (!result.nextCursor) return { conversations, partial: false };
209
+ for (const conversation of result.conversations) {
210
+ byId.set(conversation.id, conversation);
211
+ }
212
+ if (!result.nextCursor) return { conversations: collect(), partial: false };
213
+ // A repeated cursor means the provider's pagination is looping —
214
+ // following it again would refetch the same page forever.
215
+ if (seenCursors.has(result.nextCursor)) {
216
+ warnings.push(
217
+ `${connector.id}: provider pagination repeated cursor on ${date} — stopped to avoid an infinite loop; day may be partially synced (every sync refetches and re-warns while the provider misbehaves)`,
218
+ );
219
+ return { conversations: collect(), partial: true };
220
+ }
221
+ seenCursors.add(result.nextCursor);
200
222
  cursor = result.nextCursor;
201
223
  }
202
224
  warnings.push(
203
- `${connector.id}: stopped paginating ${date} after ${MAX_PAGES_PER_DAY} pages — day may be partially synced (every sync refetches and re-warns until the provider day fits the cap)`,
225
+ `${connector.id}: stopped paginating ${date} after the ${PAGE_SAFETY_CEILING}-page safety ceiling — day may be partially synced (every sync refetches and re-warns while this persists)`,
204
226
  );
205
- return { conversations, partial: true };
227
+ return { conversations: collect(), partial: true };
206
228
  }
207
229
 
208
230
  /** Visible marker appended to day files whose fetch hit the page cap. */
@@ -521,7 +543,8 @@ export async function syncWearableSource(
521
543
  : {}),
522
544
  };
523
545
  let cursor: string | null | undefined = undefined;
524
- for (let page = 0; page < MAX_NATIVE_PAGES; page++) {
546
+ const seenNativeCursors = new Set<string>();
547
+ for (let page = 0; page < PAGE_SAFETY_CEILING; page++) {
525
548
  const result = await connector.fetchNativeMemories({
526
549
  cursor,
527
550
  signal: options.signal,
@@ -538,10 +561,17 @@ export async function syncWearableSource(
538
561
  importedNativeIds.push(...imported.importedIds);
539
562
  for (const id of imported.importedIds) alreadyImported.add(id);
540
563
  if (!result.nextCursor) break;
564
+ if (seenNativeCursors.has(result.nextCursor)) {
565
+ summary.warnings.push(
566
+ `${connector.id}: provider pagination repeated cursor during native-memory import — stopped to avoid an infinite loop; remaining items import on the next sync`,
567
+ );
568
+ break;
569
+ }
570
+ seenNativeCursors.add(result.nextCursor);
541
571
  cursor = result.nextCursor;
542
- if (page === MAX_NATIVE_PAGES - 1) {
572
+ if (page === PAGE_SAFETY_CEILING - 1) {
543
573
  summary.warnings.push(
544
- `${connector.id}: stopped native-memory import after ${MAX_NATIVE_PAGES} pages — remaining items import on the next sync`,
574
+ `${connector.id}: stopped native-memory import at the ${PAGE_SAFETY_CEILING}-page safety ceiling — remaining items import on the next sync`,
545
575
  );
546
576
  }
547
577
  }
@@ -176,7 +176,9 @@ export interface WearableSourceSettings {
176
176
  /** Drop extracted facts scored below this importance level. */
177
177
  minImportance: ImportanceLevel;
178
178
  /**
179
- * Cap on memories created per source per day. 0 disables the cap.
179
+ * Cap on memories created per source per day. 0 (the default)
180
+ * disables the cap — the smart trust pipeline is the quality gate,
181
+ * and a count cap would drop real memories on busy days.
180
182
  */
181
183
  maxMemoriesPerDay: number;
182
184
  /**
@@ -250,6 +252,23 @@ export interface WearablesConfig {
250
252
  * Default false.
251
253
  */
252
254
  digestEnabled: boolean;
255
+ /**
256
+ * Periodically refresh transcripts in-process (long-lived hosts).
257
+ * Every tick re-syncs `autoSyncDays` ending today — existing day
258
+ * files included, so the current day keeps growing while the
259
+ * wearable records. Default true.
260
+ */
261
+ autoSyncEnabled: boolean;
262
+ /** Minutes between auto-sync ticks (1-1440). Default 15. */
263
+ autoSyncIntervalMinutes: number;
264
+ /** Rolling window (days ending today) per tick (1-90). Default 2. */
265
+ autoSyncDays: number;
266
+ /**
267
+ * Once-per-local-day deep pass window (days, 0-90) picking up late
268
+ * uploads and provider re-processing. 0 disables; otherwise must be
269
+ * >= autoSyncDays. Default 7.
270
+ */
271
+ autoSyncDeepDays: number;
253
272
  /** Correction rules from config (merged with CLI-managed rules). */
254
273
  corrections: WearableCorrectionRule[];
255
274
  /** Per-source settings, keyed by connector id. */