@remnic/core 9.3.624 → 9.3.625
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 +18 -16
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +12 -5
- package/dist/access-http.js +10 -9
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +8 -8
- package/dist/access-schema.d.ts +5 -5
- package/dist/{access-service-CBNEKjzN.d.ts → access-service-C_sfOHsX.d.ts} +26 -3
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +7 -7
- 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 +2 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -3
- 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 -4
- package/dist/{chunk-7TPH6UZL.js → chunk-2RHI3FGV.js} +540 -17
- package/dist/chunk-2RHI3FGV.js.map +1 -0
- package/dist/{chunk-GYTVOLNX.js → chunk-3MNBW7R7.js} +2 -2
- package/dist/{chunk-QFQQFX2H.js → chunk-3R2UZV3U.js} +2 -2
- package/dist/{chunk-O4UNM6OR.js → chunk-532VCWYW.js} +2 -2
- package/dist/{chunk-2UFQYU5F.js → chunk-57QXN2CS.js} +2 -2
- package/dist/chunk-7WV3F5DQ.js +22 -0
- package/dist/chunk-7WV3F5DQ.js.map +1 -0
- package/dist/{chunk-RKW6QR7W.js → chunk-AZ4RI3QD.js} +1461 -78
- package/dist/chunk-AZ4RI3QD.js.map +1 -0
- package/dist/{chunk-KQFQ3IS5.js → chunk-F3FY3D3S.js} +43 -7
- package/dist/chunk-F3FY3D3S.js.map +1 -0
- package/dist/{chunk-4R4KTDIE.js → chunk-FPNQF475.js} +1 -1
- package/dist/chunk-FPNQF475.js.map +1 -0
- package/dist/{chunk-UGEBPVNI.js → chunk-GE7Q7KXP.js} +2 -2
- package/dist/{chunk-GLWW3EJQ.js → chunk-KB4MFBF5.js} +3 -3
- package/dist/{chunk-5GOMXHLC.js → chunk-KKTXCFD7.js} +255 -1
- package/dist/chunk-KKTXCFD7.js.map +1 -0
- package/dist/{chunk-FH3PPO42.js → chunk-KVFYTRMV.js} +2 -2
- package/dist/{chunk-BNW5NJJH.js → chunk-LQYTQCXM.js} +2 -2
- package/dist/{chunk-AYHXQR53.js → chunk-MVQN73GT.js} +2 -2
- package/dist/{chunk-ZZPIJPPD.js → chunk-N5RGXWLQ.js} +2 -2
- package/dist/chunk-NDAH7BJ5.js +213 -0
- package/dist/chunk-NDAH7BJ5.js.map +1 -0
- package/dist/{chunk-R3OQGYOU.js → chunk-P2D2MM47.js} +2 -2
- package/dist/{chunk-PSUB67YB.js → chunk-PW6GURU3.js} +2 -2
- package/dist/{chunk-W3BKVM64.js → chunk-QDV6VAD4.js} +2 -2
- package/dist/{chunk-3QSU4NFF.js → chunk-QHXW3LZV.js} +3 -3
- package/dist/{chunk-I6UCUHLK.js → chunk-SHV5Y2WU.js} +182 -3
- package/dist/chunk-SHV5Y2WU.js.map +1 -0
- package/dist/{chunk-OZXVGYGZ.js → chunk-STDAAGH7.js} +2 -2
- package/dist/{chunk-FMGWXIES.js → chunk-TZDSNIRO.js} +5 -5
- package/dist/{chunk-2L54V4ZO.js → chunk-UELS6WWF.js} +2 -2
- package/dist/{chunk-PJGB7XRR.js → chunk-UGHUNQ74.js} +502 -134
- package/dist/chunk-UGHUNQ74.js.map +1 -0
- package/dist/{chunk-FG76RDVI.js → chunk-Y3TMFC6I.js} +136 -4
- package/dist/chunk-Y3TMFC6I.js.map +1 -0
- package/dist/{chunk-BPSGLMQ4.js → chunk-YQNADJCT.js} +2 -2
- package/dist/{cli-Cw729yLf.d.ts → cli-EZv6YE6_.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +23 -21
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -3
- 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 +2 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +2 -2
- 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 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- 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 +307 -9
- package/dist/index.js +155 -29
- 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 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- 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 -4
- 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 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +8 -7
- package/dist/{orchestrator-CqWOjfgl.d.ts → orchestrator-CEycaY3M.d.ts} +361 -4
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +13 -11
- 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-explain-renderer.js +3 -3
- 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-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -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/schemas.d.ts +10 -10
- 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-SLAa_prH.d.ts → semantic-DJR8_DMQ.d.ts} +1 -1
- package/dist/{semantic-consolidation-4HkHWgeI.d.ts → semantic-consolidation-FbhPeJjB.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -3
- 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 +5 -5
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +19 -1
- package/dist/storage.js +2 -2
- 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-D5VRAI04.d.ts +3134 -0
- package/dist/types.d.ts +3 -2862
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/access-http.ts +167 -0
- package/src/access-mcp.ts +198 -0
- package/src/access-service.ts +65 -0
- package/src/cli.ts +187 -0
- package/src/config.ts +7 -0
- package/src/index.ts +7 -0
- package/src/orchestrator.ts +42 -0
- package/src/storage.ts +106 -0
- package/src/types.ts +5 -0
- package/src/wearables/cleanup.test.ts +134 -0
- package/src/wearables/cleanup.ts +188 -0
- package/src/wearables/cli.test.ts +170 -0
- package/src/wearables/cli.ts +441 -0
- package/src/wearables/config.test.ts +143 -0
- package/src/wearables/config.ts +332 -0
- package/src/wearables/corrections.test.ts +118 -0
- package/src/wearables/corrections.ts +211 -0
- package/src/wearables/day-store.test.ts +143 -0
- package/src/wearables/day-store.ts +238 -0
- package/src/wearables/errors.test.ts +32 -0
- package/src/wearables/errors.ts +29 -0
- package/src/wearables/index.ts +114 -0
- package/src/wearables/memory-gen.test.ts +342 -0
- package/src/wearables/memory-gen.ts +413 -0
- package/src/wearables/pipeline.test.ts +608 -0
- package/src/wearables/pipeline.ts +519 -0
- package/src/wearables/redaction.test.ts +94 -0
- package/src/wearables/redaction.ts +156 -0
- package/src/wearables/registry.test.ts +62 -0
- package/src/wearables/registry.ts +133 -0
- package/src/wearables/service.test.ts +425 -0
- package/src/wearables/service.ts +691 -0
- package/src/wearables/speakers.test.ts +110 -0
- package/src/wearables/speakers.ts +174 -0
- package/src/wearables/storage-io.test.ts +105 -0
- package/src/wearables/sync-state.test.ts +134 -0
- package/src/wearables/sync-state.ts +186 -0
- package/src/wearables/types.ts +285 -0
- package/dist/chunk-4R4KTDIE.js.map +0 -1
- package/dist/chunk-5GOMXHLC.js.map +0 -1
- package/dist/chunk-7TPH6UZL.js.map +0 -1
- package/dist/chunk-FG76RDVI.js.map +0 -1
- package/dist/chunk-I6UCUHLK.js.map +0 -1
- package/dist/chunk-KQFQ3IS5.js.map +0 -1
- package/dist/chunk-PJGB7XRR.js.map +0 -1
- package/dist/chunk-RKW6QR7W.js.map +0 -1
- /package/dist/{chunk-GYTVOLNX.js.map → chunk-3MNBW7R7.js.map} +0 -0
- /package/dist/{chunk-QFQQFX2H.js.map → chunk-3R2UZV3U.js.map} +0 -0
- /package/dist/{chunk-O4UNM6OR.js.map → chunk-532VCWYW.js.map} +0 -0
- /package/dist/{chunk-2UFQYU5F.js.map → chunk-57QXN2CS.js.map} +0 -0
- /package/dist/{chunk-UGEBPVNI.js.map → chunk-GE7Q7KXP.js.map} +0 -0
- /package/dist/{chunk-GLWW3EJQ.js.map → chunk-KB4MFBF5.js.map} +0 -0
- /package/dist/{chunk-FH3PPO42.js.map → chunk-KVFYTRMV.js.map} +0 -0
- /package/dist/{chunk-BNW5NJJH.js.map → chunk-LQYTQCXM.js.map} +0 -0
- /package/dist/{chunk-AYHXQR53.js.map → chunk-MVQN73GT.js.map} +0 -0
- /package/dist/{chunk-ZZPIJPPD.js.map → chunk-N5RGXWLQ.js.map} +0 -0
- /package/dist/{chunk-R3OQGYOU.js.map → chunk-P2D2MM47.js.map} +0 -0
- /package/dist/{chunk-PSUB67YB.js.map → chunk-PW6GURU3.js.map} +0 -0
- /package/dist/{chunk-W3BKVM64.js.map → chunk-QDV6VAD4.js.map} +0 -0
- /package/dist/{chunk-3QSU4NFF.js.map → chunk-QHXW3LZV.js.map} +0 -0
- /package/dist/{chunk-OZXVGYGZ.js.map → chunk-STDAAGH7.js.map} +0 -0
- /package/dist/{chunk-FMGWXIES.js.map → chunk-TZDSNIRO.js.map} +0 -0
- /package/dist/{chunk-2L54V4ZO.js.map → chunk-UELS6WWF.js.map} +0 -0
- /package/dist/{chunk-BPSGLMQ4.js.map → chunk-YQNADJCT.js.map} +0 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wearable memory generation — trust-gated extraction from cleaned day
|
|
3
|
+
* transcripts.
|
|
4
|
+
*
|
|
5
|
+
* Wearable ASR quality varies wildly between providers and rooms, so
|
|
6
|
+
* unlike live-session extraction this path is gated per source:
|
|
7
|
+
*
|
|
8
|
+
* - memoryMode "off" -> never runs
|
|
9
|
+
* - memoryMode "review" -> candidates land with status
|
|
10
|
+
* "pending_review" (operator approves via
|
|
11
|
+
* the existing review-queue surfaces)
|
|
12
|
+
* - memoryMode "auto" -> candidates that pass every gate land
|
|
13
|
+
* active
|
|
14
|
+
*
|
|
15
|
+
* Deterministic gates (applied in order, all modes except "off"):
|
|
16
|
+
* 1. category gate — procedure / reasoning_trace candidates are
|
|
17
|
+
* skipped (they need richer persistence than
|
|
18
|
+
* this path provides)
|
|
19
|
+
* 2. confidence floor — `minConfidence`
|
|
20
|
+
* 3. importance floor — local `scoreImportance` >= `minImportance`
|
|
21
|
+
* 4. dedup — storage content-hash index + intra-run set
|
|
22
|
+
* 5. day cap — top `maxMemoriesPerDay` by importance score
|
|
23
|
+
* (0 disables the cap)
|
|
24
|
+
*
|
|
25
|
+
* The extraction engine itself is injected so this module stays free of
|
|
26
|
+
* LLM-client construction; callers hand in `orchestrator`-owned or
|
|
27
|
+
* standalone engines alike.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { scoreImportance } from "../importance.js";
|
|
31
|
+
import { describeErrorForOperator } from "./errors.js";
|
|
32
|
+
import type {
|
|
33
|
+
BufferTurn,
|
|
34
|
+
ExtractedFact,
|
|
35
|
+
ExtractionResult,
|
|
36
|
+
ImportanceLevel,
|
|
37
|
+
ImportanceScore,
|
|
38
|
+
MemoryCategory,
|
|
39
|
+
MemoryStatus,
|
|
40
|
+
} from "../types.js";
|
|
41
|
+
import { resolveSpeaker, type SpeakerRegistry } from "./speakers.js";
|
|
42
|
+
import type {
|
|
43
|
+
WearableConversation,
|
|
44
|
+
WearableMemoryMode,
|
|
45
|
+
WearableNativeMemory,
|
|
46
|
+
WearableSourceSettings,
|
|
47
|
+
} from "./types.js";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Memory status used for a given memory mode. Least-privilege default:
|
|
51
|
+
* anything that is not explicitly "auto" lands in the review queue
|
|
52
|
+
* rather than active recall.
|
|
53
|
+
*/
|
|
54
|
+
export function memoryStatusForMode(
|
|
55
|
+
mode: WearableMemoryMode,
|
|
56
|
+
): Extract<MemoryStatus, "active" | "pending_review"> {
|
|
57
|
+
return mode === "auto" ? "active" : "pending_review";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Narrow writer interface satisfied by `StorageManager`. */
|
|
61
|
+
export interface WearableMemoryWriter {
|
|
62
|
+
writeMemory(
|
|
63
|
+
category: MemoryCategory,
|
|
64
|
+
content: string,
|
|
65
|
+
options: {
|
|
66
|
+
confidence?: number;
|
|
67
|
+
tags?: string[];
|
|
68
|
+
source?: string;
|
|
69
|
+
importance?: ImportanceScore;
|
|
70
|
+
validAt?: string;
|
|
71
|
+
structuredAttributes?: Record<string, string>;
|
|
72
|
+
contentHashSource?: string;
|
|
73
|
+
status?: MemoryStatus;
|
|
74
|
+
memoryKind?: "episode" | "note" | "box" | "dream" | "procedural";
|
|
75
|
+
},
|
|
76
|
+
): Promise<string>;
|
|
77
|
+
hasFactContentHash(content: string): Promise<boolean>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface WearableMemoryGenDeps {
|
|
81
|
+
extract(turns: BufferTurn[]): Promise<ExtractionResult>;
|
|
82
|
+
writer: WearableMemoryWriter;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface WearableMemoryGenResult {
|
|
86
|
+
created: number;
|
|
87
|
+
skipped: number;
|
|
88
|
+
skippedByReason: Record<string, number>;
|
|
89
|
+
/** Non-fatal problems (e.g. the extraction engine erroring). */
|
|
90
|
+
warnings: string[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const WEARABLE_SOURCE_PREFIX = "wearable";
|
|
94
|
+
|
|
95
|
+
export function wearableSourceLabel(sourceId: string): string {
|
|
96
|
+
return `${WEARABLE_SOURCE_PREFIX}:${sourceId}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function wearableDayTag(date: string): string {
|
|
100
|
+
return `wearable-day:${date}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const IMPORTANCE_RANK: Record<ImportanceLevel, number> = {
|
|
104
|
+
trivial: 0,
|
|
105
|
+
low: 1,
|
|
106
|
+
normal: 2,
|
|
107
|
+
high: 3,
|
|
108
|
+
critical: 4,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** Max characters of transcript per extraction turn. */
|
|
112
|
+
const MAX_EXTRACTION_CHUNK_CHARS = 6_000;
|
|
113
|
+
/** Skip extraction for conversations with less substance than this. */
|
|
114
|
+
const MIN_CONVERSATION_CHARS = 80;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Render a cleaned conversation into extraction-ready turns. Each turn
|
|
118
|
+
* carries a labeled multi-speaker transcript block; the wearer is
|
|
119
|
+
* marked "(you)" so first-person facts attribute correctly.
|
|
120
|
+
*/
|
|
121
|
+
export function buildExtractionTurns(
|
|
122
|
+
sourceId: string,
|
|
123
|
+
date: string,
|
|
124
|
+
conversation: WearableConversation,
|
|
125
|
+
registry: SpeakerRegistry,
|
|
126
|
+
): BufferTurn[] {
|
|
127
|
+
const headerParts = [
|
|
128
|
+
`Wearable transcript (${sourceId}) — ${date}`,
|
|
129
|
+
conversation.title ? `"${conversation.title}"` : undefined,
|
|
130
|
+
conversation.location ? `at ${conversation.location}` : undefined,
|
|
131
|
+
].filter((part): part is string => typeof part === "string");
|
|
132
|
+
const header = `[${headerParts.join(" — ")}]`;
|
|
133
|
+
|
|
134
|
+
const lines: string[] = [];
|
|
135
|
+
for (const segment of conversation.segments) {
|
|
136
|
+
const { label } = resolveSpeaker(sourceId, segment, registry);
|
|
137
|
+
lines.push(`${label}: ${segment.text}`);
|
|
138
|
+
}
|
|
139
|
+
const transcript = lines.join("\n");
|
|
140
|
+
if (transcript.trim().length < MIN_CONVERSATION_CHARS) return [];
|
|
141
|
+
|
|
142
|
+
const sessionKey = `wearables:${sourceId}:${date}:${conversation.id}`;
|
|
143
|
+
const timestamp = conversation.startIso;
|
|
144
|
+
const turns: BufferTurn[] = [];
|
|
145
|
+
let chunkLines: string[] = [];
|
|
146
|
+
let chunkChars = 0;
|
|
147
|
+
const flush = () => {
|
|
148
|
+
if (chunkLines.length === 0) return;
|
|
149
|
+
turns.push({
|
|
150
|
+
role: "user",
|
|
151
|
+
content: `${header}\n${chunkLines.join("\n")}`,
|
|
152
|
+
timestamp,
|
|
153
|
+
sourceValidAt: timestamp,
|
|
154
|
+
sessionKey,
|
|
155
|
+
});
|
|
156
|
+
chunkLines = [];
|
|
157
|
+
chunkChars = 0;
|
|
158
|
+
};
|
|
159
|
+
for (const line of transcript.split("\n")) {
|
|
160
|
+
if (chunkChars + line.length + 1 > MAX_EXTRACTION_CHUNK_CHARS) flush();
|
|
161
|
+
chunkLines.push(line);
|
|
162
|
+
chunkChars += line.length + 1;
|
|
163
|
+
}
|
|
164
|
+
flush();
|
|
165
|
+
return turns;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
interface GatedCandidate {
|
|
169
|
+
fact: ExtractedFact;
|
|
170
|
+
importance: ImportanceScore;
|
|
171
|
+
conversation: WearableConversation;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Run extraction + gates over a day's conversations and persist the
|
|
176
|
+
* survivors. Returns counts for the sync summary.
|
|
177
|
+
*/
|
|
178
|
+
export async function generateWearableMemories(
|
|
179
|
+
sourceId: string,
|
|
180
|
+
date: string,
|
|
181
|
+
conversations: WearableConversation[],
|
|
182
|
+
settings: WearableSourceSettings,
|
|
183
|
+
registry: SpeakerRegistry,
|
|
184
|
+
deps: WearableMemoryGenDeps,
|
|
185
|
+
): Promise<WearableMemoryGenResult> {
|
|
186
|
+
const result: WearableMemoryGenResult = {
|
|
187
|
+
created: 0,
|
|
188
|
+
skipped: 0,
|
|
189
|
+
skippedByReason: {},
|
|
190
|
+
warnings: [],
|
|
191
|
+
};
|
|
192
|
+
if (settings.memoryMode === "off") return result;
|
|
193
|
+
|
|
194
|
+
const skip = (reason: string, count = 1): void => {
|
|
195
|
+
result.skipped += count;
|
|
196
|
+
result.skippedByReason[reason] =
|
|
197
|
+
(result.skippedByReason[reason] ?? 0) + count;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const candidates: GatedCandidate[] = [];
|
|
201
|
+
const seenContent = new Set<string>();
|
|
202
|
+
|
|
203
|
+
for (const conversation of conversations) {
|
|
204
|
+
const turns = buildExtractionTurns(sourceId, date, conversation, registry);
|
|
205
|
+
if (turns.length === 0) continue;
|
|
206
|
+
let extraction: ExtractionResult;
|
|
207
|
+
try {
|
|
208
|
+
extraction = await deps.extract(turns);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
// One failing extraction call almost always means every call will
|
|
211
|
+
// fail (missing key, provider outage) — stop hammering the engine
|
|
212
|
+
// and surface a single actionable warning instead of one per
|
|
213
|
+
// conversation. Candidates gathered before the failure still
|
|
214
|
+
// persist below.
|
|
215
|
+
result.warnings.push(
|
|
216
|
+
`extraction failed for ${sourceId}/${date} (conversation ${conversation.id}): ${describeErrorForOperator(err)} — the memory pass for this day retries on the next sync`,
|
|
217
|
+
);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
for (const fact of extraction.facts) {
|
|
221
|
+
const content = fact.content?.trim();
|
|
222
|
+
if (!content) {
|
|
223
|
+
skip("empty");
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (fact.category === "procedure" || fact.category === "reasoning_trace") {
|
|
227
|
+
skip("unsupported-category");
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (
|
|
231
|
+
typeof fact.confidence === "number" &&
|
|
232
|
+
fact.confidence < settings.minConfidence
|
|
233
|
+
) {
|
|
234
|
+
skip("below-confidence");
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const importance = scoreImportance(content, fact.category, fact.tags ?? []);
|
|
238
|
+
if (
|
|
239
|
+
IMPORTANCE_RANK[importance.level] <
|
|
240
|
+
IMPORTANCE_RANK[settings.minImportance]
|
|
241
|
+
) {
|
|
242
|
+
skip("below-importance");
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const dedupKey = content.toLowerCase();
|
|
246
|
+
if (seenContent.has(dedupKey)) {
|
|
247
|
+
skip("duplicate-in-run");
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
seenContent.add(dedupKey);
|
|
251
|
+
candidates.push({ fact: { ...fact, content }, importance, conversation });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Drop candidates that already exist in storage BEFORE applying the
|
|
256
|
+
// day cap so duplicates never consume cap slots that novel,
|
|
257
|
+
// lower-scoring candidates should get (Codex P2 on PR #1458).
|
|
258
|
+
const novel: GatedCandidate[] = [];
|
|
259
|
+
for (const candidate of candidates) {
|
|
260
|
+
if (await deps.writer.hasFactContentHash(candidate.fact.content)) {
|
|
261
|
+
skip("duplicate-existing");
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
novel.push(candidate);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Day cap: keep the most important candidates. Stable ordering —
|
|
268
|
+
// score desc, then content asc so equal scores compare 0-consistent.
|
|
269
|
+
novel.sort((a, b) => {
|
|
270
|
+
if (a.importance.score > b.importance.score) return -1;
|
|
271
|
+
if (a.importance.score < b.importance.score) return 1;
|
|
272
|
+
if (a.fact.content < b.fact.content) return -1;
|
|
273
|
+
if (a.fact.content > b.fact.content) return 1;
|
|
274
|
+
return 0;
|
|
275
|
+
});
|
|
276
|
+
const cap = settings.maxMemoriesPerDay;
|
|
277
|
+
const kept = cap > 0 ? novel.slice(0, cap) : novel;
|
|
278
|
+
if (novel.length > kept.length) {
|
|
279
|
+
skip("over-day-cap", novel.length - kept.length);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const status = memoryStatusForMode(settings.memoryMode);
|
|
283
|
+
for (const candidate of kept) {
|
|
284
|
+
const tags = [
|
|
285
|
+
...new Set([
|
|
286
|
+
...(candidate.fact.tags ?? []),
|
|
287
|
+
WEARABLE_SOURCE_PREFIX,
|
|
288
|
+
wearableSourceLabel(sourceId),
|
|
289
|
+
wearableDayTag(date),
|
|
290
|
+
]),
|
|
291
|
+
];
|
|
292
|
+
await deps.writer.writeMemory(candidate.fact.category, candidate.fact.content, {
|
|
293
|
+
confidence: candidate.fact.confidence,
|
|
294
|
+
tags,
|
|
295
|
+
source: wearableSourceLabel(sourceId),
|
|
296
|
+
importance: candidate.importance,
|
|
297
|
+
validAt: candidate.conversation.startIso,
|
|
298
|
+
structuredAttributes: {
|
|
299
|
+
...(candidate.fact.structuredAttributes ?? {}),
|
|
300
|
+
wearableSource: sourceId,
|
|
301
|
+
wearableDate: date,
|
|
302
|
+
wearableConversationId: candidate.conversation.id,
|
|
303
|
+
},
|
|
304
|
+
contentHashSource: candidate.fact.content,
|
|
305
|
+
status,
|
|
306
|
+
});
|
|
307
|
+
result.created += 1;
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Write one compact daily-digest memory summarizing the day's recorded
|
|
314
|
+
* conversations. Deterministic (no LLM): titles, time ranges, speaker
|
|
315
|
+
* counts. Gated by `wearables.digestEnabled`.
|
|
316
|
+
*/
|
|
317
|
+
export async function writeDailyDigestMemory(
|
|
318
|
+
sourceId: string,
|
|
319
|
+
date: string,
|
|
320
|
+
conversations: WearableConversation[],
|
|
321
|
+
settings: WearableSourceSettings,
|
|
322
|
+
registry: SpeakerRegistry,
|
|
323
|
+
writer: WearableMemoryWriter,
|
|
324
|
+
): Promise<boolean> {
|
|
325
|
+
if (settings.memoryMode === "off") return false;
|
|
326
|
+
if (conversations.length === 0) return false;
|
|
327
|
+
const lines = conversations.map((conversation) => {
|
|
328
|
+
const title = conversation.title?.trim() || "Untitled conversation";
|
|
329
|
+
const speakers = new Set(
|
|
330
|
+
conversation.segments.map(
|
|
331
|
+
(segment) => resolveSpeaker(sourceId, segment, registry).label,
|
|
332
|
+
),
|
|
333
|
+
);
|
|
334
|
+
return `- ${title} (${speakers.size} speaker${speakers.size === 1 ? "" : "s"})`;
|
|
335
|
+
});
|
|
336
|
+
const content =
|
|
337
|
+
`Wearable day digest — ${sourceId}, ${date}: ` +
|
|
338
|
+
`${conversations.length} recorded conversation${conversations.length === 1 ? "" : "s"}.\n` +
|
|
339
|
+
lines.join("\n");
|
|
340
|
+
if (await writer.hasFactContentHash(content)) return false;
|
|
341
|
+
await writer.writeMemory("moment", content, {
|
|
342
|
+
confidence: 0.9,
|
|
343
|
+
tags: [
|
|
344
|
+
WEARABLE_SOURCE_PREFIX,
|
|
345
|
+
wearableSourceLabel(sourceId),
|
|
346
|
+
wearableDayTag(date),
|
|
347
|
+
"daily-digest",
|
|
348
|
+
],
|
|
349
|
+
source: wearableSourceLabel(sourceId),
|
|
350
|
+
importance: scoreImportance(content, "moment", ["daily-digest"]),
|
|
351
|
+
validAt: `${date}T00:00:00.000Z`,
|
|
352
|
+
structuredAttributes: {
|
|
353
|
+
wearableSource: sourceId,
|
|
354
|
+
wearableDate: date,
|
|
355
|
+
},
|
|
356
|
+
contentHashSource: content,
|
|
357
|
+
status: memoryStatusForMode(settings.memoryMode),
|
|
358
|
+
memoryKind: "episode",
|
|
359
|
+
});
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Import provider-extracted memories (Bee facts, Omi memories) into the
|
|
365
|
+
* review queue. Always `pending_review` regardless of memoryMode — the
|
|
366
|
+
* provider's extraction quality is outside Remnic's control.
|
|
367
|
+
*/
|
|
368
|
+
export async function importNativeMemories(
|
|
369
|
+
sourceId: string,
|
|
370
|
+
memories: WearableNativeMemory[],
|
|
371
|
+
alreadyImportedIds: ReadonlySet<string>,
|
|
372
|
+
writer: WearableMemoryWriter,
|
|
373
|
+
): Promise<{ imported: number; importedIds: string[] }> {
|
|
374
|
+
let imported = 0;
|
|
375
|
+
const importedIds: string[] = [];
|
|
376
|
+
const seenContent = new Set<string>();
|
|
377
|
+
for (const memory of memories) {
|
|
378
|
+
const content = memory.content?.trim();
|
|
379
|
+
if (!content) continue;
|
|
380
|
+
if (alreadyImportedIds.has(memory.id)) continue;
|
|
381
|
+
// Intra-run + cross-run dedup: the storage hash index only learns a
|
|
382
|
+
// fact after its write lands, so same-content items within one page
|
|
383
|
+
// batch need the local set.
|
|
384
|
+
if (seenContent.has(content) || (await writer.hasFactContentHash(content))) {
|
|
385
|
+
importedIds.push(memory.id);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
seenContent.add(content);
|
|
389
|
+
await writer.writeMemory("fact", content, {
|
|
390
|
+
confidence: 0.6,
|
|
391
|
+
tags: [
|
|
392
|
+
...new Set([
|
|
393
|
+
...(memory.tags ?? []),
|
|
394
|
+
WEARABLE_SOURCE_PREFIX,
|
|
395
|
+
wearableSourceLabel(sourceId),
|
|
396
|
+
"native-import",
|
|
397
|
+
]),
|
|
398
|
+
],
|
|
399
|
+
source: `${wearableSourceLabel(sourceId)}:native`,
|
|
400
|
+
importance: scoreImportance(content, "fact", memory.tags ?? []),
|
|
401
|
+
validAt: memory.createdIso,
|
|
402
|
+
structuredAttributes: {
|
|
403
|
+
wearableSource: sourceId,
|
|
404
|
+
wearableNativeId: memory.id,
|
|
405
|
+
},
|
|
406
|
+
contentHashSource: content,
|
|
407
|
+
status: "pending_review",
|
|
408
|
+
});
|
|
409
|
+
imported += 1;
|
|
410
|
+
importedIds.push(memory.id);
|
|
411
|
+
}
|
|
412
|
+
return { imported, importedIds };
|
|
413
|
+
}
|