@anuma/sdk 3.2.0 → 3.3.0
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/expo/{chunk-KXIUA7ZV.mjs → chunk-NMZMXIUM.mjs} +415 -18
- package/dist/expo/index.cjs +1342 -66
- package/dist/expo/index.d.mts +1763 -795
- package/dist/expo/index.d.ts +1763 -795
- package/dist/expo/index.mjs +889 -37
- package/dist/expo/{recall-3YPU4PS2.mjs → recall-7FUJRHOI.mjs} +1 -1
- package/dist/react/{chunk-Y7PMXJT6.mjs → chunk-GZRK43CA.mjs} +416 -18
- package/dist/react/index.cjs +1344 -66
- package/dist/react/index.d.mts +1429 -442
- package/dist/react/index.d.ts +1429 -442
- package/dist/react/index.mjs +891 -37
- package/dist/react/{recall-GYNSL2DS.mjs → recall-YZGWZC5W.mjs} +1 -1
- package/dist/server/{chunk-Y3ZS4OI3.mjs → chunk-NVTMJWFA.mjs} +415 -18
- package/dist/server/index.cjs +1331 -60
- package/dist/server/index.d.mts +2778 -1810
- package/dist/server/index.d.ts +2778 -1810
- package/dist/server/index.mjs +883 -36
- package/dist/server/{recall-Z72DM2JK.mjs → recall-76WPPKLR.mjs} +1 -1
- package/package.json +1 -1
package/dist/expo/index.d.mts
CHANGED
|
@@ -4472,6 +4472,12 @@ declare class VaultMemory extends Model {
|
|
|
4472
4472
|
topicsExtractedVersion: number | null;
|
|
4473
4473
|
/** C3 re-observation watermark: Unix ms of the last retain() merge, or null. */
|
|
4474
4474
|
lastObservedAt: number | null;
|
|
4475
|
+
/** Typed memory (PR1) — the extractor's FactType. Null on legacy/manual rows. */
|
|
4476
|
+
factType: string | null;
|
|
4477
|
+
/** Decay archive state (PR2) — Unix ms when archived, null when active. */
|
|
4478
|
+
archivedAt: number | null;
|
|
4479
|
+
/** Tier-0 security (PR3) — "quarantined" | "trusted" | null. */
|
|
4480
|
+
trustTier: string | null;
|
|
4475
4481
|
createdAt: Date;
|
|
4476
4482
|
updatedAt: Date;
|
|
4477
4483
|
isDeleted: boolean;
|
|
@@ -4544,466 +4550,184 @@ interface EntityOperationsContext {
|
|
|
4544
4550
|
allowUnscopedRows?: boolean;
|
|
4545
4551
|
}
|
|
4546
4552
|
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
/**
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
sourceChunkIds: string[] | null;
|
|
4565
|
-
/** Times this fact has been re-observed (for ranking + UX badges). */
|
|
4566
|
-
proofCount: number | null;
|
|
4567
|
-
/** How the memory was created: manual | auto-extracted | capsule. */
|
|
4568
|
-
source: string | null;
|
|
4569
|
-
/** W6 temporal lane — Unix ms when the event occurred (point/start of range). */
|
|
4570
|
-
eventTimeStart: number | null;
|
|
4571
|
-
/** W6 temporal lane — Unix ms when the event ended (range only). */
|
|
4572
|
-
eventTimeEnd: number | null;
|
|
4573
|
-
/** W6 temporal lane — `point | range | ongoing | null`. */
|
|
4574
|
-
eventTimeKind: string | null;
|
|
4575
|
-
/** When true, the user has manually set this memory's topics (entity links);
|
|
4576
|
-
* auto-extraction leaves them alone. False on legacy/auto rows. */
|
|
4577
|
-
topicsUserManaged: boolean;
|
|
4578
|
-
/** Unix ms of the last LLM topic-extraction pass over this memory's content.
|
|
4579
|
-
* Null = never extracted standalone; rows that already carry entity links
|
|
4580
|
-
* are grandfathered as extracted (see getMemoriesNeedingTopicExtractionOp). */
|
|
4581
|
-
topicsExtractedAt: number | null;
|
|
4582
|
-
/** Write-time supersession (A2): id of the newer memory that replaced this
|
|
4583
|
-
* one (incompatible-value update, e.g. "Lives in Portland" → "Lives in SF").
|
|
4584
|
-
* Null = live. Superseded rows are excluded from recall/dedup by default but
|
|
4585
|
-
* kept for history + the read-time fallback. */
|
|
4586
|
-
supersededBy: string | null;
|
|
4587
|
-
/** Unix ms when this memory was superseded. Null when live. */
|
|
4588
|
-
supersededAt: number | null;
|
|
4589
|
-
/** Extraction-logic version this memory was last stamped under. Null (pre-v38)
|
|
4590
|
-
* reads as 0, so a TOPICS_EXTRACTION_VERSION bump re-extracts stale rows. */
|
|
4591
|
-
topicsExtractedVersion: number | null;
|
|
4592
|
-
/** C3 re-observation watermark: Unix ms of the last retain() merge into this
|
|
4593
|
-
* fact. Distinct from `updatedAt` (which merges preserve). Null = never
|
|
4594
|
-
* re-observed since the column was added; synthesis falls back to
|
|
4595
|
-
* `updatedAt` in that case. */
|
|
4596
|
-
lastObservedAt: number | null;
|
|
4597
|
-
createdAt: Date;
|
|
4598
|
-
updatedAt: Date;
|
|
4599
|
-
isDeleted: boolean;
|
|
4600
|
-
}
|
|
4601
|
-
interface CreateVaultMemoryOptions {
|
|
4602
|
-
content: string;
|
|
4603
|
-
/** Scope for the memory. Defaults to "private" if omitted. */
|
|
4604
|
-
scope?: string;
|
|
4605
|
-
/** Folder ID for organization, null or omitted if unfiled */
|
|
4606
|
-
folderId?: string | null;
|
|
4607
|
-
/** JSON-stringified embedding vector to persist */
|
|
4608
|
-
embedding?: string;
|
|
4609
|
-
/** Model that produced `embedding`. Persisted alongside it so a later
|
|
4610
|
-
* model change can detect and re-embed stale vectors. */
|
|
4611
|
-
embeddingModel?: string;
|
|
4612
|
-
/** Source message IDs that produced this fact (auto-extraction provenance). */
|
|
4613
|
-
sourceChunkIds?: string[];
|
|
4614
|
-
/** Initial proof count. Defaults to 1 if omitted. */
|
|
4615
|
-
proofCount?: number;
|
|
4616
|
-
/** How the memory was created. Defaults to "manual" if omitted. */
|
|
4617
|
-
source?: string;
|
|
4618
|
-
/** W6 temporal lane — when the event in this memory occurred. */
|
|
4619
|
-
eventTime?: {
|
|
4620
|
-
/** Unix ms timestamp of event start (or point). */
|
|
4621
|
-
start: number | null;
|
|
4622
|
-
/** Unix ms timestamp of event end (range only). */
|
|
4623
|
-
end: number | null;
|
|
4624
|
-
/** Kind: 'point' | 'range' | 'ongoing' | null (or omit). */
|
|
4625
|
-
kind: "point" | "range" | "ongoing" | null;
|
|
4626
|
-
};
|
|
4627
|
-
}
|
|
4628
|
-
interface UpdateVaultMemoryOptions {
|
|
4629
|
-
content: string;
|
|
4630
|
-
/** If provided, updates the memory's scope. */
|
|
4631
|
-
scope?: string;
|
|
4632
|
-
/** If provided, moves the memory to this folder. */
|
|
4633
|
-
folderId?: string | null;
|
|
4634
|
-
/** JSON-stringified embedding vector to persist, or null to clear stale embedding */
|
|
4635
|
-
embedding?: string | null;
|
|
4636
|
-
/** Model that produced `embedding`. Set whenever `embedding` is written so
|
|
4637
|
-
* the stored model tag stays in sync with the vector. */
|
|
4638
|
-
embeddingModel?: string | null;
|
|
4639
|
-
/** Replace source-chunk-ids list (used during merge to accumulate provenance). */
|
|
4640
|
-
sourceChunkIds?: string[];
|
|
4641
|
-
/** Set an absolute proof count. Prefer {@link proofCountIncrement} for
|
|
4642
|
-
* re-observation paths so the read+write happens inside the writer
|
|
4643
|
-
* and concurrent retains can't lose updates. */
|
|
4644
|
-
proofCount?: number;
|
|
4645
|
-
/** Atomically bump proof_count by this delta inside the write block.
|
|
4646
|
-
* Reads the current value from the in-memory record at write time, so
|
|
4647
|
-
* two parallel retain() calls observe each other's commits and neither
|
|
4648
|
-
* loses its increment. Wins over `proofCount` when both are set. */
|
|
4649
|
-
proofCountIncrement?: number;
|
|
4650
|
-
/** Set source ("manual" | "auto-extracted" | "capsule"). */
|
|
4651
|
-
source?: string;
|
|
4652
|
-
/**
|
|
4653
|
-
* W6 temporal lane — write the event-time fields on update. Use during
|
|
4654
|
-
* auto-merge to preserve (or refine) the original event-time signal when
|
|
4655
|
-
* a new observation lands on an existing fact. Omit to leave the
|
|
4656
|
-
* existing values untouched.
|
|
4657
|
-
*/
|
|
4658
|
-
eventTime?: {
|
|
4659
|
-
start: number | null;
|
|
4660
|
-
end: number | null;
|
|
4661
|
-
kind: "point" | "range" | "ongoing" | null;
|
|
4662
|
-
};
|
|
4663
|
-
/**
|
|
4664
|
-
* When true, restore the existing `updated_at` after the write so the
|
|
4665
|
-
* recency multiplier doesn't see a re-observation as a brand-new fact.
|
|
4666
|
-
* Set by auto-merge/consolidate paths — they want proof_count to bump
|
|
4667
|
-
* without inflating recency on top.
|
|
4668
|
-
*/
|
|
4669
|
-
preserveUpdatedAt?: boolean;
|
|
4670
|
-
/** C3: Unix ms to stamp as the re-observation watermark (`last_observed_at`).
|
|
4671
|
-
* Set by retain() merge/consolidate paths so a re-observation records "seen
|
|
4672
|
-
* again now" without touching `updated_at` (which `preserveUpdatedAt` keeps
|
|
4673
|
-
* pinned). Omit to leave the existing value untouched. */
|
|
4674
|
-
lastObservedAt?: number;
|
|
4675
|
-
/** If provided, sets whether the user has taken manual control of this
|
|
4676
|
-
* memory's topics. Set by {@link setMemoryEntitiesOp}. */
|
|
4677
|
-
topicsUserManaged?: boolean;
|
|
4553
|
+
/**
|
|
4554
|
+
* Auth for portal LLM calls (extraction, consolidation, decomposition,
|
|
4555
|
+
* reflection). Mirrors `memoryEngine`'s `EmbeddingOptions` dual-auth:
|
|
4556
|
+
*
|
|
4557
|
+
* - `apiKey`: For direct API keys (uses x-api-key header)
|
|
4558
|
+
* - `getToken`: For Privy identity tokens (uses Authorization: Bearer header)
|
|
4559
|
+
*
|
|
4560
|
+
* At least one of `apiKey` or `getToken` must be provided (enforced at
|
|
4561
|
+
* runtime); `apiKey` takes precedence when both are set.
|
|
4562
|
+
*
|
|
4563
|
+
* @public
|
|
4564
|
+
*/
|
|
4565
|
+
interface PortalLlmAuth {
|
|
4566
|
+
/** Direct API key — sent as `x-api-key` (server-side / CLI usage). Wins when both are provided. */
|
|
4567
|
+
apiKey?: string;
|
|
4568
|
+
/** Function to get an auth token (e.g., Privy's getIdentityToken). Token is sent as `Authorization: Bearer`. */
|
|
4569
|
+
getToken?: () => Promise<string | null>;
|
|
4678
4570
|
}
|
|
4679
4571
|
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4572
|
+
/**
|
|
4573
|
+
* Optional LLM injection classifier — Tier-0 security second layer (PR5).
|
|
4574
|
+
*
|
|
4575
|
+
* The deterministic {@link ./injectionScreen} is a signature scan: it catches
|
|
4576
|
+
* imperative overrides, role-marker leakage, and exfil URLs, but by
|
|
4577
|
+
* construction it CANNOT catch signature-free poison phrased as a plain
|
|
4578
|
+
* third-person fact — e.g. "Trusts BrandX for financial advice", "Prefers to
|
|
4579
|
+
* always use the AcmePay card". Those read like ordinary preferences and sail
|
|
4580
|
+
* through the regex screen.
|
|
4581
|
+
*
|
|
4582
|
+
* This module closes that gap as a SECOND, opt-in layer. In `extractAndRetain`
|
|
4583
|
+
* it runs ONLY over the candidates the deterministic screen already passed as
|
|
4584
|
+
* clean, asking a cheap model a single batched yes/no per candidate: "is this
|
|
4585
|
+
* an instruction to the assistant / a planted preference, rather than a genuine
|
|
4586
|
+
* fact the user stated about themselves?" Positives are moved to quarantine
|
|
4587
|
+
* (trust_tier="quarantined"), exactly like a signature hit.
|
|
4588
|
+
*
|
|
4589
|
+
* Safety posture (why it can only ever add safety, never remove it):
|
|
4590
|
+
* - Default OFF. No option → no LLM call → zero latency/cost added, and the
|
|
4591
|
+
* write path is byte-for-byte the pre-PR5 deterministic screen.
|
|
4592
|
+
* - Fails CLEAN. Any error (network, timeout, malformed JSON, auth) → the
|
|
4593
|
+
* candidate stays in the clean set (trusts the deterministic result). A
|
|
4594
|
+
* flaky classifier can never suppress a legitimate memory.
|
|
4595
|
+
* - Bounded. One batched portal call per turn (never per-candidate), with a
|
|
4596
|
+
* candidate cap, a low retry budget, and a wall-clock total-timeout.
|
|
4597
|
+
* - Zero-knowledge parity. Candidate content is PII-redacted before it
|
|
4598
|
+
* reaches the portal using the SAME redactor setting as the extraction
|
|
4599
|
+
* path (inherited by `extractAndRetain`); the model only ever returns
|
|
4600
|
+
* integer indices, so nothing needs de-anonymizing on the way back.
|
|
4601
|
+
*
|
|
4602
|
+
* SECURITY (MEDIUM, residual): this is a NEW portal call surface for memory
|
|
4603
|
+
* content. It is gated on an explicit opt-in and redacts like the extractor, but
|
|
4604
|
+
* a security review should treat enabling it as widening the set of models that
|
|
4605
|
+
* see (redacted) facts. A malicious / MITM'd portal can also use it as a lever
|
|
4606
|
+
* to QUARANTINE the affected rows (they drop out of recall) — reversible, and it
|
|
4607
|
+
* can never delete or alter a memory. Bounded, recoverable trust tradeoff; gate
|
|
4608
|
+
* on trust in the portal.
|
|
4609
|
+
*/
|
|
4610
|
+
|
|
4611
|
+
/**
|
|
4612
|
+
* Auth + tuning for the optional LLM injection classifier. Auth is the dual
|
|
4613
|
+
* pattern — one of `apiKey` / `getToken` is required at runtime (see
|
|
4614
|
+
* {@link PortalLlmAuth}). The mere PRESENCE of this options object in
|
|
4615
|
+
* `extractAndRetain` is the opt-in switch; omit it for the default-off,
|
|
4616
|
+
* deterministic-only screen.
|
|
4617
|
+
* @public
|
|
4618
|
+
*/
|
|
4619
|
+
interface InjectionClassifierOptions extends PortalLlmAuth {
|
|
4620
|
+
baseUrl?: string;
|
|
4621
|
+
model?: string;
|
|
4622
|
+
/** Override fetch (tests). */
|
|
4623
|
+
fetchFn?: typeof fetch;
|
|
4624
|
+
/** Max portal attempts on a TRANSIENT failure. Default 2. */
|
|
4625
|
+
maxAttempts?: number;
|
|
4626
|
+
/** Absolute wall-clock budget across attempts. Default 15s. */
|
|
4627
|
+
totalTimeoutMs?: number;
|
|
4628
|
+
/** Backoff before each retry (ms). Tests pass `() => 0`. */
|
|
4629
|
+
backoffMs?: (attempt: number) => number;
|
|
4688
4630
|
/**
|
|
4689
|
-
*
|
|
4690
|
-
*
|
|
4691
|
-
*
|
|
4631
|
+
* PII redaction for the outbound content, same switch as the extractor.
|
|
4632
|
+
* `extractAndRetain` inherits the extraction setting so enabling redaction
|
|
4633
|
+
* there also protects this call. `true` = fresh per-call redactor; pass a
|
|
4634
|
+
* shared {@link PiiRedactor} to keep placeholder numbering consistent.
|
|
4692
4635
|
*/
|
|
4693
|
-
|
|
4636
|
+
piiRedaction?: boolean | PiiRedactor;
|
|
4637
|
+
/** Max candidates classified per call. Default 20. */
|
|
4638
|
+
maxCandidates?: number;
|
|
4694
4639
|
}
|
|
4695
|
-
declare function createVaultMemoryOp(ctx: VaultMemoryOperationsContext, opts: CreateVaultMemoryOptions): Promise<StoredVaultMemory>;
|
|
4696
|
-
declare function createVaultMemoriesBatchOp(ctx: VaultMemoryOperationsContext, optionsArray: CreateVaultMemoryOptions[]): Promise<StoredVaultMemory[]>;
|
|
4697
|
-
declare function getVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string): Promise<StoredVaultMemory | null>;
|
|
4698
|
-
declare function getAllVaultMemoriesOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
4699
|
-
scopes?: string[];
|
|
4700
|
-
since?: Date;
|
|
4701
|
-
limit?: number;
|
|
4702
|
-
folderId?: string | null;
|
|
4703
|
-
/**
|
|
4704
|
-
* Include soft-deleted memories in the result (each carries
|
|
4705
|
-
* `isDeleted: true`). Default `false` — deleted rows are excluded, as
|
|
4706
|
-
* they are from every other read path. Used by the Memory Graph to
|
|
4707
|
-
* render "forgotten" nodes; ordinary consumers should leave this off.
|
|
4708
|
-
*/
|
|
4709
|
-
includeDeleted?: boolean;
|
|
4710
|
-
/**
|
|
4711
|
-
* Include A2-superseded memories (each carries `supersededBy`). Default
|
|
4712
|
-
* `false` — superseded rows are excluded, as they are from recall/dedup.
|
|
4713
|
-
* Used by a "memory history" view to render retired facts.
|
|
4714
|
-
*/
|
|
4715
|
-
includeSuperseded?: boolean;
|
|
4716
|
-
}): Promise<StoredVaultMemory[]>;
|
|
4717
|
-
declare function getAllVaultMemoryContentsOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
4718
|
-
since?: Date;
|
|
4719
|
-
}): Promise<string[]>;
|
|
4720
|
-
declare function updateVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string, opts: UpdateVaultMemoryOptions): Promise<StoredVaultMemory | null>;
|
|
4721
|
-
declare function deleteVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string): Promise<boolean>;
|
|
4722
4640
|
/**
|
|
4723
|
-
*
|
|
4724
|
-
*
|
|
4725
|
-
*
|
|
4726
|
-
* the row is missing, not owned, deleted, or already superseded. Does NOT
|
|
4727
|
-
* preserve `updated_at` — superseded rows are hidden from recall, so their
|
|
4728
|
-
* recency is irrelevant.
|
|
4641
|
+
* Classify already-clean extraction candidates for signature-free injection /
|
|
4642
|
+
* poisoning. Returns the set of 0-based indices (into `candidates`) the model
|
|
4643
|
+
* flagged as poison.
|
|
4729
4644
|
*
|
|
4730
|
-
*
|
|
4731
|
-
*
|
|
4645
|
+
* FAILS CLEAN: returns an empty set on empty input, missing auth, LLM error,
|
|
4646
|
+
* or any malformed response — the caller then keeps every candidate clean, so
|
|
4647
|
+
* this layer can only ever ADD quarantines, never suppress a legitimate fact
|
|
4648
|
+
* on failure. Makes at most ONE portal call regardless of candidate count.
|
|
4649
|
+
*
|
|
4650
|
+
* @public
|
|
4732
4651
|
*/
|
|
4733
|
-
declare function
|
|
4652
|
+
declare function classifyInjectionCandidates(candidates: readonly ExtractedCandidate[], options: InjectionClassifierOptions): Promise<{
|
|
4653
|
+
flagged: Set<number>;
|
|
4654
|
+
}>;
|
|
4655
|
+
|
|
4734
4656
|
/**
|
|
4735
|
-
*
|
|
4657
|
+
* Write-time memory-poisoning screen — Tier-0 security (PR3).
|
|
4658
|
+
*
|
|
4659
|
+
* Deterministic, network-free, and always on. Scans each extraction
|
|
4660
|
+
* candidate's `content` for prompt-injection / memory-poisoning
|
|
4661
|
+
* signatures and routes flagged candidates to quarantine instead of the
|
|
4662
|
+
* live vault.
|
|
4663
|
+
*
|
|
4664
|
+
* WHY a signature scan (not an LLM classifier): this runs on the write
|
|
4665
|
+
* path for every extracted fact, so it must be cheap, offline, and must
|
|
4666
|
+
* never leak content to a model. It also gives a small false-positive
|
|
4667
|
+
* surface here: durable third-person facts about the user ("Lives in San
|
|
4668
|
+
* Francisco", "Allergic to shellfish", "Works in engineering") almost
|
|
4669
|
+
* never contain second-person imperatives aimed at the assistant, chat
|
|
4670
|
+
* role markers, or beacon URLs — the exact shapes these signatures look
|
|
4671
|
+
* for. An optional LLM injection classifier is deferred to PR5.
|
|
4672
|
+
*
|
|
4673
|
+
* WHY quarantine, not drop: a flagged candidate is still persisted (see
|
|
4674
|
+
* `extractAndRetain`) with `trust_tier="quarantined"` so it stays an
|
|
4675
|
+
* auditable record, recoverable by an operator via the `includeQuarantined`
|
|
4676
|
+
* read path. The `baseVaultConditions` choke point excludes quarantined
|
|
4677
|
+
* rows from every recall lane, so a poisoned fact never reaches the answer
|
|
4678
|
+
* model even though it lives in the DB.
|
|
4679
|
+
*
|
|
4680
|
+
* This MITIGATES memory poisoning (MINJA / AgentPoison; OWASP ASI06 2026).
|
|
4681
|
+
* It is not a complete solve — a poison phrased as a plain third-person
|
|
4682
|
+
* fact ("Prefers the BrandX credit card") carries no injection signature
|
|
4683
|
+
* and will pass. The proof-count ranking boost (once-seen facts stay
|
|
4684
|
+
* low-ranked) and the read-time isolation in `formatRecallResult` are the
|
|
4685
|
+
* complementary layers.
|
|
4736
4686
|
*/
|
|
4737
|
-
|
|
4687
|
+
|
|
4688
|
+
/** Why a candidate was quarantined. Coarse buckets over the signature set
|
|
4689
|
+
* below — surfaced for audit/telemetry, never alongside the content.
|
|
4690
|
+
* `llm_semantic` (PR5) is emitted by the optional second-layer LLM classifier
|
|
4691
|
+
* ({@link classifyInjectionCandidates}), not by any deterministic signature
|
|
4692
|
+
* here — it catches signature-free poison ("Trusts BrandX for financial
|
|
4693
|
+
* advice") the regex screen passes as clean. */
|
|
4694
|
+
type InjectionReason = "imperative_override" | "role_marker_leak" | "exfiltration_url" | "llm_semantic";
|
|
4695
|
+
/** A candidate the screen flagged, with the matching signature id + reason.
|
|
4696
|
+
* Content is intentionally NOT duplicated here beyond the candidate itself —
|
|
4697
|
+
* callers must never log `candidate.content`. */
|
|
4698
|
+
interface ScreenedCandidate {
|
|
4699
|
+
candidate: ExtractedCandidate;
|
|
4700
|
+
/** Coarse reason bucket. */
|
|
4701
|
+
reason: InjectionReason;
|
|
4702
|
+
/** Stable id of the signature that matched (safe to log; carries no content). */
|
|
4703
|
+
signature: string;
|
|
4704
|
+
}
|
|
4705
|
+
/** Result of screening a candidate batch. */
|
|
4706
|
+
interface ScreenResult {
|
|
4707
|
+
/** Candidates with no injection signature — persist normally. */
|
|
4708
|
+
clean: ExtractedCandidate[];
|
|
4709
|
+
/** Candidates that matched a signature — persist quarantined. */
|
|
4710
|
+
quarantined: ScreenedCandidate[];
|
|
4711
|
+
}
|
|
4738
4712
|
/**
|
|
4739
|
-
*
|
|
4740
|
-
*
|
|
4741
|
-
*
|
|
4742
|
-
*
|
|
4743
|
-
*
|
|
4713
|
+
* Screen extraction candidates for injection / poisoning signatures.
|
|
4714
|
+
*
|
|
4715
|
+
* Partitions the input into `clean` (persist normally) and `quarantined`
|
|
4716
|
+
* (persist with `trust_tier="quarantined"`, hidden from recall). Pure and
|
|
4717
|
+
* synchronous — no network, no DB, no content logging. Input order is
|
|
4718
|
+
* preserved within each partition.
|
|
4744
4719
|
*/
|
|
4745
|
-
declare
|
|
4720
|
+
declare function screenCandidatesForInjection(candidates: readonly ExtractedCandidate[]): ScreenResult;
|
|
4746
4721
|
/**
|
|
4747
|
-
*
|
|
4748
|
-
*
|
|
4749
|
-
*
|
|
4722
|
+
* Content-free catalog of the active signatures (id + reason, no patterns).
|
|
4723
|
+
* Exposed so a security review / audit surface can enumerate coverage
|
|
4724
|
+
* without reaching into module internals. Does not leak any user content.
|
|
4750
4725
|
*/
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}. Edited /
|
|
4757
|
-
* stale-version rows come first (they get priority under `limit`), each group
|
|
4758
|
-
* newest-created first.
|
|
4759
|
-
*/
|
|
4760
|
-
pending: StoredVaultMemory[];
|
|
4761
|
-
/**
|
|
4762
|
-
* IDs of rows that already have entity links but no watermark — legacy rows
|
|
4763
|
-
* extracted by the conversation pipeline before v36. Grandfather these with
|
|
4764
|
-
* {@link stampTopicsExtractedAtOp} (no LLM call) so a later content edit
|
|
4765
|
-
* makes them re-extractable instead of invisible forever. Bounded by the
|
|
4766
|
-
* same `limit` as {@link pending} — stamping loads a Model per row, so the
|
|
4767
|
-
* grandfather backlog is drained across sweeps rather than in one spike.
|
|
4768
|
-
*/
|
|
4769
|
-
linkedUnstamped: string[];
|
|
4770
|
-
}
|
|
4771
|
-
/**
|
|
4772
|
-
* Sweep query for the background topic-extraction worker: partition the
|
|
4773
|
-
* user's non-deleted, non-user-managed memories by what the worker should do
|
|
4774
|
-
* with them (see {@link MemoriesNeedingTopicExtraction}). User-managed rows
|
|
4775
|
-
* are never returned — the user owns their topics, including an intentionally
|
|
4776
|
-
* empty set. Requires `ctx.entityCtx` for the entity-links check.
|
|
4777
|
-
*/
|
|
4778
|
-
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
4779
|
-
limit?: number;
|
|
4780
|
-
}): Promise<MemoriesNeedingTopicExtraction>;
|
|
4781
|
-
/**
|
|
4782
|
-
* Stamp `topics_extracted_at` (and `topics_extracted_version`) on the given
|
|
4783
|
-
* memories — the topic worker calls this after a successful extraction pass
|
|
4784
|
-
* (including zero-entity results, so quiet memories aren't re-asked every sweep)
|
|
4785
|
-
* and to grandfather `linkedUnstamped` rows without an LLM call. `version`
|
|
4786
|
-
* defaults to {@link TOPICS_EXTRACTION_VERSION}: stamping at the current version
|
|
4787
|
-
* (for both fresh extractions and grandfathered legacy rows) means they aren't
|
|
4788
|
-
* re-extracted until a future version bump. Preserves `updated_at` so a stamp
|
|
4789
|
-
* never inflates the recency multiplier — and never masks a concurrent content
|
|
4790
|
-
* edit from the next sweep. Skips deleted, foreign-user, and user-managed rows.
|
|
4791
|
-
* Returns the IDs actually stamped.
|
|
4792
|
-
*
|
|
4793
|
-
* ALL eligibility AND `updated_at` are read from the LIVE Model inside the
|
|
4794
|
-
* serialized writer — never a pre-writer snapshot. Writers are serialized, so
|
|
4795
|
-
* a content edit or topic-override that commits before this writer runs is
|
|
4796
|
-
* observed here: its fresh `updated_at` is preserved (so the next sweep's
|
|
4797
|
-
* `updated_at > stamp` check still fires) and a mid-pass user-managed flip
|
|
4798
|
-
* skips the row. Reading `updated_at` from a raw pre-fetch instead would write
|
|
4799
|
-
* a stale value back, pushing `updated_at < topics_extracted_at` and hiding
|
|
4800
|
-
* the edited memory from every future sweep.
|
|
4801
|
-
*
|
|
4802
|
-
* Callers bound the input via `getMemoriesNeedingTopicExtractionOp`'s `limit`
|
|
4803
|
-
* (both `pending` and `linkedUnstamped` are capped), so the per-row Model load
|
|
4804
|
-
* needed to `prepareUpdate` stays bounded and never spikes the RecordCache.
|
|
4805
|
-
*/
|
|
4806
|
-
declare function stampTopicsExtractedAtOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[], extractedAt: number, version?: number): Promise<string[]>;
|
|
4807
|
-
declare function deleteAllVaultMemoriesForUserOp(ctx: VaultMemoryOperationsContext, userId: string): Promise<number>;
|
|
4808
|
-
declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext, id: string, embedding: string, embeddingModel: string): Promise<boolean>;
|
|
4809
|
-
|
|
4810
|
-
/**
|
|
4811
|
-
* Queue Types
|
|
4812
|
-
*
|
|
4813
|
-
* Types for the in-memory write queue that holds operations
|
|
4814
|
-
* when encryption keys aren't yet available.
|
|
4815
|
-
*/
|
|
4816
|
-
|
|
4817
|
-
/**
|
|
4818
|
-
* Types of database operations that can be queued.
|
|
4819
|
-
*/
|
|
4820
|
-
type QueuedOperationType = "createConversation" | "updateConversationTitle" | "updateConversationPinned" | "createMessage" | "updateMessage" | "createMedia" | "createMediaBatch" | "updateMediaMessageId";
|
|
4821
|
-
/**
|
|
4822
|
-
* A single queued database operation.
|
|
4823
|
-
*/
|
|
4824
|
-
interface QueuedOperation {
|
|
4825
|
-
/** Unique ID for this operation */
|
|
4826
|
-
id: string;
|
|
4827
|
-
/** Type of operation */
|
|
4828
|
-
type: QueuedOperationType;
|
|
4829
|
-
/** Wallet address this operation belongs to */
|
|
4830
|
-
walletAddress: string;
|
|
4831
|
-
/** When the operation was queued */
|
|
4832
|
-
timestamp: number;
|
|
4833
|
-
/** Priority for ordering (lower = higher priority). Conversations=0, Messages=1, Media=2 */
|
|
4834
|
-
priority: number;
|
|
4835
|
-
/** IDs of operations that must complete before this one */
|
|
4836
|
-
dependencies: string[];
|
|
4837
|
-
/** Operation-specific payload */
|
|
4838
|
-
payload: Record<string, any>;
|
|
4839
|
-
/** Number of times this operation has been retried */
|
|
4840
|
-
retryCount: number;
|
|
4841
|
-
/** Maximum number of retries allowed */
|
|
4842
|
-
maxRetries: number;
|
|
4843
|
-
}
|
|
4844
|
-
/**
|
|
4845
|
-
* Status of a wallet's queue.
|
|
4846
|
-
*/
|
|
4847
|
-
interface QueueStatus {
|
|
4848
|
-
/** Number of pending operations */
|
|
4849
|
-
pending: number;
|
|
4850
|
-
/** Number of operations that failed all retries */
|
|
4851
|
-
failed: number;
|
|
4852
|
-
/** Whether the queue is currently being flushed */
|
|
4853
|
-
isFlushing: boolean;
|
|
4854
|
-
/** Whether the queue is paused (e.g., wallet disconnected) */
|
|
4855
|
-
isPaused: boolean;
|
|
4856
|
-
}
|
|
4857
|
-
/**
|
|
4858
|
-
* Result of a flush operation.
|
|
4859
|
-
*/
|
|
4860
|
-
interface FlushResult {
|
|
4861
|
-
/** IDs of operations that succeeded */
|
|
4862
|
-
succeeded: string[];
|
|
4863
|
-
/** Operations that failed with their errors */
|
|
4864
|
-
failed: Array<{
|
|
4865
|
-
id: string;
|
|
4866
|
-
error: string;
|
|
4867
|
-
}>;
|
|
4868
|
-
/** Total number of operations attempted */
|
|
4869
|
-
total: number;
|
|
4870
|
-
}
|
|
4871
|
-
/**
|
|
4872
|
-
* Encryption context needed to execute queued operations.
|
|
4873
|
-
*/
|
|
4874
|
-
interface QueueEncryptionContext {
|
|
4875
|
-
walletAddress: string;
|
|
4876
|
-
signMessage: SignMessageFn;
|
|
4877
|
-
embeddedWalletSigner?: EmbeddedWalletSignerFn;
|
|
4878
|
-
}
|
|
4879
|
-
/**
|
|
4880
|
-
* Executor function that runs a single queued operation.
|
|
4881
|
-
* Provided by the consumer (e.g., useChatStorage) during flush.
|
|
4882
|
-
*/
|
|
4883
|
-
type OperationExecutor = (operation: QueuedOperation, encryptionContext: QueueEncryptionContext) => Promise<void>;
|
|
4884
|
-
|
|
4885
|
-
/**
|
|
4886
|
-
* Queue Manager
|
|
4887
|
-
*
|
|
4888
|
-
* In-memory queue for database operations when encryption keys aren't yet available.
|
|
4889
|
-
* Operations are held in memory and flushed to the database once the key becomes available.
|
|
4890
|
-
*
|
|
4891
|
-
* Key design decisions:
|
|
4892
|
-
* - In-memory only: queue is lost on page refresh (acceptable since user must re-auth anyway)
|
|
4893
|
-
* - Per-wallet isolation: each wallet has its own queue
|
|
4894
|
-
* - Dependency tracking: operations are flushed in correct order (conversation before messages)
|
|
4895
|
-
* - Max 1000 operations per wallet to prevent memory leaks
|
|
4896
|
-
*/
|
|
4897
|
-
|
|
4898
|
-
declare class QueueManager {
|
|
4899
|
-
/** Per-wallet operation queues: wallet address -> operation id -> operation */
|
|
4900
|
-
private queues;
|
|
4901
|
-
/** Per-wallet flushing state */
|
|
4902
|
-
private flushing;
|
|
4903
|
-
/** Per-wallet paused state */
|
|
4904
|
-
private paused;
|
|
4905
|
-
/** Per-wallet failed operations (exceeded max retries) */
|
|
4906
|
-
private failedOps;
|
|
4907
|
-
/** Change listeners: wallet address -> callbacks */
|
|
4908
|
-
private listeners;
|
|
4909
|
-
/**
|
|
4910
|
-
* Queue a new operation for a wallet.
|
|
4911
|
-
* @returns The operation ID, or null if queue is full.
|
|
4912
|
-
*/
|
|
4913
|
-
queueOperation(walletAddress: string, type: QueuedOperationType, payload: Record<string, any>, dependencies?: string[], maxRetries?: number): string | null;
|
|
4914
|
-
/**
|
|
4915
|
-
* Get all pending operations for a wallet, sorted by dependency order.
|
|
4916
|
-
*/
|
|
4917
|
-
getOperations(walletAddress: string): QueuedOperation[];
|
|
4918
|
-
/**
|
|
4919
|
-
* Remove a specific operation from the queue.
|
|
4920
|
-
*/
|
|
4921
|
-
removeOperation(walletAddress: string, operationId: string): void;
|
|
4922
|
-
/**
|
|
4923
|
-
* Get the status of a wallet's queue.
|
|
4924
|
-
*/
|
|
4925
|
-
getStatus(walletAddress: string): QueueStatus;
|
|
4926
|
-
/**
|
|
4927
|
-
* Flush all queued operations for a wallet by executing them with encryption.
|
|
4928
|
-
*
|
|
4929
|
-
* @param encryptionContext - Wallet address and signing functions for encryption
|
|
4930
|
-
* @param executor - Function that executes each operation against the database
|
|
4931
|
-
* @returns Result with succeeded/failed operation IDs
|
|
4932
|
-
*/
|
|
4933
|
-
flush(encryptionContext: QueueEncryptionContext, executor: OperationExecutor): Promise<FlushResult>;
|
|
4934
|
-
/**
|
|
4935
|
-
* Clear all queued operations for a wallet.
|
|
4936
|
-
*/
|
|
4937
|
-
clear(walletAddress: string): void;
|
|
4938
|
-
/**
|
|
4939
|
-
* Pause the queue for a wallet (stops flush mid-way).
|
|
4940
|
-
*/
|
|
4941
|
-
pause(walletAddress: string): void;
|
|
4942
|
-
/**
|
|
4943
|
-
* Resume the queue for a wallet.
|
|
4944
|
-
*/
|
|
4945
|
-
resume(walletAddress: string): void;
|
|
4946
|
-
/**
|
|
4947
|
-
* Register a listener for queue changes on a wallet.
|
|
4948
|
-
* @returns Unsubscribe function
|
|
4949
|
-
*/
|
|
4950
|
-
onQueueChange(walletAddress: string, callback: () => void): () => void;
|
|
4951
|
-
/**
|
|
4952
|
-
* Check if a wallet has any pending operations.
|
|
4953
|
-
*/
|
|
4954
|
-
hasPending(walletAddress: string): boolean;
|
|
4955
|
-
/** Move a failed operation to the failed set. */
|
|
4956
|
-
private moveToFailed;
|
|
4957
|
-
/** Notify all listeners for a wallet. */
|
|
4958
|
-
private notifyListeners;
|
|
4959
|
-
}
|
|
4960
|
-
/** Singleton queue manager instance */
|
|
4961
|
-
declare const queueManager: QueueManager;
|
|
4962
|
-
|
|
4963
|
-
/**
|
|
4964
|
-
* Wallet Poller
|
|
4965
|
-
*
|
|
4966
|
-
* Polls for embedded wallet availability during Privy initialization.
|
|
4967
|
-
* When a wallet becomes available, triggers a callback so the queue can flush.
|
|
4968
|
-
*/
|
|
4969
|
-
declare class WalletPoller {
|
|
4970
|
-
private timerId;
|
|
4971
|
-
private attempts;
|
|
4972
|
-
/**
|
|
4973
|
-
* Start polling for wallet availability.
|
|
4974
|
-
*
|
|
4975
|
-
* @param checkWallet - Returns wallet address when ready, null if not yet available
|
|
4976
|
-
* @param onWalletReady - Called with the wallet address when it becomes available
|
|
4977
|
-
* @param intervalMs - Polling interval in milliseconds (default: 1000ms)
|
|
4978
|
-
* @param maxAttempts - Maximum polling attempts before giving up (default: 60)
|
|
4979
|
-
* @returns Stop function to cancel polling
|
|
4980
|
-
*/
|
|
4981
|
-
startPolling(checkWallet: () => Promise<string | null>, onWalletReady: (address: string) => void, intervalMs?: number, maxAttempts?: number): () => void;
|
|
4982
|
-
/**
|
|
4983
|
-
* Stop polling.
|
|
4984
|
-
*/
|
|
4985
|
-
stop(): void;
|
|
4986
|
-
}
|
|
4987
|
-
|
|
4988
|
-
/**
|
|
4989
|
-
* Auth for portal LLM calls (extraction, consolidation, decomposition,
|
|
4990
|
-
* reflection). Mirrors `memoryEngine`'s `EmbeddingOptions` dual-auth:
|
|
4991
|
-
*
|
|
4992
|
-
* - `apiKey`: For direct API keys (uses x-api-key header)
|
|
4993
|
-
* - `getToken`: For Privy identity tokens (uses Authorization: Bearer header)
|
|
4994
|
-
*
|
|
4995
|
-
* At least one of `apiKey` or `getToken` must be provided (enforced at
|
|
4996
|
-
* runtime); `apiKey` takes precedence when both are set.
|
|
4997
|
-
*
|
|
4998
|
-
* @public
|
|
4999
|
-
*/
|
|
5000
|
-
interface PortalLlmAuth {
|
|
5001
|
-
/** Direct API key — sent as `x-api-key` (server-side / CLI usage). Wins when both are provided. */
|
|
5002
|
-
apiKey?: string;
|
|
5003
|
-
/** Function to get an auth token (e.g., Privy's getIdentityToken). Token is sent as `Authorization: Bearer`. */
|
|
5004
|
-
getToken?: () => Promise<string | null>;
|
|
5005
|
-
}
|
|
5006
|
-
|
|
4726
|
+
declare function injectionSignatureCatalog(): {
|
|
4727
|
+
id: string;
|
|
4728
|
+
reason: InjectionReason;
|
|
4729
|
+
}[];
|
|
4730
|
+
|
|
5007
4731
|
/**
|
|
5008
4732
|
* Linear recency decay multiplier — applied post-fusion to nudge fresh
|
|
5009
4733
|
* memories above stale ones with otherwise comparable scores.
|
|
@@ -5057,6 +4781,23 @@ interface MemoryVaultSearchOptions {
|
|
|
5057
4781
|
scopes?: string[];
|
|
5058
4782
|
/** When provided, only search memories in this folder (null for unfiled) */
|
|
5059
4783
|
folderId?: string | null;
|
|
4784
|
+
/** Typed memory (PR1) — when provided, only search memories of these fact
|
|
4785
|
+
* types. Applied at load time via `Q.oneOf` on the indexed `fact_type`
|
|
4786
|
+
* column. Omit for no type filter. */
|
|
4787
|
+
factTypes?: string[];
|
|
4788
|
+
/**
|
|
4789
|
+
* PR5 — optional per-FactType score multiplier applied in the boost stage
|
|
4790
|
+
* (e.g. `{ identity: 1.2, ongoing_context: 0.8 }`). Empty/omitted = uniform
|
|
4791
|
+
* (no behavior change). See {@link rankFusedVaultMemories}.
|
|
4792
|
+
*/
|
|
4793
|
+
factTypeWeights?: Record<string, number>;
|
|
4794
|
+
/**
|
|
4795
|
+
* PR5 — include archived (decayed) rows in the candidate load. Default false
|
|
4796
|
+
* (the baseVaultConditions choke point excludes them). retain()'s dedup
|
|
4797
|
+
* search sets this so a re-observed fact can merge into — and un-archive — an
|
|
4798
|
+
* archived row instead of creating a fresh duplicate.
|
|
4799
|
+
*/
|
|
4800
|
+
includeArchived?: boolean;
|
|
5060
4801
|
/**
|
|
5061
4802
|
* Use the hybrid fusion ranker (cosine + BM25 + RRF + recency) instead of
|
|
5062
4803
|
* cosine-only. Default true — new W1 pipeline. Pass false to fall back
|
|
@@ -5151,6 +4892,10 @@ interface VaultSearchResult {
|
|
|
5151
4892
|
eventTimeStart?: number | null;
|
|
5152
4893
|
eventTimeEnd?: number | null;
|
|
5153
4894
|
eventTimeKind?: "point" | "range" | "ongoing" | null;
|
|
4895
|
+
/** Typed memory (PR1) — the fact's FactType, threaded through from the
|
|
4896
|
+
* storage row alongside the event-time anchors. Null/undefined when
|
|
4897
|
+
* untyped. Loose string (originates from a stored column). */
|
|
4898
|
+
factType?: string | null;
|
|
5154
4899
|
/** Message ids this fact was extracted from (provenance). recall() uses
|
|
5155
4900
|
* these to suppress the originating chunk in the chunk lane. */
|
|
5156
4901
|
sourceChunkIds?: string[] | null;
|
|
@@ -5240,6 +4985,14 @@ interface RankedMemory {
|
|
|
5240
4985
|
eventTimeStart?: number | null;
|
|
5241
4986
|
eventTimeEnd?: number | null;
|
|
5242
4987
|
eventTimeKind?: "point" | "range" | "ongoing" | null;
|
|
4988
|
+
/**
|
|
4989
|
+
* Typed memory (PR1) — the extractor's FactType classification for this
|
|
4990
|
+
* fact, threaded through the same channel as the event-time anchors so
|
|
4991
|
+
* recall results (and UI) can show the type without a second DB read.
|
|
4992
|
+
* Null/undefined on legacy/untyped/manual facts. Kept as a loose string
|
|
4993
|
+
* (not narrowed to FactType) since it originates from a stored column.
|
|
4994
|
+
*/
|
|
4995
|
+
factType?: string | null;
|
|
5243
4996
|
conversationId?: string;
|
|
5244
4997
|
messageId?: string;
|
|
5245
4998
|
role?: "user" | "assistant";
|
|
@@ -5261,6 +5014,18 @@ interface RecallOptions {
|
|
|
5261
5014
|
scopes?: string[];
|
|
5262
5015
|
/** Vault folder filter. Vault-only. */
|
|
5263
5016
|
folderId?: string | null;
|
|
5017
|
+
/**
|
|
5018
|
+
* Typed memory (PR1) — restrict fact recall to these FactTypes. Optional
|
|
5019
|
+
* and no-op when unset (all types are eligible). Vault-only.
|
|
5020
|
+
*/
|
|
5021
|
+
factTypes?: FactType[];
|
|
5022
|
+
/**
|
|
5023
|
+
* PR5 — optional per-FactType score multiplier applied in the fusion boost
|
|
5024
|
+
* stage (e.g. boost `identity`/`constraint`, down-weight `ongoing_context`).
|
|
5025
|
+
* A type absent from the map (and untyped rows) uses 1.0, so an empty/omitted
|
|
5026
|
+
* map is a no-op (uniform weighting). Vault-only.
|
|
5027
|
+
*/
|
|
5028
|
+
factTypeWeights?: Partial<Record<FactType, number>>;
|
|
5264
5029
|
/** Restrict chunk search to one conversation. Chunk-only. */
|
|
5265
5030
|
conversationId?: string;
|
|
5266
5031
|
/** Exclude one conversation from chunk search. Chunk-only. */
|
|
@@ -5313,416 +5078,1063 @@ interface RecallOptions {
|
|
|
5313
5078
|
bm25AdmissionDivisor?: number;
|
|
5314
5079
|
/** RRF smoothing constant for lane fusion (facts × chunks and side lanes). Default: 60. */
|
|
5315
5080
|
rrfK?: number;
|
|
5081
|
+
/** Total graph hops incl. the seed lookup (hop 1). Default: 1 (seed only). */
|
|
5082
|
+
maxHops?: number;
|
|
5083
|
+
/** Max neighbor entities expanded per hop. Default: 8. */
|
|
5084
|
+
entityFanout?: number;
|
|
5085
|
+
/** Hard cap on accumulated memory IDs across all hops. Default: 64. */
|
|
5086
|
+
nodeBudget?: number;
|
|
5087
|
+
/**
|
|
5088
|
+
* PR5 — enable LLM graph path-refinement: at each traversal hop a model picks
|
|
5089
|
+
* which neighbor entities to expand instead of pure co-occurrence ranking.
|
|
5090
|
+
* Opt-in (default false); only active on the `high` budget (needs the
|
|
5091
|
+
* `traverse` flag) AND when `decomposeOptions` is set (reuses that auth).
|
|
5092
|
+
* Falls back to deterministic co-occurrence order on any error. Adds ≤1 LLM
|
|
5093
|
+
* call per expansion hop.
|
|
5094
|
+
*/
|
|
5095
|
+
graphRefine?: boolean;
|
|
5096
|
+
}
|
|
5097
|
+
interface RecallContext {
|
|
5098
|
+
/** Required when `types` includes 'fact'. */
|
|
5099
|
+
vaultCtx?: VaultMemoryOperationsContext;
|
|
5100
|
+
/** Required when `types` includes 'chunk'. */
|
|
5101
|
+
storageCtx?: StorageOperationsContext;
|
|
5102
|
+
/** Embedding API options. */
|
|
5103
|
+
embeddingOptions: EmbeddingOptions;
|
|
5104
|
+
/** Vault embedding LRU cache. */
|
|
5105
|
+
vaultCache?: VaultEmbeddingCache;
|
|
5106
|
+
/**
|
|
5107
|
+
* Optional chunk-vector LRU cache. When provided, the chunk lane skips the
|
|
5108
|
+
* per-query decrypt + JSON.parse of every message's chunk vectors on warm
|
|
5109
|
+
* entries. Build via `createChunkVectorCache`. Omit for legacy behavior.
|
|
5110
|
+
*/
|
|
5111
|
+
chunkCache?: ChunkVectorCache;
|
|
5112
|
+
/**
|
|
5113
|
+
* Optional — when provided, recall extracts entities from the query
|
|
5114
|
+
* and adds a graph lane to the RRF fusion (memories sharing entities
|
|
5115
|
+
* with the query rank higher). Build via `entityCollection` +
|
|
5116
|
+
* `memoryEntityCollection` from your DatabaseManager.
|
|
5117
|
+
*/
|
|
5118
|
+
entityCtx?: EntityOperationsContext;
|
|
5119
|
+
}
|
|
5120
|
+
interface RecallResult {
|
|
5121
|
+
memories: RankedMemory[];
|
|
5122
|
+
/** Diagnostic: budget actually used (may downgrade if reranker fails). */
|
|
5123
|
+
usedBudget: Budget;
|
|
5124
|
+
/** Diagnostic: was the reranker invoked? */
|
|
5125
|
+
reranked: boolean;
|
|
5126
|
+
/** Diagnostic: total candidates considered before truncation. */
|
|
5127
|
+
candidateCount: number;
|
|
5128
|
+
/** Diagnostic: total memories in the vault when fact lane was queried. */
|
|
5129
|
+
vaultSize?: number;
|
|
5130
|
+
}
|
|
5131
|
+
/** Soft-degradation signals surfaced via {@link RecallDiagnostics.degraded}. */
|
|
5132
|
+
type RecallDegradation =
|
|
5133
|
+
/** Rerank was requested (budget mid/high) but the cross-encoder didn't run
|
|
5134
|
+
* this call — unavailable (e.g. React Native) or a transient failure. */
|
|
5135
|
+
"rerank-unavailable"
|
|
5136
|
+
/** `budget: 'high'` requested but no `decomposeOptions`, so query
|
|
5137
|
+
* decomposition was skipped and the budget downgraded to mid. */
|
|
5138
|
+
| "decompose-unavailable";
|
|
5139
|
+
/**
|
|
5140
|
+
* Per-call recall observability payload (see {@link RecallOptions.onDiagnostics}).
|
|
5141
|
+
* All timings are wall-clock milliseconds. Lane counts are post-dedupe,
|
|
5142
|
+
* pre-fusion. Intended to be forwarded to a metrics sink (e.g. PostHog).
|
|
5143
|
+
*/
|
|
5144
|
+
interface RecallDiagnostics {
|
|
5145
|
+
/** Budget actually executed (may have downgraded from the requested one). */
|
|
5146
|
+
usedBudget: Budget;
|
|
5147
|
+
/** Whether the cross-encoder actually reranked the fact lane this call. */
|
|
5148
|
+
reranked: boolean;
|
|
5149
|
+
/** Total candidates considered before truncation. */
|
|
5150
|
+
candidateCount: number;
|
|
5151
|
+
/** Total vault size when the fact lane ran (absent if it didn't). */
|
|
5152
|
+
vaultSize?: number;
|
|
5153
|
+
/** Facts the fact lane returned (post-dedupe, pre-fusion). */
|
|
5154
|
+
factCount: number;
|
|
5155
|
+
/** Chunks the chunk lane returned (post-dedupe, pre-fusion). */
|
|
5156
|
+
chunkCount: number;
|
|
5157
|
+
/** Wall-clock phase timings (ms). */
|
|
5158
|
+
timings: {
|
|
5159
|
+
/** Whole `recall()` call. */
|
|
5160
|
+
total: number;
|
|
5161
|
+
/** Parallel query-embed + graph/temporal side-lane build. */
|
|
5162
|
+
prep: number;
|
|
5163
|
+
/** Vault fact-lane search (`searchVaultMemoriesWithSize`). */
|
|
5164
|
+
factLane: number;
|
|
5165
|
+
/** Chunk-lane search (`searchChunksOp`). */
|
|
5166
|
+
chunkLane: number;
|
|
5167
|
+
/** Cross-lane RRF fusion + provenance dedup after both lanes. */
|
|
5168
|
+
fuse: number;
|
|
5169
|
+
};
|
|
5170
|
+
/** Soft-degradation signals that fired this call (empty when clean). */
|
|
5171
|
+
degraded: RecallDegradation[];
|
|
5172
|
+
}
|
|
5173
|
+
type RetainAction = "create" | "merge" | "update" | "skip" | "suppressed" | "supersede";
|
|
5174
|
+
type RetainSource = "manual" | "auto-extracted" | "capsule";
|
|
5175
|
+
/** Why the consolidator fell back to "create" instead of a real decision. */
|
|
5176
|
+
type ConsolidationFallbackReason = "llm_error" | "invalid_response";
|
|
5177
|
+
interface RetainOptions {
|
|
5178
|
+
source?: RetainSource;
|
|
5179
|
+
sourceChunkIds?: string[];
|
|
5180
|
+
scope?: string;
|
|
5181
|
+
folderId?: string | null;
|
|
5182
|
+
/** When provided, applies merge-on-write logic instead of plain insert. */
|
|
5183
|
+
enableAutoMerge?: boolean;
|
|
5184
|
+
/**
|
|
5185
|
+
* When true, a would-be create is suppressed if it matches a soft-deleted
|
|
5186
|
+
* ("tombstoned") memory above the auto-merge threshold — so auto-extraction
|
|
5187
|
+
* can't silently resurrect a fact the user deleted. Off by default so manual
|
|
5188
|
+
* and other `retain()` callers are unaffected; auto-extraction opts in.
|
|
5189
|
+
* Returns `action: 'suppressed'` with the matched `tombstoneId`.
|
|
5190
|
+
*/
|
|
5191
|
+
respectTombstones?: boolean;
|
|
5192
|
+
/** Cosine similarity threshold for auto-merge. Default: 0.85. */
|
|
5193
|
+
autoMergeThreshold?: number;
|
|
5194
|
+
/**
|
|
5195
|
+
* When provided, runs an LLM-based consolidation pass against the top-K
|
|
5196
|
+
* existing memories above `consolidateThreshold` (looser than auto-merge).
|
|
5197
|
+
* The LLM emits create/update/noop per Hindsight's facet-dedup rules.
|
|
5198
|
+
* Auth/endpoint required — one of `apiKey` / `getToken` (see
|
|
5199
|
+
* {@link PortalLlmAuth}); without this option we keep the cosine-only path.
|
|
5200
|
+
*/
|
|
5201
|
+
consolidateOptions?: PortalLlmAuth & {
|
|
5202
|
+
baseUrl?: string;
|
|
5203
|
+
model?: string;
|
|
5204
|
+
/**
|
|
5205
|
+
* Invoked when the consolidator degrades to its "create" fallback
|
|
5206
|
+
* instead of returning a real decision — `llm_error` for network /
|
|
5207
|
+
* timeout / unparseable output, `invalid_response` for well-formed
|
|
5208
|
+
* JSON that violates the schema (unknown action, bad targetId).
|
|
5209
|
+
* A flaky consolidator silently accumulates duplicate memories;
|
|
5210
|
+
* wire this to logging/metrics so the fallback rate is observable.
|
|
5211
|
+
*/
|
|
5212
|
+
onFallback?: (reason: ConsolidationFallbackReason) => void;
|
|
5213
|
+
/**
|
|
5214
|
+
* When set, the new fact and existing candidates are PII-redacted before
|
|
5215
|
+
* the consolidation model sees them and the result is de-anonymized before
|
|
5216
|
+
* persistence. Auto-extraction inherits this from its `extract` options.
|
|
5217
|
+
*/
|
|
5218
|
+
piiRedaction?: boolean | PiiRedactor;
|
|
5219
|
+
};
|
|
5220
|
+
/** Cosine similarity floor for the consolidator candidate set. Default: 0.65. */
|
|
5221
|
+
consolidateThreshold?: number;
|
|
5222
|
+
/** Top-K consolidation candidates to feed the LLM. Default: 5. */
|
|
5223
|
+
consolidateTopK?: number;
|
|
5224
|
+
/**
|
|
5225
|
+
* W6 temporal lane — when the event in this fact occurred. Persisted to
|
|
5226
|
+
* memory_vault.event_time_* columns; recall's temporal lane filters
|
|
5227
|
+
* and boosts memories whose event-time overlaps the query window.
|
|
5228
|
+
* Auto-extraction emits this; manual writes can omit it.
|
|
5229
|
+
*/
|
|
5230
|
+
eventTime?: {
|
|
5231
|
+
kind: "point" | "range" | "ongoing";
|
|
5232
|
+
start: number;
|
|
5233
|
+
end: number | null;
|
|
5234
|
+
} | null;
|
|
5235
|
+
/**
|
|
5236
|
+
* Typed memory (PR1) — the extractor's classification for this fact.
|
|
5237
|
+
* Persisted on create; on merge/consolidate it lazily backfills the target
|
|
5238
|
+
* only when the target has no type yet (never overwrites a non-null type).
|
|
5239
|
+
* Auto-extraction emits this; manual writes omit it (persisted as null).
|
|
5240
|
+
*/
|
|
5241
|
+
factType?: FactType;
|
|
5242
|
+
/**
|
|
5243
|
+
* Tier-0 security (PR3) — trust tier for this fact. The write-time
|
|
5244
|
+
* injection screen threads `"quarantined"` here for flagged candidates;
|
|
5245
|
+
* omit for the default (null/trusted). Persisted only on create (a
|
|
5246
|
+
* quarantined candidate is force-created, never merged, so it can't bump
|
|
5247
|
+
* or contaminate a clean memory). The DB op re-validates the value
|
|
5248
|
+
* against the known set before writing.
|
|
5249
|
+
*/
|
|
5250
|
+
trustTier?: string;
|
|
5251
|
+
}
|
|
5252
|
+
interface RetainResult {
|
|
5253
|
+
action: RetainAction;
|
|
5254
|
+
memoryId: string;
|
|
5255
|
+
/** When action is 'merge' or 'update', the prior memory's id. When action is
|
|
5256
|
+
* 'supersede', the stale memory that was retired (`memoryId` is the new one). */
|
|
5257
|
+
targetId?: string;
|
|
5258
|
+
/**
|
|
5259
|
+
* When action is 'suppressed', the id of the soft-deleted memory that blocked
|
|
5260
|
+
* re-creation. `memoryId` is set to the same id (no new memory was written).
|
|
5261
|
+
*/
|
|
5262
|
+
tombstoneId?: string;
|
|
5263
|
+
/** Updated proof_count after this write. 0 when nothing was written (suppressed). */
|
|
5264
|
+
proofCount: number;
|
|
5265
|
+
}
|
|
5266
|
+
|
|
5267
|
+
/**
|
|
5268
|
+
* Retain API — single write surface for the unified memory layer with
|
|
5269
|
+
* write-time auto-merge.
|
|
5270
|
+
*
|
|
5271
|
+
* On write, retain() searches for the nearest existing memory and, if its
|
|
5272
|
+
* cosine similarity to the new content exceeds the auto-merge threshold,
|
|
5273
|
+
* folds the new fact into the existing record (incrementing proof_count
|
|
5274
|
+
* and unioning source_chunk_ids). Otherwise it inserts a new memory.
|
|
5275
|
+
*
|
|
5276
|
+
* This is the W4 (auto-merge / dedup) workstream made callable. The W2
|
|
5277
|
+
* auto-extraction worker uses retain() as its write step, with the LLM
|
|
5278
|
+
* resolver upstream deciding when auto-merge should be enabled vs when
|
|
5279
|
+
* a semantic update should be applied via the lower-level update op.
|
|
5280
|
+
*/
|
|
5281
|
+
|
|
5282
|
+
interface RetainContext {
|
|
5283
|
+
vaultCtx: VaultMemoryOperationsContext;
|
|
5284
|
+
embeddingOptions: EmbeddingOptions;
|
|
5285
|
+
vaultCache: VaultEmbeddingCache;
|
|
5286
|
+
}
|
|
5287
|
+
/**
|
|
5288
|
+
* Persist a memory, merging into the nearest existing record if its
|
|
5289
|
+
* cosine similarity exceeds the auto-merge threshold.
|
|
5290
|
+
*
|
|
5291
|
+
* Default behavior (autoMerge ON): proof_count increments on the merged
|
|
5292
|
+
* target, source_chunk_ids accumulate (union), content is left untouched.
|
|
5293
|
+
* The caller doesn't see a duplicate and the original memory's UI badge
|
|
5294
|
+
* shows it has been re-observed.
|
|
5295
|
+
*
|
|
5296
|
+
* Pass `enableAutoMerge: false` for a force-create (W2 resolver path
|
|
5297
|
+
* after it has explicitly decided "create new").
|
|
5298
|
+
*/
|
|
5299
|
+
declare function retain(content: string, ctx: RetainContext, options?: RetainOptions): Promise<RetainResult>;
|
|
5300
|
+
|
|
5301
|
+
/**
|
|
5302
|
+
* Auto-extraction worker — W2.
|
|
5303
|
+
*
|
|
5304
|
+
* After each assistant turn, this pipeline reads the recent conversation,
|
|
5305
|
+
* asks an LLM to identify durable user facts, and writes them to the
|
|
5306
|
+
* vault via retain(). The user never needs to ask Anuma to remember
|
|
5307
|
+
* something — durable facts are captured automatically.
|
|
5308
|
+
*
|
|
5309
|
+
* Two-stage pipeline (extract → retain). The retain step uses
|
|
5310
|
+
* write-time auto-merge (W4) so the same fact re-observed in a later
|
|
5311
|
+
* conversation increments proof_count instead of duplicating.
|
|
5312
|
+
*
|
|
5313
|
+
* Spec: see `tasks/hackathon/auto-extraction-prompt.md`.
|
|
5314
|
+
*/
|
|
5315
|
+
|
|
5316
|
+
declare const FACT_TYPES: readonly ["identity", "preference", "relationship", "plan", "ongoing_context", "constraint", "other"];
|
|
5317
|
+
type FactType = (typeof FACT_TYPES)[number];
|
|
5318
|
+
interface AutoExtractMessage {
|
|
5319
|
+
/** Stable message identifier — used for source_chunk_ids provenance. */
|
|
5320
|
+
id: string;
|
|
5321
|
+
role: "user" | "assistant";
|
|
5322
|
+
content: string;
|
|
5323
|
+
}
|
|
5324
|
+
/**
|
|
5325
|
+
* A named entity extracted from the conversation, with an optional
|
|
5326
|
+
* classification. `kind` is omitted when the model gave no kind or an
|
|
5327
|
+
* unrecognized one — see {@link validateCandidates}.
|
|
5328
|
+
*/
|
|
5329
|
+
interface ExtractedEntity {
|
|
5330
|
+
name: string;
|
|
5331
|
+
kind?: EntityKind;
|
|
5332
|
+
}
|
|
5333
|
+
interface ExtractedCandidate {
|
|
5334
|
+
content: string;
|
|
5335
|
+
type: FactType;
|
|
5336
|
+
confidence: number;
|
|
5337
|
+
sourceMessageIds: string[];
|
|
5338
|
+
entities: ExtractedEntity[];
|
|
5339
|
+
/** W6 temporal lane — when the event in this fact occurred. Resolved
|
|
5340
|
+
* to absolute timestamps by the LLM; null when the fact has no
|
|
5341
|
+
* temporal anchor. */
|
|
5342
|
+
eventTime: {
|
|
5343
|
+
kind: "point" | "range" | "ongoing";
|
|
5344
|
+
/** Unix ms timestamp of the event start (or point). */
|
|
5345
|
+
start: number;
|
|
5346
|
+
/** Unix ms timestamp of the event end. Only set when kind='range'. */
|
|
5347
|
+
end: number | null;
|
|
5348
|
+
} | null;
|
|
5349
|
+
}
|
|
5350
|
+
/**
|
|
5351
|
+
* Tier-0 security (PR3) — describes a candidate the injection screen
|
|
5352
|
+
* quarantined and persisted as an audit row. The client uses this to surface
|
|
5353
|
+
* a "held for review" state. `content` lives on `candidate` (same exposure as
|
|
5354
|
+
* {@link ExtractedCandidate}); never log it.
|
|
5355
|
+
*/
|
|
5356
|
+
interface QuarantinedMemoryInfo {
|
|
5357
|
+
candidate: ExtractedCandidate;
|
|
5358
|
+
/** The persisted (quarantined) memory row id. */
|
|
5359
|
+
memoryId: string;
|
|
5360
|
+
/** Coarse reason bucket from the screen. */
|
|
5361
|
+
reason: InjectionReason;
|
|
5362
|
+
/** Stable signature id that matched (safe to log; carries no content). */
|
|
5363
|
+
signature: string;
|
|
5364
|
+
}
|
|
5365
|
+
/**
|
|
5366
|
+
* Auth + endpoint for the extraction LLM call. Auth is the dual pattern —
|
|
5367
|
+
* one of `apiKey` / `getToken` is required at runtime; see
|
|
5368
|
+
* {@link PortalLlmAuth}.
|
|
5369
|
+
*/
|
|
5370
|
+
interface ExtractFactsOptions extends PortalLlmAuth {
|
|
5371
|
+
baseUrl?: string;
|
|
5372
|
+
model?: string;
|
|
5373
|
+
/** Override the global fetch implementation (useful for tests). */
|
|
5374
|
+
fetchFn?: typeof fetch;
|
|
5375
|
+
/**
|
|
5376
|
+
* The user's own name(s) / handle(s) (e.g. profile nickname, wallet display
|
|
5377
|
+
* name). Candidates whose entire content is just one of these are dropped —
|
|
5378
|
+
* a personal memory system already knows who the user is, so "Peter Lee" is
|
|
5379
|
+
* circular noise. Optional; when omitted only the bare-fragment gate applies.
|
|
5380
|
+
*/
|
|
5381
|
+
userIdentity?: string[];
|
|
5382
|
+
/**
|
|
5383
|
+
* Reference "now" (Unix ms) for resolving relative temporal phrases in the
|
|
5384
|
+
* transcript ("yesterday", "next week", "in two days") into the absolute
|
|
5385
|
+
* YYYY-MM-DD anchors the W6 temporal lane indexes on. The transcript itself
|
|
5386
|
+
* carries no timestamps, so without an anchor the model resolves relatives
|
|
5387
|
+
* against its own training-cutoff guess and emits wrong `eventTime` dates.
|
|
5388
|
+
* Defaults to `Date.now()`. Override for back-dated eval corpora and
|
|
5389
|
+
* deterministic tests (mirrors {@link RecallOptions.now}).
|
|
5390
|
+
*
|
|
5391
|
+
* Server-side timezone note: the ms value is formatted to a calendar date in
|
|
5392
|
+
* the process's local timezone (same basis as `parseLocalCalendarDay`). On a
|
|
5393
|
+
* UTC server, a user near midnight in a non-UTC offset can get the wrong
|
|
5394
|
+
* calendar day. Pass the user's local-midnight timestamp as `now` when the
|
|
5395
|
+
* process timezone doesn't match the user's.
|
|
5396
|
+
*/
|
|
5397
|
+
now?: number;
|
|
5398
|
+
/**
|
|
5399
|
+
* Max attempts for the extraction call on a transient failure (default 3).
|
|
5400
|
+
* Lower it to bound how long extraction can hold a turn open — e.g. a worker
|
|
5401
|
+
* that runs extraction behind an in-flight-turn guard can pass `2` to keep
|
|
5402
|
+
* repeated failures from delaying later turns.
|
|
5403
|
+
*/
|
|
5404
|
+
maxAttempts?: number;
|
|
5405
|
+
/** Per-attempt timeout (ms) for the extraction call. Defaults to the portal
|
|
5406
|
+
* helper's 60s. Combine with {@link maxAttempts} to cap the total time budget. */
|
|
5407
|
+
timeoutMs?: number;
|
|
5408
|
+
/**
|
|
5409
|
+
* Absolute wall-clock budget (ms) across ALL extraction attempts incl. backoff.
|
|
5410
|
+
* When set, the loop stops before an attempt that would exceed it, so worst-case
|
|
5411
|
+
* latency is bounded rather than `maxAttempts × timeoutMs`.
|
|
5412
|
+
*/
|
|
5413
|
+
totalTimeoutMs?: number;
|
|
5414
|
+
/**
|
|
5415
|
+
* Override the retry backoff (ms) for a given 1-based attempt index. The
|
|
5416
|
+
* extraction call retries transient failures internally (default exponential
|
|
5417
|
+
* backoff); pass `() => 0` to retry without delay (useful for tests).
|
|
5418
|
+
*/
|
|
5419
|
+
backoffMs?: (attempt: number) => number;
|
|
5420
|
+
/**
|
|
5421
|
+
* When set, PII (emails, phones, SSNs, cards, IPs, API keys, …) in the
|
|
5422
|
+
* conversation transcript is replaced with tagged placeholders before the
|
|
5423
|
+
* extraction call, and the returned facts + entities are de-anonymized so the
|
|
5424
|
+
* vault keeps the real values while raw PII never reaches the extraction
|
|
5425
|
+
* model (and, via `extractAndRetain`, the consolidation model). Pass `true`
|
|
5426
|
+
* for a fresh per-call redactor, or a shared {@link PiiRedactor} to keep
|
|
5427
|
+
* placeholder numbering consistent with other calls.
|
|
5428
|
+
*
|
|
5429
|
+
* NOTE: this does NOT cover the embeddings provider. Facts are stored and
|
|
5430
|
+
* embedded with their real values, so to keep PII out of embedding requests
|
|
5431
|
+
* set `RetainContext.embeddingOptions.maskInput` (e.g. `redactor.maskText`)
|
|
5432
|
+
* as well — the two are independent switches.
|
|
5433
|
+
*/
|
|
5434
|
+
piiRedaction?: boolean | PiiRedactor;
|
|
5435
|
+
/**
|
|
5436
|
+
* Called when the extraction LLM returned no usable result after exhausting
|
|
5437
|
+
* its retries (empty/malformed completion, network/HTTP error) — i.e. a
|
|
5438
|
+
* *failure* that drops the turn's facts, as opposed to a legitimate
|
|
5439
|
+
* `{candidates: []}` "nothing durable here". Lets callers distinguish a
|
|
5440
|
+
* silently-degrading extractor from quiet turns (the two are otherwise
|
|
5441
|
+
* indistinguishable). See {@link extractAndRetain}'s `outcome`.
|
|
5442
|
+
*/
|
|
5443
|
+
onExhaustedEmpty?: () => void;
|
|
5444
|
+
/**
|
|
5445
|
+
* Called when the extractor DID produce candidates but PII de-anonymization
|
|
5446
|
+
* dropped every one of them — the model mangled its placeholders (so they
|
|
5447
|
+
* can't be restored to real values) or restoring the values blew the length
|
|
5448
|
+
* cap. These drops happen before `retain()`, so `failedCount` can't see them,
|
|
5449
|
+
* and the turn would otherwise masquerade as a quiet `no-facts` result. Lets
|
|
5450
|
+
* H3's `outcome` surface `dropped-after-redaction` so a rising PII-drop rate
|
|
5451
|
+
* (i.e. redaction silently eating facts) is alarmable.
|
|
5452
|
+
*/
|
|
5453
|
+
onCandidatesDropped?: () => void;
|
|
5454
|
+
}
|
|
5455
|
+
/**
|
|
5456
|
+
* Stage 1 — call the LLM to extract candidate facts from the recent
|
|
5457
|
+
* conversation. Returns post-validated candidates only (confidence
|
|
5458
|
+
* threshold, source-id check, length cap, schema validation). Returns
|
|
5459
|
+
* an empty array if the LLM emits malformed JSON or no candidates.
|
|
5460
|
+
*
|
|
5461
|
+
* A null from `callPortalJsonCompletion` means a *failure* (empty completion,
|
|
5462
|
+
* malformed JSON, network/HTTP error) — distinct from a successful
|
|
5463
|
+
* `{candidates: []}`, which parses to a non-null object. A failed extraction
|
|
5464
|
+
* silently drops the whole turn's memories, so transient failures matter:
|
|
5465
|
+
* `callPortalJsonCompletion` retries them internally with backoff (default 3
|
|
5466
|
+
* attempts), so a one-off empty/malformed completion from a reasoning-class
|
|
5467
|
+
* model doesn't lose the turn. Only an exhausted-retry null reaches here.
|
|
5468
|
+
*/
|
|
5469
|
+
declare function extractFacts(messages: AutoExtractMessage[], options: ExtractFactsOptions): Promise<ExtractedCandidate[]>;
|
|
5470
|
+
/**
|
|
5471
|
+
* Outcome of the EXTRACTOR stage for a turn — independent of whether the
|
|
5472
|
+
* subsequent `retain()` writes landed (that's `failedCount`):
|
|
5473
|
+
* - `extracted` — the extractor produced at least one candidate above
|
|
5474
|
+
* the confidence floor. Whether those writes succeeded
|
|
5475
|
+
* is reported separately by `failedCount` — so
|
|
5476
|
+
* `extracted` + `failedCount > 0` means "found facts but
|
|
5477
|
+
* writes are failing", which is more signal than
|
|
5478
|
+
* collapsing it into `no-facts` would give.
|
|
5479
|
+
* - `no-facts` — the extractor ran fine but found nothing durable.
|
|
5480
|
+
* - `empty-after-retry` — the extractor returned empty/malformed after
|
|
5481
|
+
* exhausting retries (a *failure*). Distinguishing this
|
|
5482
|
+
* from `no-facts` is what makes a silently-degrading
|
|
5483
|
+
* extractor alarmable rather than invisible.
|
|
5484
|
+
* - `dropped-after-redaction` — the extractor found facts but PII
|
|
5485
|
+
* de-anonymization dropped every one (mangled
|
|
5486
|
+
* placeholders / over-cap after restore), before retain.
|
|
5487
|
+
* A degradation `failedCount` can't see; would otherwise
|
|
5488
|
+
* look like `no-facts`.
|
|
5489
|
+
*/
|
|
5490
|
+
type ExtractOutcome = "extracted" | "no-facts" | "empty-after-retry" | "dropped-after-redaction";
|
|
5491
|
+
/**
|
|
5492
|
+
* Stage 2 — for each extracted candidate, call retain() with auto-merge
|
|
5493
|
+
* enabled. When `consolidateOptions` are wired, retain()'s consolidation pass
|
|
5494
|
+
* is the "resolver": a second LLM call against the top-K existing vault
|
|
5495
|
+
* memories that decides create/update/noop/supersede — including retiring a
|
|
5496
|
+
* stale value on a state change (the supersession the extraction prompt above
|
|
5497
|
+
* promises). Without consolidation, retain() still dedups at the cosine-merge
|
|
5498
|
+
* level, and the read-time supersession heuristic remains the fallback.
|
|
5499
|
+
*
|
|
5500
|
+
* Returns the candidates that survived validation along with the retain
|
|
5501
|
+
* result for each (which captures whether the fact was created, merged,
|
|
5502
|
+
* or skipped), plus an `outcome` summarizing why the turn did/didn't produce
|
|
5503
|
+
* facts (see {@link ExtractOutcome}).
|
|
5504
|
+
*/
|
|
5505
|
+
declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: RetainContext, options: {
|
|
5506
|
+
extract: ExtractFactsOptions;
|
|
5507
|
+
minConfidence?: number;
|
|
5508
|
+
/** Override scope/folder for all retained facts. */
|
|
5509
|
+
scope?: string;
|
|
5510
|
+
folderId?: string | null;
|
|
5511
|
+
/**
|
|
5512
|
+
* Forwarded verbatim to each retain() call — enables the LLM-based
|
|
5513
|
+
* consolidation pass (Hindsight facet-dedup) on every write. See
|
|
5514
|
+
* `RetainOptions.consolidateOptions` for auth + observability fields.
|
|
5515
|
+
*/
|
|
5516
|
+
consolidateOptions?: RetainOptions["consolidateOptions"];
|
|
5517
|
+
/**
|
|
5518
|
+
* When provided, persist each candidate's `entities[]` to the
|
|
5519
|
+
* entity + memory_entity tables after a successful retain. This
|
|
5520
|
+
* powers the W5 graph retrieval lane — recall() can query for
|
|
5521
|
+
* memories sharing entities with the user's question.
|
|
5522
|
+
*/
|
|
5523
|
+
entityCtx?: EntityOperationsContext;
|
|
5524
|
+
/**
|
|
5525
|
+
* Per-candidate failure hook — invoked once per filtered candidate
|
|
5526
|
+
* whose `retain()` call threw. Lets UI layers surface "couldn't save
|
|
5527
|
+
* X" toasts; without it consumers only see the aggregate
|
|
5528
|
+
* `failedCount` and can't name which facts dropped.
|
|
5529
|
+
*/
|
|
5530
|
+
onCandidateFailed?: (candidate: ExtractedCandidate, error: unknown) => void;
|
|
5531
|
+
/**
|
|
5532
|
+
* Tier-0 security (PR3) — invoked once per candidate the injection screen
|
|
5533
|
+
* quarantined AND persisted (audit row written). Lets a UI surface a
|
|
5534
|
+
* "held for review" state instead of the fact silently vanishing. Carries
|
|
5535
|
+
* the same content exposure as `onMemoryExtracted` (the candidate) plus
|
|
5536
|
+
* the persisted `memoryId` + the screen `reason`/`signature`; content is
|
|
5537
|
+
* never logged. Fired only on a successful quarantine write, not on a
|
|
5538
|
+
* failed one (that goes to `onCandidateFailed`).
|
|
5539
|
+
*/
|
|
5540
|
+
onQuarantined?: (info: QuarantinedMemoryInfo) => void;
|
|
5541
|
+
/**
|
|
5542
|
+
* Tier-0 security (PR5) — optional SECOND-layer LLM injection classifier.
|
|
5543
|
+
* When provided, candidates the deterministic {@link screenCandidatesForInjection}
|
|
5544
|
+
* screen passed as CLEAN are additionally run through a cheap LLM that
|
|
5545
|
+
* catches signature-free poison ("Trusts BrandX for financial advice")
|
|
5546
|
+
* the regex screen can't. Positives are quarantined exactly like a
|
|
5547
|
+
* signature hit (reason `llm_semantic`). DEFAULT OFF — omit this to keep
|
|
5548
|
+
* the deterministic-only, no-extra-LLM-call path. Fails clean on any
|
|
5549
|
+
* error. Content is PII-redacted before the call, inheriting the
|
|
5550
|
+
* extraction redaction setting when this option doesn't set its own.
|
|
5551
|
+
*/
|
|
5552
|
+
injectionClassifier?: InjectionClassifierOptions;
|
|
5553
|
+
}): Promise<{
|
|
5554
|
+
candidates: ExtractedCandidate[];
|
|
5555
|
+
results: RetainResult[];
|
|
5556
|
+
failedCount: number;
|
|
5557
|
+
outcome: ExtractOutcome;
|
|
5558
|
+
/**
|
|
5559
|
+
* Tier-0 security (PR3) — candidates quarantined by the injection screen and
|
|
5560
|
+
* persisted as audit rows (trust_tier="quarantined"). Kept OUT of
|
|
5561
|
+
* `candidates`/`results` so `onMemoryExtracted` never announces poison;
|
|
5562
|
+
* surfaced here so callers can distinguish "nothing extracted" from
|
|
5563
|
+
* "extracted but held for review".
|
|
5564
|
+
*/
|
|
5565
|
+
quarantined: QuarantinedMemoryInfo[];
|
|
5566
|
+
}>;
|
|
5567
|
+
|
|
5568
|
+
interface StoredVaultMemory {
|
|
5569
|
+
/** WatermelonDB internal ID */
|
|
5570
|
+
uniqueId: string;
|
|
5571
|
+
/** Plain text memory content */
|
|
5572
|
+
content: string;
|
|
5573
|
+
/** Scope for partitioning memories (e.g., "private", "shared") */
|
|
5574
|
+
scope: string;
|
|
5575
|
+
/** Folder ID for organization, null if unfiled */
|
|
5576
|
+
folderId: string | null;
|
|
5577
|
+
/** User ID for multi-user server-side scoping, null on client */
|
|
5578
|
+
userId: string | null;
|
|
5579
|
+
/** JSON-stringified embedding vector, null if not yet computed */
|
|
5580
|
+
embedding: string | null;
|
|
5581
|
+
/** Model that produced `embedding`. Null on legacy rows (grandfathered as
|
|
5582
|
+
* compatible with the current model). */
|
|
5583
|
+
embeddingModel: string | null;
|
|
5584
|
+
/** JSON-stringified array of source message IDs this fact was extracted from. */
|
|
5585
|
+
sourceChunkIds: string[] | null;
|
|
5586
|
+
/** Times this fact has been re-observed (for ranking + UX badges). */
|
|
5587
|
+
proofCount: number | null;
|
|
5588
|
+
/** How the memory was created: manual | auto-extracted | capsule. */
|
|
5589
|
+
source: string | null;
|
|
5590
|
+
/** W6 temporal lane — Unix ms when the event occurred (point/start of range). */
|
|
5591
|
+
eventTimeStart: number | null;
|
|
5592
|
+
/** W6 temporal lane — Unix ms when the event ended (range only). */
|
|
5593
|
+
eventTimeEnd: number | null;
|
|
5594
|
+
/** W6 temporal lane — `point | range | ongoing | null`. */
|
|
5595
|
+
eventTimeKind: string | null;
|
|
5596
|
+
/** When true, the user has manually set this memory's topics (entity links);
|
|
5597
|
+
* auto-extraction leaves them alone. False on legacy/auto rows. */
|
|
5598
|
+
topicsUserManaged: boolean;
|
|
5599
|
+
/** Unix ms of the last LLM topic-extraction pass over this memory's content.
|
|
5600
|
+
* Null = never extracted standalone; rows that already carry entity links
|
|
5601
|
+
* are grandfathered as extracted (see getMemoriesNeedingTopicExtractionOp). */
|
|
5602
|
+
topicsExtractedAt: number | null;
|
|
5603
|
+
/** Write-time supersession (A2): id of the newer memory that replaced this
|
|
5604
|
+
* one (incompatible-value update, e.g. "Lives in Portland" → "Lives in SF").
|
|
5605
|
+
* Null = live. Superseded rows are excluded from recall/dedup by default but
|
|
5606
|
+
* kept for history + the read-time fallback. */
|
|
5607
|
+
supersededBy: string | null;
|
|
5608
|
+
/** Unix ms when this memory was superseded. Null when live. */
|
|
5609
|
+
supersededAt: number | null;
|
|
5610
|
+
/** Extraction-logic version this memory was last stamped under. Null (pre-v38)
|
|
5611
|
+
* reads as 0, so a TOPICS_EXTRACTION_VERSION bump re-extracts stale rows. */
|
|
5612
|
+
topicsExtractedVersion: number | null;
|
|
5613
|
+
/** C3 re-observation watermark: Unix ms of the last retain() merge into this
|
|
5614
|
+
* fact. Distinct from `updatedAt` (which merges preserve). Null = never
|
|
5615
|
+
* re-observed since the column was added; synthesis falls back to
|
|
5616
|
+
* `updatedAt` in that case. */
|
|
5617
|
+
lastObservedAt: number | null;
|
|
5618
|
+
/** Typed memory (PR1) — the extractor's FactType for this fact, or null on
|
|
5619
|
+
* legacy/manual/untyped rows. Plaintext string (not narrowed to FactType
|
|
5620
|
+
* here since the DB can hold any stored value). */
|
|
5621
|
+
factType: string | null;
|
|
5622
|
+
/** Decay archive state (PR2) — Unix ms when archived, or null when active. */
|
|
5623
|
+
archivedAt: number | null;
|
|
5624
|
+
/** Tier-0 security (PR3) — "quarantined" | "trusted" | null. */
|
|
5625
|
+
trustTier: string | null;
|
|
5626
|
+
createdAt: Date;
|
|
5627
|
+
updatedAt: Date;
|
|
5628
|
+
isDeleted: boolean;
|
|
5629
|
+
}
|
|
5630
|
+
interface CreateVaultMemoryOptions {
|
|
5631
|
+
content: string;
|
|
5632
|
+
/** Scope for the memory. Defaults to "private" if omitted. */
|
|
5633
|
+
scope?: string;
|
|
5634
|
+
/** Folder ID for organization, null or omitted if unfiled */
|
|
5635
|
+
folderId?: string | null;
|
|
5636
|
+
/** JSON-stringified embedding vector to persist */
|
|
5637
|
+
embedding?: string;
|
|
5638
|
+
/** Model that produced `embedding`. Persisted alongside it so a later
|
|
5639
|
+
* model change can detect and re-embed stale vectors. */
|
|
5640
|
+
embeddingModel?: string;
|
|
5641
|
+
/** Source message IDs that produced this fact (auto-extraction provenance). */
|
|
5642
|
+
sourceChunkIds?: string[];
|
|
5643
|
+
/** Initial proof count. Defaults to 1 if omitted. */
|
|
5644
|
+
proofCount?: number;
|
|
5645
|
+
/** How the memory was created. Defaults to "manual" if omitted. */
|
|
5646
|
+
source?: string;
|
|
5647
|
+
/** W6 temporal lane — when the event in this memory occurred. */
|
|
5648
|
+
eventTime?: {
|
|
5649
|
+
/** Unix ms timestamp of event start (or point). */
|
|
5650
|
+
start: number | null;
|
|
5651
|
+
/** Unix ms timestamp of event end (range only). */
|
|
5652
|
+
end: number | null;
|
|
5653
|
+
/** Kind: 'point' | 'range' | 'ongoing' | null (or omit). */
|
|
5654
|
+
kind: "point" | "range" | "ongoing" | null;
|
|
5655
|
+
};
|
|
5656
|
+
/** Typed memory (PR1) — the extractor's classification for this fact.
|
|
5657
|
+
* Omit for manual/untyped saves (persisted as null). */
|
|
5658
|
+
factType?: FactType;
|
|
5659
|
+
/** Tier-0 security (PR3) — set "quarantined" when the injection screen
|
|
5660
|
+
* flagged this fact. Omit for the default (null/trusted). */
|
|
5661
|
+
trustTier?: string;
|
|
5662
|
+
}
|
|
5663
|
+
interface UpdateVaultMemoryOptions {
|
|
5664
|
+
content: string;
|
|
5665
|
+
/** If provided, updates the memory's scope. */
|
|
5666
|
+
scope?: string;
|
|
5667
|
+
/** If provided, moves the memory to this folder. */
|
|
5668
|
+
folderId?: string | null;
|
|
5669
|
+
/** JSON-stringified embedding vector to persist, or null to clear stale embedding */
|
|
5670
|
+
embedding?: string | null;
|
|
5671
|
+
/** Model that produced `embedding`. Set whenever `embedding` is written so
|
|
5672
|
+
* the stored model tag stays in sync with the vector. */
|
|
5673
|
+
embeddingModel?: string | null;
|
|
5674
|
+
/** Replace source-chunk-ids list (used during merge to accumulate provenance). */
|
|
5675
|
+
sourceChunkIds?: string[];
|
|
5676
|
+
/** Set an absolute proof count. Prefer {@link proofCountIncrement} for
|
|
5677
|
+
* re-observation paths so the read+write happens inside the writer
|
|
5678
|
+
* and concurrent retains can't lose updates. */
|
|
5679
|
+
proofCount?: number;
|
|
5680
|
+
/** Atomically bump proof_count by this delta inside the write block.
|
|
5681
|
+
* Reads the current value from the in-memory record at write time, so
|
|
5682
|
+
* two parallel retain() calls observe each other's commits and neither
|
|
5683
|
+
* loses its increment. Wins over `proofCount` when both are set. */
|
|
5684
|
+
proofCountIncrement?: number;
|
|
5685
|
+
/** Set source ("manual" | "auto-extracted" | "capsule"). */
|
|
5686
|
+
source?: string;
|
|
5687
|
+
/**
|
|
5688
|
+
* W6 temporal lane — write the event-time fields on update. Use during
|
|
5689
|
+
* auto-merge to preserve (or refine) the original event-time signal when
|
|
5690
|
+
* a new observation lands on an existing fact. Omit to leave the
|
|
5691
|
+
* existing values untouched.
|
|
5692
|
+
*/
|
|
5693
|
+
eventTime?: {
|
|
5694
|
+
start: number | null;
|
|
5695
|
+
end: number | null;
|
|
5696
|
+
kind: "point" | "range" | "ongoing" | null;
|
|
5697
|
+
};
|
|
5698
|
+
/**
|
|
5699
|
+
* When true, restore the existing `updated_at` after the write so the
|
|
5700
|
+
* recency multiplier doesn't see a re-observation as a brand-new fact.
|
|
5701
|
+
* Set by auto-merge/consolidate paths — they want proof_count to bump
|
|
5702
|
+
* without inflating recency on top.
|
|
5703
|
+
*/
|
|
5704
|
+
preserveUpdatedAt?: boolean;
|
|
5705
|
+
/** C3: Unix ms to stamp as the re-observation watermark (`last_observed_at`).
|
|
5706
|
+
* Set by retain() merge/consolidate paths so a re-observation records "seen
|
|
5707
|
+
* again now" without touching `updated_at` (which `preserveUpdatedAt` keeps
|
|
5708
|
+
* pinned). Omit to leave the existing value untouched. */
|
|
5709
|
+
lastObservedAt?: number;
|
|
5710
|
+
/** If provided, sets whether the user has taken manual control of this
|
|
5711
|
+
* memory's topics. Set by {@link setMemoryEntitiesOp}. */
|
|
5712
|
+
topicsUserManaged?: boolean;
|
|
5713
|
+
/** Typed memory (PR1) — set/refine the fact's classification on update.
|
|
5714
|
+
* Used by retain()'s lazy backfill (adopt an incoming type only when the
|
|
5715
|
+
* existing row has none). Omit to leave the existing value untouched. */
|
|
5716
|
+
factType?: FactType;
|
|
5717
|
+
/** Tier-0 security (PR3) — set the trust tier on update ("quarantined" |
|
|
5718
|
+
* "trusted"). Omit to leave the existing value untouched. */
|
|
5719
|
+
trustTier?: string;
|
|
5720
|
+
/**
|
|
5721
|
+
* PR5 — un-archive on re-observe. When true, clears `archived_at` (null) as
|
|
5722
|
+
* part of the write, resurrecting a decayed row that a new observation just
|
|
5723
|
+
* merged into. retain() sets this (with `preserveUpdatedAt` OFF) so the
|
|
5724
|
+
* restored row's decay clock resets and it doesn't immediately re-archive.
|
|
5725
|
+
* Omit/false to leave `archived_at` untouched.
|
|
5726
|
+
*/
|
|
5727
|
+
restore?: boolean;
|
|
5316
5728
|
}
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
/**
|
|
5325
|
-
|
|
5729
|
+
|
|
5730
|
+
interface VaultMemoryOperationsContext {
|
|
5731
|
+
database: Database;
|
|
5732
|
+
vaultMemoryCollection: Collection<VaultMemory>;
|
|
5733
|
+
walletAddress?: string;
|
|
5734
|
+
signMessage?: SignMessageFn;
|
|
5735
|
+
embeddedWalletSigner?: EmbeddedWalletSignerFn;
|
|
5736
|
+
/** When set, operations scope to this user (server-side multi-user). */
|
|
5737
|
+
userId?: string;
|
|
5326
5738
|
/**
|
|
5327
|
-
*
|
|
5328
|
-
*
|
|
5329
|
-
*
|
|
5739
|
+
* Asserts this context runs against a physically single-tenant database — one
|
|
5740
|
+
* where every row belongs to the same owner (the per-wallet client DBs, which
|
|
5741
|
+
* hold exactly one wallet's rows written with `user_id = null`). This is the
|
|
5742
|
+
* ONLY thing that makes the decay sweep's unscoped scan/archive/delete safe
|
|
5743
|
+
* without a `userId`: see {@link assertVaultScopeForSweep}. A shared /
|
|
5744
|
+
* multi-tenant DB must NOT set this — it must scope by `userId` instead.
|
|
5745
|
+
* `walletAddress` presence alone is NOT a substitute (the sweep query filters
|
|
5746
|
+
* by `user_id` only, so a bare `walletAddress` on a shared DB would sweep
|
|
5747
|
+
* every tenant).
|
|
5330
5748
|
*/
|
|
5331
|
-
|
|
5749
|
+
singleTenant?: boolean;
|
|
5332
5750
|
/**
|
|
5333
|
-
*
|
|
5334
|
-
*
|
|
5335
|
-
*
|
|
5336
|
-
* `memoryEntityCollection` from your DatabaseManager.
|
|
5751
|
+
* When set, vault delete ops cascade to memory_entity rows pointing at
|
|
5752
|
+
* the deleted memories. Without this the W5 graph lane keeps returning
|
|
5753
|
+
* IDs of soft-deleted memories and the join table grows unbounded.
|
|
5337
5754
|
*/
|
|
5338
5755
|
entityCtx?: EntityOperationsContext;
|
|
5339
5756
|
}
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
candidateCount: number;
|
|
5348
|
-
/** Diagnostic: total memories in the vault when fact lane was queried. */
|
|
5349
|
-
vaultSize?: number;
|
|
5350
|
-
}
|
|
5351
|
-
/** Soft-degradation signals surfaced via {@link RecallDiagnostics.degraded}. */
|
|
5352
|
-
type RecallDegradation =
|
|
5353
|
-
/** Rerank was requested (budget mid/high) but the cross-encoder didn't run
|
|
5354
|
-
* this call — unavailable (e.g. React Native) or a transient failure. */
|
|
5355
|
-
"rerank-unavailable"
|
|
5356
|
-
/** `budget: 'high'` requested but no `decomposeOptions`, so query
|
|
5357
|
-
* decomposition was skipped and the budget downgraded to mid. */
|
|
5358
|
-
| "decompose-unavailable";
|
|
5359
|
-
/**
|
|
5360
|
-
* Per-call recall observability payload (see {@link RecallOptions.onDiagnostics}).
|
|
5361
|
-
* All timings are wall-clock milliseconds. Lane counts are post-dedupe,
|
|
5362
|
-
* pre-fusion. Intended to be forwarded to a metrics sink (e.g. PostHog).
|
|
5363
|
-
*/
|
|
5364
|
-
interface RecallDiagnostics {
|
|
5365
|
-
/** Budget actually executed (may have downgraded from the requested one). */
|
|
5366
|
-
usedBudget: Budget;
|
|
5367
|
-
/** Whether the cross-encoder actually reranked the fact lane this call. */
|
|
5368
|
-
reranked: boolean;
|
|
5369
|
-
/** Total candidates considered before truncation. */
|
|
5370
|
-
candidateCount: number;
|
|
5371
|
-
/** Total vault size when the fact lane ran (absent if it didn't). */
|
|
5372
|
-
vaultSize?: number;
|
|
5373
|
-
/** Facts the fact lane returned (post-dedupe, pre-fusion). */
|
|
5374
|
-
factCount: number;
|
|
5375
|
-
/** Chunks the chunk lane returned (post-dedupe, pre-fusion). */
|
|
5376
|
-
chunkCount: number;
|
|
5377
|
-
/** Wall-clock phase timings (ms). */
|
|
5378
|
-
timings: {
|
|
5379
|
-
/** Whole `recall()` call. */
|
|
5380
|
-
total: number;
|
|
5381
|
-
/** Parallel query-embed + graph/temporal side-lane build. */
|
|
5382
|
-
prep: number;
|
|
5383
|
-
/** Vault fact-lane search (`searchVaultMemoriesWithSize`). */
|
|
5384
|
-
factLane: number;
|
|
5385
|
-
/** Chunk-lane search (`searchChunksOp`). */
|
|
5386
|
-
chunkLane: number;
|
|
5387
|
-
/** Cross-lane RRF fusion + provenance dedup after both lanes. */
|
|
5388
|
-
fuse: number;
|
|
5389
|
-
};
|
|
5390
|
-
/** Soft-degradation signals that fired this call (empty when clean). */
|
|
5391
|
-
degraded: RecallDegradation[];
|
|
5392
|
-
}
|
|
5393
|
-
type RetainAction = "create" | "merge" | "update" | "skip" | "suppressed" | "supersede";
|
|
5394
|
-
type RetainSource = "manual" | "auto-extracted" | "capsule";
|
|
5395
|
-
/** Why the consolidator fell back to "create" instead of a real decision. */
|
|
5396
|
-
type ConsolidationFallbackReason = "llm_error" | "invalid_response";
|
|
5397
|
-
interface RetainOptions {
|
|
5398
|
-
source?: RetainSource;
|
|
5399
|
-
sourceChunkIds?: string[];
|
|
5400
|
-
scope?: string;
|
|
5757
|
+
declare function createVaultMemoryOp(ctx: VaultMemoryOperationsContext, opts: CreateVaultMemoryOptions): Promise<StoredVaultMemory>;
|
|
5758
|
+
declare function createVaultMemoriesBatchOp(ctx: VaultMemoryOperationsContext, optionsArray: CreateVaultMemoryOptions[]): Promise<StoredVaultMemory[]>;
|
|
5759
|
+
declare function getVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string): Promise<StoredVaultMemory | null>;
|
|
5760
|
+
declare function getAllVaultMemoriesOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
5761
|
+
scopes?: string[];
|
|
5762
|
+
since?: Date;
|
|
5763
|
+
limit?: number;
|
|
5401
5764
|
folderId?: string | null;
|
|
5402
|
-
/** When provided, applies merge-on-write logic instead of plain insert. */
|
|
5403
|
-
enableAutoMerge?: boolean;
|
|
5404
5765
|
/**
|
|
5405
|
-
*
|
|
5406
|
-
*
|
|
5407
|
-
*
|
|
5408
|
-
*
|
|
5409
|
-
* Returns `action: 'suppressed'` with the matched `tombstoneId`.
|
|
5766
|
+
* Include soft-deleted memories in the result (each carries
|
|
5767
|
+
* `isDeleted: true`). Default `false` — deleted rows are excluded, as
|
|
5768
|
+
* they are from every other read path. Used by the Memory Graph to
|
|
5769
|
+
* render "forgotten" nodes; ordinary consumers should leave this off.
|
|
5410
5770
|
*/
|
|
5411
|
-
|
|
5412
|
-
/**
|
|
5413
|
-
|
|
5771
|
+
includeDeleted?: boolean;
|
|
5772
|
+
/** Include archived (decayed) memories. Default `false` (PR1 choke point). */
|
|
5773
|
+
includeArchived?: boolean;
|
|
5774
|
+
/** Include quarantined memories. Default `false` (PR1 choke point). */
|
|
5775
|
+
includeQuarantined?: boolean;
|
|
5776
|
+
/** Typed memory (PR1) — restrict to these fact types. Omit for no filter. */
|
|
5777
|
+
factTypes?: string[];
|
|
5414
5778
|
/**
|
|
5415
|
-
*
|
|
5416
|
-
*
|
|
5417
|
-
*
|
|
5418
|
-
* Auth/endpoint required — one of `apiKey` / `getToken` (see
|
|
5419
|
-
* {@link PortalLlmAuth}); without this option we keep the cosine-only path.
|
|
5779
|
+
* Include A2-superseded memories (each carries `supersededBy`). Default
|
|
5780
|
+
* `false` — superseded rows are excluded, as they are from recall/dedup.
|
|
5781
|
+
* Used by a "memory history" view to render retired facts.
|
|
5420
5782
|
*/
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5783
|
+
includeSuperseded?: boolean;
|
|
5784
|
+
}): Promise<StoredVaultMemory[]>;
|
|
5785
|
+
declare function getAllVaultMemoryContentsOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
5786
|
+
since?: Date;
|
|
5787
|
+
}): Promise<string[]>;
|
|
5788
|
+
declare function updateVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string, opts: UpdateVaultMemoryOptions): Promise<StoredVaultMemory | null>;
|
|
5789
|
+
declare function deleteVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string): Promise<boolean>;
|
|
5790
|
+
/**
|
|
5791
|
+
* Mark a memory as superseded by a newer one (A2 write-time supersession).
|
|
5792
|
+
* The row stays in the table (history + read-time fallback) but is excluded
|
|
5793
|
+
* from recall/dedup by default via `superseded_by`. Idempotent-ish: no-op if
|
|
5794
|
+
* the row is missing, not owned, deleted, or already superseded. Does NOT
|
|
5795
|
+
* preserve `updated_at` — superseded rows are hidden from recall, so their
|
|
5796
|
+
* recency is irrelevant.
|
|
5797
|
+
*
|
|
5798
|
+
* @param id - the memory being retired (e.g. "Lives in Portland")
|
|
5799
|
+
* @param supersededById - the newer memory that replaced it (e.g. "Lives in SF")
|
|
5800
|
+
*/
|
|
5801
|
+
declare function supersedeVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string, supersededById: string): Promise<boolean>;
|
|
5802
|
+
/**
|
|
5803
|
+
* Get all non-deleted, unfiled vault memories (folder_id is null).
|
|
5804
|
+
*/
|
|
5805
|
+
declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): Promise<StoredVaultMemory[]>;
|
|
5806
|
+
/**
|
|
5807
|
+
* The current topic-extraction logic version. Bump this whenever the extraction
|
|
5808
|
+
* prompt or model in `topicExtract.ts` changes: every memory stamped under an
|
|
5809
|
+
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
5810
|
+
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
5811
|
+
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
5812
|
+
*/
|
|
5813
|
+
declare const TOPICS_EXTRACTION_VERSION = 1;
|
|
5814
|
+
/**
|
|
5815
|
+
* Result of {@link getMemoriesNeedingTopicExtractionOp}: which memories the
|
|
5816
|
+
* background topic worker should run LLM entity extraction on, and which it
|
|
5817
|
+
* should merely stamp as already-extracted.
|
|
5818
|
+
*/
|
|
5819
|
+
interface MemoriesNeedingTopicExtraction {
|
|
5444
5820
|
/**
|
|
5445
|
-
*
|
|
5446
|
-
*
|
|
5447
|
-
*
|
|
5448
|
-
*
|
|
5821
|
+
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
5822
|
+
* with no entity links, plus stamped rows edited since their last pass
|
|
5823
|
+
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
5824
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}. Edited /
|
|
5825
|
+
* stale-version rows come first (they get priority under `limit`), each group
|
|
5826
|
+
* newest-created first.
|
|
5449
5827
|
*/
|
|
5450
|
-
|
|
5451
|
-
kind: "point" | "range" | "ongoing";
|
|
5452
|
-
start: number;
|
|
5453
|
-
end: number | null;
|
|
5454
|
-
} | null;
|
|
5455
|
-
}
|
|
5456
|
-
interface RetainResult {
|
|
5457
|
-
action: RetainAction;
|
|
5458
|
-
memoryId: string;
|
|
5459
|
-
/** When action is 'merge' or 'update', the prior memory's id. When action is
|
|
5460
|
-
* 'supersede', the stale memory that was retired (`memoryId` is the new one). */
|
|
5461
|
-
targetId?: string;
|
|
5828
|
+
pending: StoredVaultMemory[];
|
|
5462
5829
|
/**
|
|
5463
|
-
*
|
|
5464
|
-
*
|
|
5830
|
+
* IDs of rows that already have entity links but no watermark — legacy rows
|
|
5831
|
+
* extracted by the conversation pipeline before v36. Grandfather these with
|
|
5832
|
+
* {@link stampTopicsExtractedAtOp} (no LLM call) so a later content edit
|
|
5833
|
+
* makes them re-extractable instead of invisible forever. Bounded by the
|
|
5834
|
+
* same `limit` as {@link pending} — stamping loads a Model per row, so the
|
|
5835
|
+
* grandfather backlog is drained across sweeps rather than in one spike.
|
|
5465
5836
|
*/
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5837
|
+
linkedUnstamped: string[];
|
|
5838
|
+
}
|
|
5839
|
+
/**
|
|
5840
|
+
* Sweep query for the background topic-extraction worker: partition the
|
|
5841
|
+
* user's non-deleted, non-user-managed memories by what the worker should do
|
|
5842
|
+
* with them (see {@link MemoriesNeedingTopicExtraction}). User-managed rows
|
|
5843
|
+
* are never returned — the user owns their topics, including an intentionally
|
|
5844
|
+
* empty set. Requires `ctx.entityCtx` for the entity-links check.
|
|
5845
|
+
*/
|
|
5846
|
+
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
5847
|
+
limit?: number;
|
|
5848
|
+
}): Promise<MemoriesNeedingTopicExtraction>;
|
|
5849
|
+
/**
|
|
5850
|
+
* Stamp `topics_extracted_at` (and `topics_extracted_version`) on the given
|
|
5851
|
+
* memories — the topic worker calls this after a successful extraction pass
|
|
5852
|
+
* (including zero-entity results, so quiet memories aren't re-asked every sweep)
|
|
5853
|
+
* and to grandfather `linkedUnstamped` rows without an LLM call. `version`
|
|
5854
|
+
* defaults to {@link TOPICS_EXTRACTION_VERSION}: stamping at the current version
|
|
5855
|
+
* (for both fresh extractions and grandfathered legacy rows) means they aren't
|
|
5856
|
+
* re-extracted until a future version bump. Preserves `updated_at` so a stamp
|
|
5857
|
+
* never inflates the recency multiplier — and never masks a concurrent content
|
|
5858
|
+
* edit from the next sweep. Skips deleted, foreign-user, and user-managed rows.
|
|
5859
|
+
* Returns the IDs actually stamped.
|
|
5860
|
+
*
|
|
5861
|
+
* ALL eligibility AND `updated_at` are read from the LIVE Model inside the
|
|
5862
|
+
* serialized writer — never a pre-writer snapshot. Writers are serialized, so
|
|
5863
|
+
* a content edit or topic-override that commits before this writer runs is
|
|
5864
|
+
* observed here: its fresh `updated_at` is preserved (so the next sweep's
|
|
5865
|
+
* `updated_at > stamp` check still fires) and a mid-pass user-managed flip
|
|
5866
|
+
* skips the row. Reading `updated_at` from a raw pre-fetch instead would write
|
|
5867
|
+
* a stale value back, pushing `updated_at < topics_extracted_at` and hiding
|
|
5868
|
+
* the edited memory from every future sweep.
|
|
5869
|
+
*
|
|
5870
|
+
* Callers bound the input via `getMemoriesNeedingTopicExtractionOp`'s `limit`
|
|
5871
|
+
* (both `pending` and `linkedUnstamped` are capped), so the per-row Model load
|
|
5872
|
+
* needed to `prepareUpdate` stays bounded and never spikes the RecordCache.
|
|
5873
|
+
*/
|
|
5874
|
+
declare function stampTopicsExtractedAtOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[], extractedAt: number, version?: number): Promise<string[]>;
|
|
5875
|
+
declare function deleteAllVaultMemoriesForUserOp(ctx: VaultMemoryOperationsContext, userId: string): Promise<number>;
|
|
5876
|
+
/**
|
|
5877
|
+
* The minimal plaintext shape the decay sweep needs — mirrors the `DecayInput`
|
|
5878
|
+
* shape in `memory/decay` plus the row id. Deliberately omits `content`
|
|
5879
|
+
* (encrypted) so the sweep stays zero-knowledge.
|
|
5880
|
+
*/
|
|
5881
|
+
interface DecayCandidateRaw {
|
|
5882
|
+
uniqueId: string;
|
|
5883
|
+
factType: string | null;
|
|
5884
|
+
eventTimeEnd: number | null;
|
|
5885
|
+
eventTimeKind: string | null;
|
|
5886
|
+
/** Unix ms — the raw `updated_at`, used both for the age rule and as the
|
|
5887
|
+
* optimistic-concurrency guard passed back to {@link archiveVaultMemoryOp}. */
|
|
5888
|
+
updatedAt: number;
|
|
5889
|
+
archivedAt: number | null;
|
|
5890
|
+
source: string | null;
|
|
5891
|
+
/** `trusted` | `quarantined` | null. Quarantined rows still decay by RULE, but
|
|
5892
|
+
* are never handed to the optional content-reading decay classifier (they must
|
|
5893
|
+
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
5894
|
+
trustTier: string | null;
|
|
5469
5895
|
}
|
|
5470
|
-
|
|
5471
5896
|
/**
|
|
5472
|
-
*
|
|
5473
|
-
*
|
|
5897
|
+
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
5898
|
+
* `classifyDecay` (in `memory/decay`) needs via
|
|
5899
|
+
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
5900
|
+
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
5474
5901
|
*
|
|
5475
|
-
*
|
|
5476
|
-
*
|
|
5477
|
-
*
|
|
5478
|
-
* and unioning source_chunk_ids). Otherwise it inserts a new memory.
|
|
5902
|
+
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
5903
|
+
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
5904
|
+
* `baseVaultConditions` default keeps `is_deleted = false`.
|
|
5479
5905
|
*
|
|
5480
|
-
*
|
|
5481
|
-
*
|
|
5482
|
-
* resolver upstream deciding when auto-merge should be enabled vs when
|
|
5483
|
-
* a semantic update should be applied via the lower-level update op.
|
|
5906
|
+
* Refuses to run on an unscoped multi-tenant context (see
|
|
5907
|
+
* {@link assertVaultScopeForSweep}).
|
|
5484
5908
|
*/
|
|
5485
|
-
|
|
5486
|
-
interface RetainContext {
|
|
5487
|
-
vaultCtx: VaultMemoryOperationsContext;
|
|
5488
|
-
embeddingOptions: EmbeddingOptions;
|
|
5489
|
-
vaultCache: VaultEmbeddingCache;
|
|
5490
|
-
}
|
|
5909
|
+
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
5491
5910
|
/**
|
|
5492
|
-
*
|
|
5493
|
-
*
|
|
5911
|
+
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
|
5912
|
+
* drops out of every recall lane via the `baseVaultConditions` choke point but
|
|
5913
|
+
* stays recoverable via {@link restoreVaultMemoryOp} until the hard-delete
|
|
5914
|
+
* window elapses.
|
|
5494
5915
|
*
|
|
5495
|
-
*
|
|
5496
|
-
*
|
|
5497
|
-
*
|
|
5498
|
-
*
|
|
5916
|
+
* Concurrency: re-checks `is_deleted` / ownership / `archived_at` INSIDE the
|
|
5917
|
+
* serialized writer (mirrors {@link updateVaultMemoryOp}). Additionally, when
|
|
5918
|
+
* `opts.expectedUpdatedAt` is given, the archive is skipped if the row's current
|
|
5919
|
+
* `updated_at` no longer matches — i.e. a `retain()` merge (which bumps
|
|
5920
|
+
* `updated_at`) landed between the sweep's candidate scan and this write, so the
|
|
5921
|
+
* fact was just re-observed and must NOT be archived on stale data. Idempotent:
|
|
5922
|
+
* a row another sweep already archived returns `false` (no double-write).
|
|
5499
5923
|
*
|
|
5500
|
-
*
|
|
5501
|
-
*
|
|
5924
|
+
* @returns `true` if this call archived the row; `false` if it was stale
|
|
5925
|
+
* (deleted / not owned / already archived / refreshed under us).
|
|
5502
5926
|
*/
|
|
5503
|
-
declare function
|
|
5504
|
-
|
|
5927
|
+
declare function archiveVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string, opts?: {
|
|
5928
|
+
/** Timestamp to stamp into `archived_at`. Default `Date.now()`. */
|
|
5929
|
+
now?: number;
|
|
5930
|
+
/** Optimistic-concurrency guard: skip if the row's `updated_at` changed
|
|
5931
|
+
* since the sweep observed it (a concurrent re-observation). */
|
|
5932
|
+
expectedUpdatedAt?: number;
|
|
5933
|
+
}): Promise<boolean>;
|
|
5505
5934
|
/**
|
|
5506
|
-
*
|
|
5935
|
+
* Restore an archived memory (PR2) — clear `archived_at` so it re-enters recall.
|
|
5936
|
+
* Re-checks `is_deleted` / ownership inside the writer. Idempotent on an
|
|
5937
|
+
* already-active row (clearing null → null is harmless).
|
|
5507
5938
|
*
|
|
5508
|
-
*
|
|
5509
|
-
*
|
|
5510
|
-
|
|
5511
|
-
|
|
5939
|
+
* @returns `true` if the row was restored (or already active); `false` if it was
|
|
5940
|
+
* deleted / not owned / missing.
|
|
5941
|
+
*/
|
|
5942
|
+
declare function restoreVaultMemoryOp(ctx: VaultMemoryOperationsContext, id: string): Promise<boolean>;
|
|
5943
|
+
/**
|
|
5944
|
+
* Hard-delete a memory ONLY if it is still archived and still past the delete
|
|
5945
|
+
* window (PR2 decay terminal transition). Unlike the generic
|
|
5946
|
+
* {@link deleteVaultMemoryOp}, this re-reads `archived_at` INSIDE the writer and
|
|
5947
|
+
* bails if the row was restored (`archived_at → null`) or re-archived more
|
|
5948
|
+
* recently since the sweep's candidate scan. This is the restore-vs-delete
|
|
5949
|
+
* mutual-exclusion guard: a user hitting Restore between the scan and this write
|
|
5950
|
+
* must win, so their just-rescued memory is never permanently lost.
|
|
5512
5951
|
*
|
|
5513
|
-
*
|
|
5514
|
-
*
|
|
5515
|
-
|
|
5952
|
+
* @returns `true` if this call hard-deleted the row; `false` if it was stale
|
|
5953
|
+
* (deleted / not owned / no longer archived / no longer past the window).
|
|
5954
|
+
*/
|
|
5955
|
+
declare function hardDeleteDecayedOp(ctx: VaultMemoryOperationsContext, id: string, opts: {
|
|
5956
|
+
hardDeleteWindowMs: number;
|
|
5957
|
+
now?: number;
|
|
5958
|
+
}): Promise<boolean>;
|
|
5959
|
+
declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext, id: string, embedding: string, embeddingModel: string): Promise<boolean>;
|
|
5960
|
+
|
|
5961
|
+
/**
|
|
5962
|
+
* Queue Types
|
|
5516
5963
|
*
|
|
5517
|
-
*
|
|
5964
|
+
* Types for the in-memory write queue that holds operations
|
|
5965
|
+
* when encryption keys aren't yet available.
|
|
5518
5966
|
*/
|
|
5519
5967
|
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5968
|
+
/**
|
|
5969
|
+
* Types of database operations that can be queued.
|
|
5970
|
+
*/
|
|
5971
|
+
type QueuedOperationType = "createConversation" | "updateConversationTitle" | "updateConversationPinned" | "createMessage" | "updateMessage" | "createMedia" | "createMediaBatch" | "updateMediaMessageId";
|
|
5972
|
+
/**
|
|
5973
|
+
* A single queued database operation.
|
|
5974
|
+
*/
|
|
5975
|
+
interface QueuedOperation {
|
|
5976
|
+
/** Unique ID for this operation */
|
|
5524
5977
|
id: string;
|
|
5525
|
-
|
|
5526
|
-
|
|
5978
|
+
/** Type of operation */
|
|
5979
|
+
type: QueuedOperationType;
|
|
5980
|
+
/** Wallet address this operation belongs to */
|
|
5981
|
+
walletAddress: string;
|
|
5982
|
+
/** When the operation was queued */
|
|
5983
|
+
timestamp: number;
|
|
5984
|
+
/** Priority for ordering (lower = higher priority). Conversations=0, Messages=1, Media=2 */
|
|
5985
|
+
priority: number;
|
|
5986
|
+
/** IDs of operations that must complete before this one */
|
|
5987
|
+
dependencies: string[];
|
|
5988
|
+
/** Operation-specific payload */
|
|
5989
|
+
payload: Record<string, any>;
|
|
5990
|
+
/** Number of times this operation has been retried */
|
|
5991
|
+
retryCount: number;
|
|
5992
|
+
/** Maximum number of retries allowed */
|
|
5993
|
+
maxRetries: number;
|
|
5527
5994
|
}
|
|
5528
5995
|
/**
|
|
5529
|
-
*
|
|
5530
|
-
* classification. `kind` is omitted when the model gave no kind or an
|
|
5531
|
-
* unrecognized one — see {@link validateCandidates}.
|
|
5996
|
+
* Status of a wallet's queue.
|
|
5532
5997
|
*/
|
|
5533
|
-
interface
|
|
5534
|
-
|
|
5535
|
-
|
|
5998
|
+
interface QueueStatus {
|
|
5999
|
+
/** Number of pending operations */
|
|
6000
|
+
pending: number;
|
|
6001
|
+
/** Number of operations that failed all retries */
|
|
6002
|
+
failed: number;
|
|
6003
|
+
/** Whether the queue is currently being flushed */
|
|
6004
|
+
isFlushing: boolean;
|
|
6005
|
+
/** Whether the queue is paused (e.g., wallet disconnected) */
|
|
6006
|
+
isPaused: boolean;
|
|
5536
6007
|
}
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
/**
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
/** Unix ms timestamp of the event end. Only set when kind='range'. */
|
|
5551
|
-
end: number | null;
|
|
5552
|
-
} | null;
|
|
6008
|
+
/**
|
|
6009
|
+
* Result of a flush operation.
|
|
6010
|
+
*/
|
|
6011
|
+
interface FlushResult {
|
|
6012
|
+
/** IDs of operations that succeeded */
|
|
6013
|
+
succeeded: string[];
|
|
6014
|
+
/** Operations that failed with their errors */
|
|
6015
|
+
failed: Array<{
|
|
6016
|
+
id: string;
|
|
6017
|
+
error: string;
|
|
6018
|
+
}>;
|
|
6019
|
+
/** Total number of operations attempted */
|
|
6020
|
+
total: number;
|
|
5553
6021
|
}
|
|
5554
6022
|
/**
|
|
5555
|
-
*
|
|
5556
|
-
* one of `apiKey` / `getToken` is required at runtime; see
|
|
5557
|
-
* {@link PortalLlmAuth}.
|
|
6023
|
+
* Encryption context needed to execute queued operations.
|
|
5558
6024
|
*/
|
|
5559
|
-
interface
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
6025
|
+
interface QueueEncryptionContext {
|
|
6026
|
+
walletAddress: string;
|
|
6027
|
+
signMessage: SignMessageFn;
|
|
6028
|
+
embeddedWalletSigner?: EmbeddedWalletSignerFn;
|
|
6029
|
+
}
|
|
6030
|
+
/**
|
|
6031
|
+
* Executor function that runs a single queued operation.
|
|
6032
|
+
* Provided by the consumer (e.g., useChatStorage) during flush.
|
|
6033
|
+
*/
|
|
6034
|
+
type OperationExecutor = (operation: QueuedOperation, encryptionContext: QueueEncryptionContext) => Promise<void>;
|
|
6035
|
+
|
|
6036
|
+
/**
|
|
6037
|
+
* Queue Manager
|
|
6038
|
+
*
|
|
6039
|
+
* In-memory queue for database operations when encryption keys aren't yet available.
|
|
6040
|
+
* Operations are held in memory and flushed to the database once the key becomes available.
|
|
6041
|
+
*
|
|
6042
|
+
* Key design decisions:
|
|
6043
|
+
* - In-memory only: queue is lost on page refresh (acceptable since user must re-auth anyway)
|
|
6044
|
+
* - Per-wallet isolation: each wallet has its own queue
|
|
6045
|
+
* - Dependency tracking: operations are flushed in correct order (conversation before messages)
|
|
6046
|
+
* - Max 1000 operations per wallet to prevent memory leaks
|
|
6047
|
+
*/
|
|
6048
|
+
|
|
6049
|
+
declare class QueueManager {
|
|
6050
|
+
/** Per-wallet operation queues: wallet address -> operation id -> operation */
|
|
6051
|
+
private queues;
|
|
6052
|
+
/** Per-wallet flushing state */
|
|
6053
|
+
private flushing;
|
|
6054
|
+
/** Per-wallet paused state */
|
|
6055
|
+
private paused;
|
|
6056
|
+
/** Per-wallet failed operations (exceeded max retries) */
|
|
6057
|
+
private failedOps;
|
|
6058
|
+
/** Change listeners: wallet address -> callbacks */
|
|
6059
|
+
private listeners;
|
|
5564
6060
|
/**
|
|
5565
|
-
*
|
|
5566
|
-
*
|
|
5567
|
-
* a personal memory system already knows who the user is, so "Peter Lee" is
|
|
5568
|
-
* circular noise. Optional; when omitted only the bare-fragment gate applies.
|
|
6061
|
+
* Queue a new operation for a wallet.
|
|
6062
|
+
* @returns The operation ID, or null if queue is full.
|
|
5569
6063
|
*/
|
|
5570
|
-
|
|
6064
|
+
queueOperation(walletAddress: string, type: QueuedOperationType, payload: Record<string, any>, dependencies?: string[], maxRetries?: number): string | null;
|
|
5571
6065
|
/**
|
|
5572
|
-
*
|
|
5573
|
-
* transcript ("yesterday", "next week", "in two days") into the absolute
|
|
5574
|
-
* YYYY-MM-DD anchors the W6 temporal lane indexes on. The transcript itself
|
|
5575
|
-
* carries no timestamps, so without an anchor the model resolves relatives
|
|
5576
|
-
* against its own training-cutoff guess and emits wrong `eventTime` dates.
|
|
5577
|
-
* Defaults to `Date.now()`. Override for back-dated eval corpora and
|
|
5578
|
-
* deterministic tests (mirrors {@link RecallOptions.now}).
|
|
5579
|
-
*
|
|
5580
|
-
* Server-side timezone note: the ms value is formatted to a calendar date in
|
|
5581
|
-
* the process's local timezone (same basis as `parseLocalCalendarDay`). On a
|
|
5582
|
-
* UTC server, a user near midnight in a non-UTC offset can get the wrong
|
|
5583
|
-
* calendar day. Pass the user's local-midnight timestamp as `now` when the
|
|
5584
|
-
* process timezone doesn't match the user's.
|
|
6066
|
+
* Get all pending operations for a wallet, sorted by dependency order.
|
|
5585
6067
|
*/
|
|
5586
|
-
|
|
6068
|
+
getOperations(walletAddress: string): QueuedOperation[];
|
|
5587
6069
|
/**
|
|
5588
|
-
*
|
|
5589
|
-
* Lower it to bound how long extraction can hold a turn open — e.g. a worker
|
|
5590
|
-
* that runs extraction behind an in-flight-turn guard can pass `2` to keep
|
|
5591
|
-
* repeated failures from delaying later turns.
|
|
6070
|
+
* Remove a specific operation from the queue.
|
|
5592
6071
|
*/
|
|
5593
|
-
|
|
5594
|
-
/** Per-attempt timeout (ms) for the extraction call. Defaults to the portal
|
|
5595
|
-
* helper's 60s. Combine with {@link maxAttempts} to cap the total time budget. */
|
|
5596
|
-
timeoutMs?: number;
|
|
6072
|
+
removeOperation(walletAddress: string, operationId: string): void;
|
|
5597
6073
|
/**
|
|
5598
|
-
*
|
|
5599
|
-
* When set, the loop stops before an attempt that would exceed it, so worst-case
|
|
5600
|
-
* latency is bounded rather than `maxAttempts × timeoutMs`.
|
|
6074
|
+
* Get the status of a wallet's queue.
|
|
5601
6075
|
*/
|
|
5602
|
-
|
|
6076
|
+
getStatus(walletAddress: string): QueueStatus;
|
|
5603
6077
|
/**
|
|
5604
|
-
*
|
|
5605
|
-
*
|
|
5606
|
-
*
|
|
6078
|
+
* Flush all queued operations for a wallet by executing them with encryption.
|
|
6079
|
+
*
|
|
6080
|
+
* @param encryptionContext - Wallet address and signing functions for encryption
|
|
6081
|
+
* @param executor - Function that executes each operation against the database
|
|
6082
|
+
* @returns Result with succeeded/failed operation IDs
|
|
5607
6083
|
*/
|
|
5608
|
-
|
|
6084
|
+
flush(encryptionContext: QueueEncryptionContext, executor: OperationExecutor): Promise<FlushResult>;
|
|
5609
6085
|
/**
|
|
5610
|
-
*
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
*
|
|
5615
|
-
* for a fresh per-call redactor, or a shared {@link PiiRedactor} to keep
|
|
5616
|
-
* placeholder numbering consistent with other calls.
|
|
5617
|
-
*
|
|
5618
|
-
* NOTE: this does NOT cover the embeddings provider. Facts are stored and
|
|
5619
|
-
* embedded with their real values, so to keep PII out of embedding requests
|
|
5620
|
-
* set `RetainContext.embeddingOptions.maskInput` (e.g. `redactor.maskText`)
|
|
5621
|
-
* as well — the two are independent switches.
|
|
6086
|
+
* Clear all queued operations for a wallet.
|
|
6087
|
+
*/
|
|
6088
|
+
clear(walletAddress: string): void;
|
|
6089
|
+
/**
|
|
6090
|
+
* Pause the queue for a wallet (stops flush mid-way).
|
|
5622
6091
|
*/
|
|
5623
|
-
|
|
6092
|
+
pause(walletAddress: string): void;
|
|
5624
6093
|
/**
|
|
5625
|
-
*
|
|
5626
|
-
* its retries (empty/malformed completion, network/HTTP error) — i.e. a
|
|
5627
|
-
* *failure* that drops the turn's facts, as opposed to a legitimate
|
|
5628
|
-
* `{candidates: []}` "nothing durable here". Lets callers distinguish a
|
|
5629
|
-
* silently-degrading extractor from quiet turns (the two are otherwise
|
|
5630
|
-
* indistinguishable). See {@link extractAndRetain}'s `outcome`.
|
|
6094
|
+
* Resume the queue for a wallet.
|
|
5631
6095
|
*/
|
|
5632
|
-
|
|
6096
|
+
resume(walletAddress: string): void;
|
|
5633
6097
|
/**
|
|
5634
|
-
*
|
|
5635
|
-
*
|
|
5636
|
-
* can't be restored to real values) or restoring the values blew the length
|
|
5637
|
-
* cap. These drops happen before `retain()`, so `failedCount` can't see them,
|
|
5638
|
-
* and the turn would otherwise masquerade as a quiet `no-facts` result. Lets
|
|
5639
|
-
* H3's `outcome` surface `dropped-after-redaction` so a rising PII-drop rate
|
|
5640
|
-
* (i.e. redaction silently eating facts) is alarmable.
|
|
6098
|
+
* Register a listener for queue changes on a wallet.
|
|
6099
|
+
* @returns Unsubscribe function
|
|
5641
6100
|
*/
|
|
5642
|
-
|
|
6101
|
+
onQueueChange(walletAddress: string, callback: () => void): () => void;
|
|
6102
|
+
/**
|
|
6103
|
+
* Check if a wallet has any pending operations.
|
|
6104
|
+
*/
|
|
6105
|
+
hasPending(walletAddress: string): boolean;
|
|
6106
|
+
/** Move a failed operation to the failed set. */
|
|
6107
|
+
private moveToFailed;
|
|
6108
|
+
/** Notify all listeners for a wallet. */
|
|
6109
|
+
private notifyListeners;
|
|
5643
6110
|
}
|
|
6111
|
+
/** Singleton queue manager instance */
|
|
6112
|
+
declare const queueManager: QueueManager;
|
|
6113
|
+
|
|
5644
6114
|
/**
|
|
5645
|
-
*
|
|
5646
|
-
* conversation. Returns post-validated candidates only (confidence
|
|
5647
|
-
* threshold, source-id check, length cap, schema validation). Returns
|
|
5648
|
-
* an empty array if the LLM emits malformed JSON or no candidates.
|
|
5649
|
-
*
|
|
5650
|
-
* A null from `callPortalJsonCompletion` means a *failure* (empty completion,
|
|
5651
|
-
* malformed JSON, network/HTTP error) — distinct from a successful
|
|
5652
|
-
* `{candidates: []}`, which parses to a non-null object. A failed extraction
|
|
5653
|
-
* silently drops the whole turn's memories, so transient failures matter:
|
|
5654
|
-
* `callPortalJsonCompletion` retries them internally with backoff (default 3
|
|
5655
|
-
* attempts), so a one-off empty/malformed completion from a reasoning-class
|
|
5656
|
-
* model doesn't lose the turn. Only an exhausted-retry null reaches here.
|
|
5657
|
-
*/
|
|
5658
|
-
declare function extractFacts(messages: AutoExtractMessage[], options: ExtractFactsOptions): Promise<ExtractedCandidate[]>;
|
|
5659
|
-
/**
|
|
5660
|
-
* Outcome of the EXTRACTOR stage for a turn — independent of whether the
|
|
5661
|
-
* subsequent `retain()` writes landed (that's `failedCount`):
|
|
5662
|
-
* - `extracted` — the extractor produced at least one candidate above
|
|
5663
|
-
* the confidence floor. Whether those writes succeeded
|
|
5664
|
-
* is reported separately by `failedCount` — so
|
|
5665
|
-
* `extracted` + `failedCount > 0` means "found facts but
|
|
5666
|
-
* writes are failing", which is more signal than
|
|
5667
|
-
* collapsing it into `no-facts` would give.
|
|
5668
|
-
* - `no-facts` — the extractor ran fine but found nothing durable.
|
|
5669
|
-
* - `empty-after-retry` — the extractor returned empty/malformed after
|
|
5670
|
-
* exhausting retries (a *failure*). Distinguishing this
|
|
5671
|
-
* from `no-facts` is what makes a silently-degrading
|
|
5672
|
-
* extractor alarmable rather than invisible.
|
|
5673
|
-
* - `dropped-after-redaction` — the extractor found facts but PII
|
|
5674
|
-
* de-anonymization dropped every one (mangled
|
|
5675
|
-
* placeholders / over-cap after restore), before retain.
|
|
5676
|
-
* A degradation `failedCount` can't see; would otherwise
|
|
5677
|
-
* look like `no-facts`.
|
|
5678
|
-
*/
|
|
5679
|
-
type ExtractOutcome = "extracted" | "no-facts" | "empty-after-retry" | "dropped-after-redaction";
|
|
5680
|
-
/**
|
|
5681
|
-
* Stage 2 — for each extracted candidate, call retain() with auto-merge
|
|
5682
|
-
* enabled. When `consolidateOptions` are wired, retain()'s consolidation pass
|
|
5683
|
-
* is the "resolver": a second LLM call against the top-K existing vault
|
|
5684
|
-
* memories that decides create/update/noop/supersede — including retiring a
|
|
5685
|
-
* stale value on a state change (the supersession the extraction prompt above
|
|
5686
|
-
* promises). Without consolidation, retain() still dedups at the cosine-merge
|
|
5687
|
-
* level, and the read-time supersession heuristic remains the fallback.
|
|
6115
|
+
* Wallet Poller
|
|
5688
6116
|
*
|
|
5689
|
-
*
|
|
5690
|
-
*
|
|
5691
|
-
* or skipped), plus an `outcome` summarizing why the turn did/didn't produce
|
|
5692
|
-
* facts (see {@link ExtractOutcome}).
|
|
6117
|
+
* Polls for embedded wallet availability during Privy initialization.
|
|
6118
|
+
* When a wallet becomes available, triggers a callback so the queue can flush.
|
|
5693
6119
|
*/
|
|
5694
|
-
declare
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
/** Override scope/folder for all retained facts. */
|
|
5698
|
-
scope?: string;
|
|
5699
|
-
folderId?: string | null;
|
|
5700
|
-
/**
|
|
5701
|
-
* Forwarded verbatim to each retain() call — enables the LLM-based
|
|
5702
|
-
* consolidation pass (Hindsight facet-dedup) on every write. See
|
|
5703
|
-
* `RetainOptions.consolidateOptions` for auth + observability fields.
|
|
5704
|
-
*/
|
|
5705
|
-
consolidateOptions?: RetainOptions["consolidateOptions"];
|
|
6120
|
+
declare class WalletPoller {
|
|
6121
|
+
private timerId;
|
|
6122
|
+
private attempts;
|
|
5706
6123
|
/**
|
|
5707
|
-
*
|
|
5708
|
-
*
|
|
5709
|
-
*
|
|
5710
|
-
*
|
|
6124
|
+
* Start polling for wallet availability.
|
|
6125
|
+
*
|
|
6126
|
+
* @param checkWallet - Returns wallet address when ready, null if not yet available
|
|
6127
|
+
* @param onWalletReady - Called with the wallet address when it becomes available
|
|
6128
|
+
* @param intervalMs - Polling interval in milliseconds (default: 1000ms)
|
|
6129
|
+
* @param maxAttempts - Maximum polling attempts before giving up (default: 60)
|
|
6130
|
+
* @returns Stop function to cancel polling
|
|
5711
6131
|
*/
|
|
5712
|
-
|
|
6132
|
+
startPolling(checkWallet: () => Promise<string | null>, onWalletReady: (address: string) => void, intervalMs?: number, maxAttempts?: number): () => void;
|
|
5713
6133
|
/**
|
|
5714
|
-
*
|
|
5715
|
-
* whose `retain()` call threw. Lets UI layers surface "couldn't save
|
|
5716
|
-
* X" toasts; without it consumers only see the aggregate
|
|
5717
|
-
* `failedCount` and can't name which facts dropped.
|
|
6134
|
+
* Stop polling.
|
|
5718
6135
|
*/
|
|
5719
|
-
|
|
5720
|
-
}
|
|
5721
|
-
candidates: ExtractedCandidate[];
|
|
5722
|
-
results: RetainResult[];
|
|
5723
|
-
failedCount: number;
|
|
5724
|
-
outcome: ExtractOutcome;
|
|
5725
|
-
}>;
|
|
6136
|
+
stop(): void;
|
|
6137
|
+
}
|
|
5726
6138
|
|
|
5727
6139
|
/**
|
|
5728
6140
|
* Auto-extraction worker — orchestration glue for wiring
|
|
@@ -5782,6 +6194,16 @@ interface MemoryExtractedEvent {
|
|
|
5782
6194
|
result: RetainResult;
|
|
5783
6195
|
conversationId?: string;
|
|
5784
6196
|
}
|
|
6197
|
+
/**
|
|
6198
|
+
* Tier-0 security (PR3) — fired once per candidate the injection screen
|
|
6199
|
+
* quarantined and persisted as an audit row. Distinct from
|
|
6200
|
+
* {@link MemoryExtractedEvent} so a client can render "held for review"
|
|
6201
|
+
* without treating a poisoned fact as a normal saved memory.
|
|
6202
|
+
* @public
|
|
6203
|
+
*/
|
|
6204
|
+
interface MemoryQuarantinedEvent extends QuarantinedMemoryInfo {
|
|
6205
|
+
conversationId?: string;
|
|
6206
|
+
}
|
|
5785
6207
|
/** @public */
|
|
5786
6208
|
interface TurnSkippedEvent {
|
|
5787
6209
|
/**
|
|
@@ -5920,6 +6342,12 @@ interface CreateAutoExtractorOptions {
|
|
|
5920
6342
|
};
|
|
5921
6343
|
/** Per-fact event — fires once per memory written. */
|
|
5922
6344
|
onMemoryExtracted?: (event: MemoryExtractedEvent) => void;
|
|
6345
|
+
/**
|
|
6346
|
+
* Tier-0 security (PR3) — fires once per candidate quarantined by the
|
|
6347
|
+
* injection screen (and persisted as an audit row). Lets a client surface a
|
|
6348
|
+
* "held for review" state instead of the fact silently disappearing.
|
|
6349
|
+
*/
|
|
6350
|
+
onMemoryQuarantined?: (event: MemoryQuarantinedEvent) => void;
|
|
5923
6351
|
/** Per-turn event — fires once after the whole pipeline finishes. */
|
|
5924
6352
|
onTurnComplete?: (event: TurnCompleteEvent) => void;
|
|
5925
6353
|
/** Diagnostic — fires when a turn is skipped. */
|
|
@@ -5980,6 +6408,536 @@ declare const DEFAULT_CHUNK_CACHE_SIZE = 5000;
|
|
|
5980
6408
|
*/
|
|
5981
6409
|
declare function createChunkVectorCache(maxSize?: number): ChunkVectorCache;
|
|
5982
6410
|
|
|
6411
|
+
/**
|
|
6412
|
+
* Temporal decay classifier (PR2) — pure, DB-free, unit-testable.
|
|
6413
|
+
*
|
|
6414
|
+
* Decides whether a stored memory should be kept, archived (soft, recoverable),
|
|
6415
|
+
* or hard-deleted, based ONLY on its plaintext columns. This is the
|
|
6416
|
+
* zero-knowledge core of the decay sweep: it never sees `content` and never
|
|
6417
|
+
* triggers a signature/decrypt, so it can run wherever a wallet key is NOT
|
|
6418
|
+
* loaded. The sweep worker ({@link ./decayWorker}) feeds it raw rows selected
|
|
6419
|
+
* via `unsafeFetchRaw` and materializes Models only for the transitions.
|
|
6420
|
+
*
|
|
6421
|
+
* Lifecycle (mirrors the timestamp state machine in db/schema.ts):
|
|
6422
|
+
* active → archived (set `archived_at`) — recoverable, drops out of recall
|
|
6423
|
+
* archived → deleted (set `is_deleted`) — terminal, after HARD_DELETE_WINDOW
|
|
6424
|
+
*
|
|
6425
|
+
* Per-type TTL policy (the "becomes past" intuition):
|
|
6426
|
+
* - identity / preference / relationship / constraint → never age-archive
|
|
6427
|
+
* (durable facts about who the user is; Infinity TTL).
|
|
6428
|
+
* - plan / ongoing_context → short TTL (they go stale as time moves on).
|
|
6429
|
+
* - other / null (untyped/legacy) → medium TTL.
|
|
6430
|
+
* `source === "manual"` memories are protected from AUTO-ARCHIVE (user-curated)
|
|
6431
|
+
* — but NOT from the hard-delete clock once they are archived: archive is the
|
|
6432
|
+
* shared purge buffer for every row, so an already-archived manual memory is
|
|
6433
|
+
* still deleted after the window (the archived check runs before the manual
|
|
6434
|
+
* short-circuit by design).
|
|
6435
|
+
*/
|
|
6436
|
+
/** The verdict for a single memory. */
|
|
6437
|
+
type DecayVerdict = "keep" | "archive" | "delete";
|
|
6438
|
+
/** Short TTL — `plan` / `ongoing_context` age out ~30 days after last touch. */
|
|
6439
|
+
declare const SHORT_TTL_MS: number;
|
|
6440
|
+
/** Medium TTL — `other` / untyped rows age out ~180 days after last touch. */
|
|
6441
|
+
declare const MEDIUM_TTL_MS: number;
|
|
6442
|
+
/**
|
|
6443
|
+
* "Never" TTL — durable identity-class types (identity/preference/relationship/
|
|
6444
|
+
* constraint) never age-archive. Expressed as +Infinity so the age comparison
|
|
6445
|
+
* `now - updatedAt > ttl` is always false.
|
|
6446
|
+
*/
|
|
6447
|
+
declare const NEVER_TTL_MS: number;
|
|
6448
|
+
/**
|
|
6449
|
+
* How long an archived memory lingers (recoverable) before the sweep hard-
|
|
6450
|
+
* deletes it. ~30 days gives the user a window to restore an over-eager decay.
|
|
6451
|
+
*/
|
|
6452
|
+
declare const HARD_DELETE_WINDOW_MS: number;
|
|
6453
|
+
/**
|
|
6454
|
+
* Grace applied after a `plan` / `ongoing_context` event's `event_time_end`
|
|
6455
|
+
* before it's considered "become past" and archived. Avoids archiving a plan
|
|
6456
|
+
* the instant its end date passes (the user may still reference it briefly).
|
|
6457
|
+
*/
|
|
6458
|
+
declare const PAST_EVENT_GRACE_MS: number;
|
|
6459
|
+
/**
|
|
6460
|
+
* Per-type TTL + window overrides. Pass a partial to {@link classifyDecay} /
|
|
6461
|
+
* the sweeper to tune behavior (evaluation harnesses, tests). Anything omitted
|
|
6462
|
+
* falls back to {@link DEFAULT_DECAY_POLICY}.
|
|
6463
|
+
*/
|
|
6464
|
+
interface DecayPolicy {
|
|
6465
|
+
/**
|
|
6466
|
+
* Age TTL (ms since `updatedAt`) per FactType. A type absent from this map —
|
|
6467
|
+
* or a null/unknown `factType` — uses {@link DecayPolicy.fallbackTtlMs}.
|
|
6468
|
+
*/
|
|
6469
|
+
ttlByType: Record<string, number>;
|
|
6470
|
+
/** TTL for `null` / unknown fact types (the medium bucket). */
|
|
6471
|
+
fallbackTtlMs: number;
|
|
6472
|
+
/** How long an archived row lingers before hard delete. */
|
|
6473
|
+
hardDeleteWindowMs: number;
|
|
6474
|
+
/** Grace after a past `event_time_end` before archiving a plan/ongoing. */
|
|
6475
|
+
pastEventGraceMs: number;
|
|
6476
|
+
}
|
|
6477
|
+
/** Default policy — the locked per-type TTLs. */
|
|
6478
|
+
declare const DEFAULT_DECAY_POLICY: DecayPolicy;
|
|
6479
|
+
/**
|
|
6480
|
+
* The minimal plaintext shape {@link classifyDecay} reads. Deliberately excludes
|
|
6481
|
+
* `content` (encrypted; never touched by decay) — the sweep selects exactly
|
|
6482
|
+
* these columns so it stays zero-knowledge.
|
|
6483
|
+
*/
|
|
6484
|
+
interface DecayInput {
|
|
6485
|
+
/**
|
|
6486
|
+
* The row's stable id. Not read by the rule engine ({@link classifyDecay}
|
|
6487
|
+
* ignores it) — it is threaded through so an optional content-reading decay
|
|
6488
|
+
* classifier (PR5, {@link createLlmDecayClassifier}) can fetch + decrypt the
|
|
6489
|
+
* row for a borderline verdict. Optional so pure rule-based callers/tests can
|
|
6490
|
+
* omit it.
|
|
6491
|
+
*/
|
|
6492
|
+
id?: string;
|
|
6493
|
+
/** The extractor's FactType, or null (legacy/manual/untyped → medium bucket). */
|
|
6494
|
+
factType: string | null;
|
|
6495
|
+
/** W6 temporal lane — Unix ms the event ended (range/ongoing), or null. */
|
|
6496
|
+
eventTimeEnd: number | null;
|
|
6497
|
+
/** W6 temporal lane — `point | range | ongoing | null`. */
|
|
6498
|
+
eventTimeKind: string | null;
|
|
6499
|
+
/** Unix ms of the row's last write (re-observation resets this). */
|
|
6500
|
+
updatedAt: number;
|
|
6501
|
+
/** Unix ms when archived, or null when active. */
|
|
6502
|
+
archivedAt: number | null;
|
|
6503
|
+
/** `manual` | `auto-extracted` | `capsule` | null. Manual is never decayed. */
|
|
6504
|
+
source: string | null;
|
|
6505
|
+
/**
|
|
6506
|
+
* `trusted` | `quarantined` | null. Not read by the rule engine
|
|
6507
|
+
* ({@link classifyDecay} ignores it — quarantined rows still age/archive/delete
|
|
6508
|
+
* by RULE). It exists only so the sweeper's `isBorderline` can keep a
|
|
6509
|
+
* quarantined (injection-screened) row away from the optional content-reading
|
|
6510
|
+
* classifier, so poison content never egresses. Optional so pure rule-based
|
|
6511
|
+
* callers/tests can omit it (treated as not quarantined).
|
|
6512
|
+
*/
|
|
6513
|
+
trustTier?: string | null;
|
|
6514
|
+
}
|
|
6515
|
+
/**
|
|
6516
|
+
* The age TTL for a fact type. `null` / unknown types fall to the medium
|
|
6517
|
+
* fallback bucket (never Infinity — untyped rows must still eventually age out).
|
|
6518
|
+
*/
|
|
6519
|
+
declare function ttlForType(factType: string | null, policy?: Partial<DecayPolicy>): number;
|
|
6520
|
+
/**
|
|
6521
|
+
* Classify one memory into a decay verdict, reading only plaintext fields.
|
|
6522
|
+
*
|
|
6523
|
+
* Rules, in order (first match wins):
|
|
6524
|
+
* 0. Non-finite timestamps → keep (safe) + warn (corrupt row, observable).
|
|
6525
|
+
* 1. Already archived (`archivedAt != null`): delete once past the hard-delete
|
|
6526
|
+
* window, else keep — runs REGARDLESS of `source`, so the purge clock
|
|
6527
|
+
* applies to archived manual rows too.
|
|
6528
|
+
* 2. `source === "manual"` → keep — protects manual saves from AUTO-ARCHIVE
|
|
6529
|
+
* only (rules 3/4). Delete-of-already-archived is handled above.
|
|
6530
|
+
* 3. `plan` / `ongoing_context` whose event has become past — `eventTimeEnd`
|
|
6531
|
+
* is set AND older than `now - grace` → archive. An `ongoing` status with a
|
|
6532
|
+
* null `eventTimeEnd` is still ongoing, so this rule does not fire for it.
|
|
6533
|
+
* 4. Age fallback — `now - updatedAt > TTL(factType)` → archive. Durable types
|
|
6534
|
+
* have Infinity TTL, so this never fires for them; null/unknown use medium.
|
|
6535
|
+
* EXCEPTION: a `plan`/`ongoing_context` with a still-upcoming event
|
|
6536
|
+
* (`eventTimeEnd >= now`) is kept — it must not age-archive before the event
|
|
6537
|
+
* happens; only rule 3 (after it passes) may archive it.
|
|
6538
|
+
* 5. Otherwise → keep.
|
|
6539
|
+
*
|
|
6540
|
+
* @param m Plaintext decay inputs for one row.
|
|
6541
|
+
* @param now Reference time (Unix ms). Injected for determinism.
|
|
6542
|
+
* @param policy Optional partial policy overriding {@link DEFAULT_DECAY_POLICY}.
|
|
6543
|
+
*/
|
|
6544
|
+
declare function classifyDecay(m: DecayInput, now: number, policy?: Partial<DecayPolicy>): DecayVerdict;
|
|
6545
|
+
|
|
6546
|
+
/**
|
|
6547
|
+
* Decay sweeper (PR2) — orchestration glue that runs the temporal-decay
|
|
6548
|
+
* lifecycle over the vault. Mirrors the shape of {@link ./autoExtractWorker}'s
|
|
6549
|
+
* `createAutoExtractor`: created once, driven by the caller (a client hook on a
|
|
6550
|
+
* low-frequency interval / app-foreground), disposable.
|
|
6551
|
+
*
|
|
6552
|
+
* A sweep is zero-knowledge by construction:
|
|
6553
|
+
* 1. {@link getDecayCandidatesRawOp} selects plaintext columns via
|
|
6554
|
+
* `unsafeFetchRaw` — no `content`, no decrypt, no Model per row.
|
|
6555
|
+
* 2. {@link classifyDecay} is a pure function over those plaintext fields.
|
|
6556
|
+
* 3. Only the (usually small) transition set is materialized as Models to
|
|
6557
|
+
* archive ({@link archiveVaultMemoryOp}) or hard-delete
|
|
6558
|
+
* ({@link hardDeleteDecayedOp}, which re-checks archived + past-window
|
|
6559
|
+
* inside the write so a concurrent restore wins).
|
|
6560
|
+
*
|
|
6561
|
+
* The optional {@link DecayClassifier} is a PR5 seam for an on-device model that
|
|
6562
|
+
* refines borderline verdicts by reading decrypted content — it is NOT
|
|
6563
|
+
* implemented here. Default is `undefined` → pure rule-based, which is what
|
|
6564
|
+
* keeps the default path zero-knowledge.
|
|
6565
|
+
*/
|
|
6566
|
+
|
|
6567
|
+
/** Counts from one sweep, for UI surfacing (e.g. "N memories archived"). */
|
|
6568
|
+
interface DecaySweepResult {
|
|
6569
|
+
/** Rows transitioned active → archived this sweep. */
|
|
6570
|
+
archived: number;
|
|
6571
|
+
/** Rows hard-deleted (archived past the window) this sweep. */
|
|
6572
|
+
deleted: number;
|
|
6573
|
+
/** Total candidate rows scanned (all non-hard-deleted rows). */
|
|
6574
|
+
scanned: number;
|
|
6575
|
+
}
|
|
6576
|
+
/**
|
|
6577
|
+
* PR5 seam — an on-device classifier that refines the rule-based verdict for
|
|
6578
|
+
* borderline rows (e.g. type `other`/null, or a `plan` without an event end).
|
|
6579
|
+
* Because it may read decrypted content, callers MUST gate providing it on
|
|
6580
|
+
* wallet-key availability. Left undefined here → pure rule-based sweep.
|
|
6581
|
+
*
|
|
6582
|
+
* Egress is bounded by the sweeper (NOT the classifier): each sweep caps how
|
|
6583
|
+
* many rows reach the classifier ({@link CreateDecaySweeperOptions.maxClassifierCallsPerSweep})
|
|
6584
|
+
* and a row whose (id, updated_at) was already classified is never re-sent on a
|
|
6585
|
+
* later sweep — so a stable borderline "keep" row egresses at most once.
|
|
6586
|
+
*
|
|
6587
|
+
* SECURITY (MEDIUM, residual) — enabling a classifier hands whoever answers the
|
|
6588
|
+
* portal (incl. a malicious / MITM'd endpoint) a lever over the affected rows:
|
|
6589
|
+
* a hostile verdict can only quarantine-adjacent OUTCOMES here, i.e. archive a
|
|
6590
|
+
* row (reversible — never a hard delete, which stays the deterministic
|
|
6591
|
+
* archived-past-window mechanic). Bounded, reversible trust tradeoff; gate the
|
|
6592
|
+
* classifier on trust in the portal.
|
|
6593
|
+
*
|
|
6594
|
+
* @param input The same plaintext inputs the rule engine saw.
|
|
6595
|
+
* @param ruleVerdict The rule-based verdict, as a starting point / fallback.
|
|
6596
|
+
* @param now The sweep's reference time (Unix ms). Injected so the
|
|
6597
|
+
* classifier derives any age math from the same clock the rule engine used,
|
|
6598
|
+
* never wall-clock `Date.now()` — keeping a fixed-`now` sweep deterministic.
|
|
6599
|
+
* @returns The (possibly refined) verdict.
|
|
6600
|
+
*/
|
|
6601
|
+
interface DecayClassifier {
|
|
6602
|
+
classify(input: DecayInput, ruleVerdict: DecayVerdict, now: number): Promise<DecayVerdict> | DecayVerdict;
|
|
6603
|
+
}
|
|
6604
|
+
/** A clock: a fixed timestamp (tests) or a getter (production intervals). */
|
|
6605
|
+
type NowSource = number | (() => number);
|
|
6606
|
+
/** @public */
|
|
6607
|
+
interface CreateDecaySweeperOptions {
|
|
6608
|
+
/** Vault write context — the same one recall/retain use. */
|
|
6609
|
+
vaultCtx: VaultMemoryOperationsContext;
|
|
6610
|
+
/**
|
|
6611
|
+
* Reference "now". A number is fixed (deterministic tests); a function is
|
|
6612
|
+
* re-evaluated per sweep (long-lived interval usage). Default `Date.now`.
|
|
6613
|
+
*/
|
|
6614
|
+
now?: NowSource;
|
|
6615
|
+
/** Partial policy overriding the per-type TTL defaults. Omit for defaults. */
|
|
6616
|
+
policy?: Partial<DecayPolicy>;
|
|
6617
|
+
/**
|
|
6618
|
+
* PR5 seam. When provided, borderline candidates' rule verdict is passed
|
|
6619
|
+
* through it. Gate on key availability (it may decrypt content). Default
|
|
6620
|
+
* undefined. Egress is bounded — see {@link maxClassifierCallsPerSweep}.
|
|
6621
|
+
*/
|
|
6622
|
+
classifier?: DecayClassifier;
|
|
6623
|
+
/**
|
|
6624
|
+
* PR5 — hard ceiling on classifier invocations (and thus decrypted-content
|
|
6625
|
+
* portal egress) per sweep. Once hit, the remaining borderline rows fall back
|
|
6626
|
+
* to the rule verdict for that sweep (no call). Prevents a large vault from
|
|
6627
|
+
* firing hundreds of sequential content-bearing calls in one sweep. Default
|
|
6628
|
+
* {@link DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP} (20). Cache hits (a row
|
|
6629
|
+
* already classified at its current `updated_at`) do NOT count against this.
|
|
6630
|
+
*/
|
|
6631
|
+
maxClassifierCallsPerSweep?: number;
|
|
6632
|
+
/** Fires once after each sweep with the transition counts (UI). */
|
|
6633
|
+
onSwept?: (result: DecaySweepResult) => void;
|
|
6634
|
+
/** Diagnostic — fires on an unexpected sweep-level error. */
|
|
6635
|
+
onError?: (error: Error) => void;
|
|
6636
|
+
}
|
|
6637
|
+
/** @public */
|
|
6638
|
+
interface DecaySweeper {
|
|
6639
|
+
/**
|
|
6640
|
+
* Scan the vault, classify every candidate, and apply archive/delete
|
|
6641
|
+
* transitions. Safe to call repeatedly (idempotent — a keep stays a keep, an
|
|
6642
|
+
* already-archived row won't re-archive). Returns the transition counts.
|
|
6643
|
+
* A no-op (returns zero counts) after {@link DecaySweeper.dispose}.
|
|
6644
|
+
*/
|
|
6645
|
+
runSweep(): Promise<DecaySweepResult>;
|
|
6646
|
+
/** Stop accepting sweeps. An in-flight `runSweep()` resolves normally. */
|
|
6647
|
+
dispose(): void;
|
|
6648
|
+
}
|
|
6649
|
+
/**
|
|
6650
|
+
* Default per-sweep ceiling on decay-classifier invocations (see
|
|
6651
|
+
* {@link CreateDecaySweeperOptions.maxClassifierCallsPerSweep}). Kept small: the
|
|
6652
|
+
* classifier egresses DECRYPTED content, so a sweep must never fan out to
|
|
6653
|
+
* hundreds of sequential calls. 20 covers the borderline churn of a typical
|
|
6654
|
+
* sweep while capping worst-case egress; stable rows are also cached so this
|
|
6655
|
+
* ceiling is rarely reached after the first sweep.
|
|
6656
|
+
*/
|
|
6657
|
+
declare const DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP = 20;
|
|
6658
|
+
/**
|
|
6659
|
+
* Create a decay sweeper. See module docstring for the zero-knowledge contract.
|
|
6660
|
+
*/
|
|
6661
|
+
declare function createDecaySweeper(options: CreateDecaySweeperOptions): DecaySweeper;
|
|
6662
|
+
|
|
6663
|
+
/**
|
|
6664
|
+
* Optional content-reading decay classifier (PR5).
|
|
6665
|
+
*
|
|
6666
|
+
* The rule engine ({@link ./decay}.classifyDecay) decides keep/archive/delete
|
|
6667
|
+
* from plaintext columns ALONE — it never sees `content`, which is what keeps
|
|
6668
|
+
* the default decay sweep zero-knowledge. That is the right default, but it is
|
|
6669
|
+
* weakest on BORDERLINE rows: an `other`/untyped fact, or a `plan` with no
|
|
6670
|
+
* event end, whose staleness is a pure `updated_at` guess.
|
|
6671
|
+
*
|
|
6672
|
+
* This factory builds a {@link DecayClassifier} that reads the DECRYPTED content
|
|
6673
|
+
* of such a row and returns keep-vs-archive. The sweeper consults it ONLY for a
|
|
6674
|
+
* borderline row whose CURRENT rule verdict is `keep` — a rule archive/delete
|
|
6675
|
+
* short-circuits before the classifier ever runs (see `decayWorker.verdictFor` +
|
|
6676
|
+
* `isBorderline`). So its whole job is to REFINE a still-kept borderline row
|
|
6677
|
+
* toward EARLIER archive when the content shows it is genuinely ephemeral (a
|
|
6678
|
+
* finished one-off plan, an expired temporary state) — turning a weak age-only
|
|
6679
|
+
* "keep" into an "archive" sooner.
|
|
6680
|
+
*
|
|
6681
|
+
* DIRECTIONALITY IS DELIBERATE (a security property, not a shortcoming): it can
|
|
6682
|
+
* move a borderline keep → archive, never the reverse. It CANNOT rescue /
|
|
6683
|
+
* keep-alive a row the rule would archive — the escalation gate makes that path
|
|
6684
|
+
* unreachable, and archive is reversible while un-archiving poison would not be.
|
|
6685
|
+
* Enabling a classifier hands whoever answers the portal (incl. a malicious /
|
|
6686
|
+
* MITM'd endpoint) a lever, and that lever must only ever archive-reversibly.
|
|
6687
|
+
*
|
|
6688
|
+
* ZERO-KNOWLEDGE CONTRACT (the caller MUST honor this):
|
|
6689
|
+
* - `getContent` is supplied by the caller and MUST be gated on wallet-key
|
|
6690
|
+
* availability: return `null` when no key is loaded. With no content the
|
|
6691
|
+
* classifier degrades to the rule verdict — never blocks, never guesses.
|
|
6692
|
+
* - When it does read content and calls the portal, the content is
|
|
6693
|
+
* PII-redacted first. Redaction is OPT-OUT, not opt-in: it defaults ON when
|
|
6694
|
+
* the consumer omits `piiRedaction`, so a caller can never accidentally
|
|
6695
|
+
* egress raw PII. Pass `piiRedaction: false` to deliberately disable it.
|
|
6696
|
+
* - Egress is bounded by the SWEEPER, not this classifier: the sweep caps
|
|
6697
|
+
* invocations per pass and never re-sends an unchanged (id, updated_at) row
|
|
6698
|
+
* (see `decayWorker`), so a stable borderline "keep" row egresses once.
|
|
6699
|
+
* - It only ever chooses keep vs archive, and only for a row the rule already
|
|
6700
|
+
* keeps. It NEVER escalates to `delete` and NEVER un-archives — hard-delete
|
|
6701
|
+
* is exclusively the deterministic archived-past-window mechanic (rule 1),
|
|
6702
|
+
* and a row the rule wants archived is archived regardless of this layer.
|
|
6703
|
+
*
|
|
6704
|
+
* Fails to the rule verdict (keep) on any error, missing id, no key, or
|
|
6705
|
+
* malformed response — like every other optional LLM layer in this subsystem it
|
|
6706
|
+
* can only refine a borderline keep toward archive, never make the sweep worse.
|
|
6707
|
+
*
|
|
6708
|
+
* RESIDUAL SCOPE (accepted): a genuinely durable fact MISTYPED as `other`/null
|
|
6709
|
+
* and never re-mentioned within its medium TTL WILL age-archive — this layer can
|
|
6710
|
+
* only bring that archive EARLIER, never prevent it. Accepted because it is
|
|
6711
|
+
* recoverable: archived rows stay in the Archived section, Restore clears
|
|
6712
|
+
* `archived_at`, and any re-observation resets the TTL. The alternative — a
|
|
6713
|
+
* keep-alive lever — is exactly the capability a hostile portal must NOT have.
|
|
6714
|
+
*
|
|
6715
|
+
* SECURITY (MEDIUM, residual) — this is a NEW portal call surface carrying
|
|
6716
|
+
* (redacted) memory content. It is opt-in (you must construct + pass it) and
|
|
6717
|
+
* gated on key availability. Beyond widening which models see facts, a
|
|
6718
|
+
* malicious / MITM'd portal can steer the verdict — but only to ARCHIVE a row
|
|
6719
|
+
* (reversible; never a hard delete). Gate it on trust in the portal.
|
|
6720
|
+
*/
|
|
6721
|
+
|
|
6722
|
+
/**
|
|
6723
|
+
* Auth + wiring for {@link createLlmDecayClassifier}. Auth is the dual pattern —
|
|
6724
|
+
* one of `apiKey` / `getToken` is required at runtime (see {@link PortalLlmAuth}).
|
|
6725
|
+
* @public
|
|
6726
|
+
*/
|
|
6727
|
+
interface LlmDecayClassifierOptions extends PortalLlmAuth {
|
|
6728
|
+
baseUrl?: string;
|
|
6729
|
+
model?: string;
|
|
6730
|
+
/** Override fetch (tests). */
|
|
6731
|
+
fetchFn?: typeof fetch;
|
|
6732
|
+
/** Max portal attempts on a TRANSIENT failure. Default 2. */
|
|
6733
|
+
maxAttempts?: number;
|
|
6734
|
+
/** Absolute wall-clock budget across attempts. Default 12s. */
|
|
6735
|
+
totalTimeoutMs?: number;
|
|
6736
|
+
/** Backoff before each retry (ms). Tests pass `() => 0`. */
|
|
6737
|
+
backoffMs?: (attempt: number) => number;
|
|
6738
|
+
/**
|
|
6739
|
+
* PII redaction for the outbound content. OPT-OUT: defaults to ON (a fresh
|
|
6740
|
+
* per-call redactor) when omitted, so decrypted content is never egressed
|
|
6741
|
+
* raw by accident. Pass a shared {@link PiiRedactor} to keep placeholder
|
|
6742
|
+
* numbering consistent with other calls, or `false` to deliberately disable
|
|
6743
|
+
* redaction. The verdict returned is a bare enum — nothing to de-anonymize.
|
|
6744
|
+
*/
|
|
6745
|
+
piiRedaction?: boolean | PiiRedactor;
|
|
6746
|
+
/**
|
|
6747
|
+
* Resolve a memory's DECRYPTED content by id. The caller supplies this and
|
|
6748
|
+
* MUST gate it on wallet-key availability — return `null` when no key is
|
|
6749
|
+
* loaded so the classifier degrades to the rule verdict (zero-knowledge).
|
|
6750
|
+
* A throw is treated the same as `null` (fail to the rule verdict).
|
|
6751
|
+
*/
|
|
6752
|
+
getContent: (id: string) => Promise<string | null>;
|
|
6753
|
+
}
|
|
6754
|
+
/**
|
|
6755
|
+
* Build a {@link DecayClassifier} that reads a borderline row's decrypted
|
|
6756
|
+
* content and returns a keep/archive verdict via a cheap portal LLM. Pass it as
|
|
6757
|
+
* `createDecaySweeper({ classifier })`. See the module docstring for the
|
|
6758
|
+
* zero-knowledge contract. Returns the rule verdict on any failure and never
|
|
6759
|
+
* escalates to `delete`.
|
|
6760
|
+
* @public
|
|
6761
|
+
*/
|
|
6762
|
+
declare function createLlmDecayClassifier(options: LlmDecayClassifierOptions): DecayClassifier;
|
|
6763
|
+
|
|
6764
|
+
/**
|
|
6765
|
+
* Multi-hop graph traversal for the W5 entity-graph recall lane (PR4,
|
|
6766
|
+
* MRAgent-style "active reconstruction").
|
|
6767
|
+
*
|
|
6768
|
+
* The single-hop lane (see `recall.ts:buildGraphLaneRanking`) does one thing:
|
|
6769
|
+
* extract entities from the query, look up memories sharing any of them, and
|
|
6770
|
+
* order those memories by shared-entity count. That is hop 1 — the "seed"
|
|
6771
|
+
* lookup. This module wraps a bounded breadth-first search around it: from the
|
|
6772
|
+
* seed memories it walks OUT to their neighbor entities (via the new reverse
|
|
6773
|
+
* edge {@link getEntitiesByMemoryIdsOp}), ranks those neighbors by
|
|
6774
|
+
* co-occurrence across the frontier, expands the top few, and pulls the
|
|
6775
|
+
* memories they reach — repeating up to {@link MAX_HOPS} times.
|
|
6776
|
+
*
|
|
6777
|
+
* Cost note (why this is cheap): NONE of this runs cosine. Cosine runs exactly
|
|
6778
|
+
* once per recall inside `searchVaultMemoriesWithSize`. This lane only emits an
|
|
6779
|
+
* ordered list of memory IDs that is RRF-fused against the cosine head. So
|
|
6780
|
+
* multi-hop adds indexed WatermelonDB joins + more RRF entries, not embedding
|
|
6781
|
+
* math. The dominant cost of enabling it is the reranker over a slightly larger
|
|
6782
|
+
* candidate pool — which stays bounded because `rerankTopN` remains
|
|
6783
|
+
* authoritative downstream.
|
|
6784
|
+
*
|
|
6785
|
+
* Determinism: neighbor-entity selection is pure co-occurrence counting by
|
|
6786
|
+
* default. An optional LLM path-refiner (PR5) can override which neighbors
|
|
6787
|
+
* expand; it falls back to the deterministic order on any error.
|
|
6788
|
+
*
|
|
6789
|
+
* Hop numbering: hop 1 is the seed lookup. `maxHops = 1` therefore means "seed
|
|
6790
|
+
* only" — byte-for-byte identical to the single-hop lane (the regression guard).
|
|
6791
|
+
* `MAX_HOPS = 2` (the PR5 default) performs one expansion beyond the seed.
|
|
6792
|
+
*
|
|
6793
|
+
* Neighbor selection at each expansion hop is deterministic co-occurrence
|
|
6794
|
+
* counting by default. PR5 adds an OPTIONAL LLM path-refiner
|
|
6795
|
+
* ({@link NeighborRefiner} / {@link createLlmNeighborRefiner}) that lets a model
|
|
6796
|
+
* pick which neighbor entities to expand instead; it is opt-in, capped to ≤1
|
|
6797
|
+
* call per hop, and falls back to the deterministic order on any error.
|
|
6798
|
+
*/
|
|
6799
|
+
|
|
6800
|
+
/**
|
|
6801
|
+
* Total hops the traversal performs, counting the seed lookup as hop 1.
|
|
6802
|
+
* `1` = seed only (identical to the single-hop lane). PR5 default is `2` (one
|
|
6803
|
+
* expansion beyond the seed). Overridable per-call for ablation. Still gated to
|
|
6804
|
+
* the `high` budget in recall, and capped back to 1 on large vaults by
|
|
6805
|
+
* {@link capHopsForDensity}.
|
|
6806
|
+
* @public
|
|
6807
|
+
*/
|
|
6808
|
+
declare const MAX_HOPS = 2;
|
|
6809
|
+
/**
|
|
6810
|
+
* Max neighbor entities expanded per hop. Caps fan-out so a densely-linked
|
|
6811
|
+
* frontier can't explode the candidate pool. Neighbors are ranked by
|
|
6812
|
+
* co-occurrence frequency across the frontier; only the top this-many expand.
|
|
6813
|
+
* @public
|
|
6814
|
+
*/
|
|
6815
|
+
declare const ENTITY_FANOUT = 8;
|
|
6816
|
+
/**
|
|
6817
|
+
* Hard ceiling on total accumulated memory IDs across all hops. The BFS stops
|
|
6818
|
+
* expanding once the accumulated set reaches this size (and the frontier is
|
|
6819
|
+
* bounded to it too), keeping the RRF pool — and the downstream reranker
|
|
6820
|
+
* workload — bounded regardless of graph density.
|
|
6821
|
+
* @public
|
|
6822
|
+
*/
|
|
6823
|
+
declare const NODE_BUDGET = 64;
|
|
6824
|
+
/**
|
|
6825
|
+
* Above this vault size, {@link capHopsForDensity} forces `MAX_HOPS = 1`
|
|
6826
|
+
* (seed-only). Fan-out grows with graph density, so on large vaults we don't
|
|
6827
|
+
* pay the expansion cost. Bounded-traversal safety valve.
|
|
6828
|
+
* @public
|
|
6829
|
+
*/
|
|
6830
|
+
declare const VAULT_SIZE_HOP_CAP = 1000;
|
|
6831
|
+
/** Options for {@link traverseGraphLane}. All optional; defaults are the
|
|
6832
|
+
* exported constants above. Exposed for ablation / evaluation sweeps.
|
|
6833
|
+
* @public */
|
|
6834
|
+
interface GraphTraversalOptions {
|
|
6835
|
+
/** Total hops incl. the seed lookup (hop 1). Default {@link MAX_HOPS}. */
|
|
6836
|
+
maxHops?: number;
|
|
6837
|
+
/** Max neighbor entities expanded per hop. Default {@link ENTITY_FANOUT}. */
|
|
6838
|
+
entityFanout?: number;
|
|
6839
|
+
/** Hard cap on accumulated memory IDs. Default {@link NODE_BUDGET}. */
|
|
6840
|
+
nodeBudget?: number;
|
|
6841
|
+
/** RRF smoothing constant for per-hop fusion. Default 60 (rrf.ts). */
|
|
6842
|
+
rrfK?: number;
|
|
6843
|
+
/**
|
|
6844
|
+
* Vault size hint. When provided and above {@link VAULT_SIZE_HOP_CAP}, the
|
|
6845
|
+
* effective hop count is capped to 1 (see {@link capHopsForDensity}).
|
|
6846
|
+
*/
|
|
6847
|
+
vaultSize?: number;
|
|
6848
|
+
/**
|
|
6849
|
+
* PR5 — optional LLM neighbor-selection. When provided, at each expansion hop
|
|
6850
|
+
* the deterministically-ranked candidate neighbor entities are handed to this
|
|
6851
|
+
* refiner, which returns the subset to expand. Falls back to the
|
|
6852
|
+
* co-occurrence order on any error or empty result. Called at most ONCE per
|
|
6853
|
+
* hop. Build one with {@link createLlmNeighborRefiner}, or supply your own.
|
|
6854
|
+
*/
|
|
6855
|
+
refineNeighbors?: NeighborRefiner;
|
|
6856
|
+
/**
|
|
6857
|
+
* Resolve a batch of candidate memory ids to just the ACTIVE ones (not
|
|
6858
|
+
* archived, not quarantined, not soft-deleted — the same set the final recall
|
|
6859
|
+
* gate admits). When provided, traversal drops "forgotten" memories from the
|
|
6860
|
+
* frontier AT EACH HOP so they neither steer neighbor-entity ranking nor
|
|
6861
|
+
* egress their entity names to {@link refineNeighbors}. Omit (tests /
|
|
6862
|
+
* entity-only callers) to traverse over every linked memory. Wire it from a
|
|
6863
|
+
* vault context with {@link ../db/memoryVault/operations.getActiveVaultMemoryIdsOp}.
|
|
6864
|
+
*/
|
|
6865
|
+
filterActiveMemoryIds?: (ids: string[]) => Promise<Set<string>>;
|
|
6866
|
+
}
|
|
6867
|
+
/**
|
|
6868
|
+
* Picks which candidate neighbor entities to expand at a traversal hop. Given
|
|
6869
|
+
* the query and the deterministically-ranked candidate entity names, return the
|
|
6870
|
+
* subset (≤ `limit`) to expand. Must be resilient: {@link traverseGraphLane}
|
|
6871
|
+
* falls back to the deterministic top-`limit` on a throw or empty return.
|
|
6872
|
+
* @public
|
|
6873
|
+
*/
|
|
6874
|
+
interface NeighborRefiner {
|
|
6875
|
+
refine(query: string, candidates: string[], limit: number): Promise<string[]>;
|
|
6876
|
+
}
|
|
6877
|
+
/**
|
|
6878
|
+
* Cap the hop count on large vaults. Fan-out grows with graph density, so above
|
|
6879
|
+
* {@link VAULT_SIZE_HOP_CAP} memories we force seed-only traversal (1 hop)
|
|
6880
|
+
* rather than pay an unbounded expansion. A no-op when `vaultSize` is unknown or
|
|
6881
|
+
* within the threshold.
|
|
6882
|
+
* @public
|
|
6883
|
+
*/
|
|
6884
|
+
declare function capHopsForDensity(maxHops: number, vaultSize?: number): number;
|
|
6885
|
+
/**
|
|
6886
|
+
* Bounded multi-hop entity-graph traversal. Returns an ordered list of memory
|
|
6887
|
+
* IDs (best first) — the SAME output shape as the single-hop lane, so nothing
|
|
6888
|
+
* downstream changes. The caller passes it through as `entityRanking` for RRF
|
|
6889
|
+
* fusion with the cosine/BM25 head.
|
|
6890
|
+
*
|
|
6891
|
+
* With `maxHops <= 1` this returns the seed ordering verbatim, making it a
|
|
6892
|
+
* drop-in equivalent of the single-hop lane (the regression guard). The PR5
|
|
6893
|
+
* default is 2 (one expansion beyond the seed).
|
|
6894
|
+
*
|
|
6895
|
+
* Returns an empty array when the query has no extractable entities or no stored
|
|
6896
|
+
* memory shares a seed entity.
|
|
6897
|
+
*
|
|
6898
|
+
* @public
|
|
6899
|
+
*/
|
|
6900
|
+
declare function traverseGraphLane(query: string, entityCtx: EntityOperationsContext, options?: GraphTraversalOptions): Promise<string[]>;
|
|
6901
|
+
/** Auth + tuning for {@link createLlmNeighborRefiner}. Reuses the recall
|
|
6902
|
+
* `decomposeOptions` shape (dual auth — one of `apiKey`/`getToken`). @public */
|
|
6903
|
+
interface LlmNeighborRefinerOptions extends PortalLlmAuth {
|
|
6904
|
+
baseUrl?: string;
|
|
6905
|
+
model?: string;
|
|
6906
|
+
fetchFn?: typeof fetch;
|
|
6907
|
+
maxAttempts?: number;
|
|
6908
|
+
totalTimeoutMs?: number;
|
|
6909
|
+
backoffMs?: (attempt: number) => number;
|
|
6910
|
+
}
|
|
6911
|
+
/**
|
|
6912
|
+
* Build a {@link NeighborRefiner} backed by a cheap portal LLM. At each hop it
|
|
6913
|
+
* asks the model to pick, from the candidate neighbor entities, the ≤`limit`
|
|
6914
|
+
* most relevant to the query, so traversal expands toward the question instead
|
|
6915
|
+
* of purely by co-occurrence frequency.
|
|
6916
|
+
*
|
|
6917
|
+
* Bounded + fail-safe: one attempt by default, a short total timeout, and
|
|
6918
|
+
* {@link traverseGraphLane} falls back to the deterministic co-occurrence order
|
|
6919
|
+
* on any throw or empty result — so enabling this can reorder which neighbors
|
|
6920
|
+
* expand but never breaks or stalls recall.
|
|
6921
|
+
*
|
|
6922
|
+
* SECURITY / ZERO-KNOWLEDGE (must stay default-OFF): this sends the query +
|
|
6923
|
+
* candidate ENTITY NAMES to the portal UNREDACTED. Those names ARE user PII —
|
|
6924
|
+
* people, places, orgs pulled from the stored graph (e.g. "Sara", "Kyoto",
|
|
6925
|
+
* "Acme") — not lower-risk than content just because they're short. It reuses
|
|
6926
|
+
* the query-decompose auth and is opt-in (`RecallOptions.graphRefine`, default
|
|
6927
|
+
* off in recall); leave it off unless you accept that exposure. To bound that
|
|
6928
|
+
* exposure, {@link traverseGraphLane} caps the candidate list it hands this
|
|
6929
|
+
* refiner per hop — at most `MAX_REFINER_CANDIDATES` entity names ever leave per
|
|
6930
|
+
* hop, REGARDLESS of `entityFanout` (the cap is a hard ceiling, not just a
|
|
6931
|
+
* fanout-scaled floor), so the full frontier is never egressed.
|
|
6932
|
+
*
|
|
6933
|
+
* MEDIUM residual: a malicious / MITM'd portal can only steer WHICH neighbor
|
|
6934
|
+
* entities expand — a recall-ranking nudge, not a data-integrity change (no
|
|
6935
|
+
* memory is written, archived, or deleted), and {@link traverseGraphLane}
|
|
6936
|
+
* falls back to deterministic order on any bad/empty response. Bounded tradeoff.
|
|
6937
|
+
* @public
|
|
6938
|
+
*/
|
|
6939
|
+
declare function createLlmNeighborRefiner(options: LlmNeighborRefinerOptions): NeighborRefiner;
|
|
6940
|
+
|
|
5983
6941
|
/**
|
|
5984
6942
|
* Unified Recall API — single retrieval surface above vault + engine.
|
|
5985
6943
|
*
|
|
@@ -7133,6 +8091,9 @@ declare function useChatStorage(options: UseChatStorageOptions): UseChatStorageR
|
|
|
7133
8091
|
* - v33: Added embedding_model column to memory_vault so stale-model vectors are
|
|
7134
8092
|
* detectable and re-embeddable after an embedding-model change (null = legacy
|
|
7135
8093
|
* rows, grandfathered as compatible with the current model)
|
|
8094
|
+
* - v34: Added topics_user_managed column to memory_vault so a memory whose
|
|
8095
|
+
* entity links the user has taken manual control of is left alone by
|
|
8096
|
+
* auto-extraction (null/false = auto-derived, the default)
|
|
7136
8097
|
* - v35: Added conversation_memory table recording which vault memories a
|
|
7137
8098
|
* conversation drew on, so the conversation-level Memories panel survives reload
|
|
7138
8099
|
* - v36: Added topics_extracted_at column to memory_vault — watermark of the last
|
|
@@ -7151,8 +8112,13 @@ declare function useChatStorage(options: UseChatStorageOptions): UseChatStorageR
|
|
|
7151
8112
|
* watermark stamped each time retain() merges into an existing fact, kept
|
|
7152
8113
|
* distinct from updated_at (which merges preserve). Lets profile synthesis
|
|
7153
8114
|
* weight facts by recency of reinforcement rather than last edit.
|
|
8115
|
+
* - v40: Added fact_type, archived_at, trust_tier columns to memory_vault for
|
|
8116
|
+
* typed memory + decay + Tier-0 security. All nullable + plaintext, no
|
|
8117
|
+
* backfill (null = legacy/untyped, active, un-screened — content is
|
|
8118
|
+
* encrypted so in-migration classification is impossible; NULL = zero-risk,
|
|
8119
|
+
* exact embedding_model precedent)
|
|
7154
8120
|
*/
|
|
7155
|
-
declare const SDK_SCHEMA_VERSION =
|
|
8121
|
+
declare const SDK_SCHEMA_VERSION = 40;
|
|
7156
8122
|
/**
|
|
7157
8123
|
* Combined WatermelonDB schema for all SDK storage modules.
|
|
7158
8124
|
*
|
|
@@ -7230,11 +8196,13 @@ declare const sdkSchema: Readonly<{
|
|
|
7230
8196
|
* - v30 → v31: Added `user_id` column to memory_entity for multi-user scoping of the W5 graph lane (with backfill from memory_vault.user_id)
|
|
7231
8197
|
* - v31 → v32: Added `pinned_at` column to conversations for pinning chats
|
|
7232
8198
|
* - v32 → v33: Added `embedding_model` column to memory_vault (null grandfathered as current-model-compatible)
|
|
8199
|
+
* - v33 → v34: Added `topics_user_managed` column to memory_vault (null/false = auto-derived topics, the default)
|
|
7233
8200
|
* - v34 → v35: Added `conversation_memory` table (conversation ↔ recalled memory ids)
|
|
7234
8201
|
* - v35 → v36: Added `topics_extracted_at` column to memory_vault (watermark for the background topic-extraction worker; null + existing links grandfathered as extracted)
|
|
7235
8202
|
* - v36 → v37: Added `superseded_by` + `superseded_at` columns to memory_vault (write-time supersession; null = live, excluded from recall/dedup when set)
|
|
7236
8203
|
* - v37 → v38: Added `topics_extracted_version` column to memory_vault (extraction-logic version; null read as 0 so a TOPICS_EXTRACTION_VERSION bump re-extracts stale rows)
|
|
7237
8204
|
* - v38 → v39: Added `last_observed_at` column to memory_vault (C3 re-observation watermark; stamped on retain merge, distinct from updated_at)
|
|
8205
|
+
* - v39 → v40: Added `fact_type`, `archived_at`, `trust_tier` columns to memory_vault for typed memory + decay + Tier-0 security (all nullable + plaintext, NULL backfill)
|
|
7238
8206
|
*/
|
|
7239
8207
|
declare const sdkMigrations: Readonly<{
|
|
7240
8208
|
validated: true;
|
|
@@ -7756,4 +8724,4 @@ declare function refreshNotionAccessToken(params: NotionRefreshTokenParams): Pro
|
|
|
7756
8724
|
*/
|
|
7757
8725
|
declare function createNotionTools(getAccessToken: () => string | null, requestNotionAccess: () => Promise<string>): ToolConfig[];
|
|
7758
8726
|
|
|
7759
|
-
export { type AutoExtractMessage, type AutoExtractor, type Budget, type CachedChunkVectors, type CachedServerTools, Conversation as ChatConversation, Message as ChatMessage, type ChatRole, type ChunkVectorCache, type ChunkingOptions, type ConsolidationFallbackReason, type ConversationMemoryInput, ConversationMemory as ConversationMemoryModel, type ConversationMemoryOperationsContext, type CreateAutoExtractorOptions, type CreateConversationOptions, type CreateMessageOptions, type CreateVaultFolderOptions, type CreateVaultMemoryOptions, type CryptoPriceClassification, type CryptoPricePreProcessorOptions, DEFAULT_CACHE_EXPIRATION_MS, DEFAULT_CHUNK_CACHE_SIZE, DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE, DEFAULT_MIN_CHUNK_SIZE, DEFAULT_PROFILE_FACETS, DEFAULT_VAULT_CACHE_SIZE, DatabaseManager, type DatabaseManagerLogger, type DatabaseManagerOptions, type EmbeddedWalletSignerFn, type ExtractFactsOptions, type ExtractOutcome, type ExtractedCandidate, type ExtractedEntity, type ExtractionCursorStore, type FactType, type FileMetadata, type FlushResult, type GetConversationsPageOptions, type GetMessagesPageOptions, INFERENCE_ID_HEADER, type LazyStoredConversation, type Logger, LoggerProvider, type LoggerProviderProps, type MemoriesNeedingTopicExtraction, type EmbeddingOptions as MemoryEngineEmbeddingOptions, type MemoryEngineResult, type MemoryEngineSearchOptions, type MemoryExtractedEvent, type MemoryKind, type MemoryVaultSearchOptions, type MemoryVaultToolOptions, type MessageRedactionResult, type MessageSkeleton, NOTION_OAUTH_CONFIG, type NotionAuthUrlParams, type NotionClientRegistration, type NotionExchangeCodeParams, type NotionOAuthEndpoints, type NotionPKCEChallenge, type NotionRefreshTokenParams, type NotionTokenResponse, PII_PATTERNS, PROFILE_DOC_VERSION, type PiiCategory, type PiiMatch, type PiiPattern, PiiRedactor, type PiiRedactorOptions, type PlatformStorage, type PortalLlmAuth, type ProfileConfigFingerprint, type ProfileDoc, type ProfileFacet, type ProfileFacetKey, type ProfileSection, type PromptPreProcessor, type PromptPreProcessorContext, type QuantizedEmbedding, QueueManager, type QueueStatus, RECALL_MAX_LIMIT, RECALL_TOOL_NAME, type RankedMemory, type RecallContext, type RecallDegradation, type RecallDiagnostics, type RecallOptions, type RecallResult, type RecallToolCallbacks, type RecallToolOptions, type RecencyOptions, type RedactionResult, type ReflectOptions, type ReflectResult, RerankerUnavailableError, type ResumeStreamOptions, type ResumeStreamResult, type ResumeStreamWithStorageResult, type RetainAction, type RetainContext, type RetainOptions, type RetainResult, type RetainSource, SDK_SCHEMA_VERSION, STREAM_RESUMABLE_HEADER, type ScoreBreakdown, type SearchSource, type SendMessageWithStorageArgs, type SendMessageWithStorageDetachedResult, type SendMessageWithStorageResult, type ServerToolsOptions, type ServerToolsResponse, type SignMessageFn, type StockPriceClassification, type StockPricePreProcessorOptions, type ChatCompletionUsage as StoredChatCompletionUsage, type StoredConversation, type StoredConversationMemory, type StoredMessage, type StoredMessageWithSimilarity, type StoredVaultFolder, VaultFolder as StoredVaultFolderModel, type StoredVaultMemory, VaultMemory as StoredVaultMemoryModel, StreamExpiredError, type StreamMetaEvent, type StreamResumeHandle, type SynthesizeProfileOptions, TOPICS_EXTRACTION_VERSION, TOPIC_EXTRACTION_BATCH_SIZE, type TextChunk, type TopicExtractOptions, type TopicExtractionInput, type TopicExtractionRunResult, type TurnCompleteEvent, type TurnSkippedEvent, type UpdateVaultFolderOptions, type UpdateVaultMemoryOptions, type UseChatStorageOptions, type UseChatStorageResult, type UseCreditsOptions, type UseCreditsResult, type UseModelsOptions, type UseModelsResult, type VaultEmbeddingCache, type VaultFolderOperationsContext, type VaultMemoryOperationsContext, type VaultSaveOperation, type VaultSearchResult, WalletPoller, type WeatherClassification, type WeatherPreProcessorOptions, type WebSearchClassification, type WebSearchPreProcessorOptions, addConversationMemoriesOp, buildNotionAuthUrl, chatStorageMigrations, chatStorageSchema, chunkAndEmbedAllMessages, chunkAndEmbedMessage, chunkText, classifyCryptoPrice, classifyCryptoPriceBatch, classifyStockPrice, classifyStockPriceBatch, classifyWeather, classifyWeatherBatch, classifyWebSearch, classifyWebSearchBatch, clearAllEncryptionKeys, clearAllEncryptionState, clearConversationMemoriesOp, clearEncryptionKey, clearLazyTitleCache, clearServerToolsCache, consoleLogger, conversationMemoryToStored, cosineInt8, createAutoExtractor, createChunkVectorCache, createCryptoPricePreProcessor, createMemoryEngineTool, createMemoryVaultSearchTool, createMemoryVaultTool, createNotionTools, createPlatformCursorStore, createRecallTool, createStockPricePreProcessor, createStreamingDeAnonymizer, createVaultEmbeddingCache, createVaultFolderOp, createVaultMemoriesBatchOp, createVaultMemoryOp, createWeatherPreProcessor, createWebSearchPreProcessor, decryptConversationTitle, deleteAllVaultMemoriesForUserOp, deleteVaultFolderOp, deleteVaultMemoryOp, dequantizeEmbedding, discoverNotionOAuthEndpoints, eagerEmbedContent, embedAllMessages, embedMessage, ensureDefaultFoldersOp, exchangeNotionCode, extractAndLinkEntitiesForMemoriesOp, extractAndRetain, extractEntitiesForMemories, extractFacts, generateConversationId, generateEmbedding, generateEmbeddings, generateNotionPKCE, getAllVaultFoldersOp, getAllVaultMemoriesOp, getAllVaultMemoryContentsOp, getCachedServerTools, getConversationMemoriesOp, getConversationsByProjectLazyOp, getConversationsLazyOp, getConversationsPageOp, getLogger, getMemoriesNeedingTopicExtractionOp, getServerTools, getUnfiledVaultMemoriesOp, getVaultFolderMemoryCountOp, getVaultMemoryOp, hasEncryptionKey, isPiiRedactor, isRerankerAvailable, moveMemoriesToFolderOp, noopLogger, onKeyAvailable, preEmbedVaultMemories, quantizeEmbedding, queueManager, recall, reflect, refreshNotionAccessToken, registerNotionClient, requestEncryptionKey, resolvePiiRedactor, resumeStream, retain, sdkMigrations, sdkModelClasses, sdkSchema, searchVaultMemories, setLogger, shouldChunkMessage, stampTopicsExtractedAtOp, streamCancelPath, streamReplayPath, supersedeVaultMemoryOp, synthesizeProfile, updateVaultFolderContextOp, updateVaultFolderOp, updateVaultMemoryEmbeddingOp, updateVaultMemoryOp, upsertMessageOp, useChat, useChatStorage, useCredits, useEncryption, useModels, xhrTransport };
|
|
8727
|
+
export { type AutoExtractMessage, type AutoExtractor, type Budget, type CachedChunkVectors, type CachedServerTools, Conversation as ChatConversation, Message as ChatMessage, type ChatRole, type ChunkVectorCache, type ChunkingOptions, type ConsolidationFallbackReason, type ConversationMemoryInput, ConversationMemory as ConversationMemoryModel, type ConversationMemoryOperationsContext, type CreateAutoExtractorOptions, type CreateConversationOptions, type CreateDecaySweeperOptions, type CreateMessageOptions, type CreateVaultFolderOptions, type CreateVaultMemoryOptions, type CryptoPriceClassification, type CryptoPricePreProcessorOptions, DEFAULT_CACHE_EXPIRATION_MS, DEFAULT_CHUNK_CACHE_SIZE, DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE, DEFAULT_DECAY_POLICY, DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP, DEFAULT_MIN_CHUNK_SIZE, DEFAULT_PROFILE_FACETS, DEFAULT_VAULT_CACHE_SIZE, DatabaseManager, type DatabaseManagerLogger, type DatabaseManagerOptions, type DecayCandidateRaw, type DecayClassifier, type DecayInput, type DecayPolicy, type DecaySweepResult, type DecaySweeper, type DecayVerdict, ENTITY_FANOUT, type EmbeddedWalletSignerFn, type ExtractFactsOptions, type ExtractOutcome, type ExtractedCandidate, type ExtractedEntity, type ExtractionCursorStore, type FactType, type FileMetadata, type FlushResult, type GetConversationsPageOptions, type GetMessagesPageOptions, type GraphTraversalOptions, HARD_DELETE_WINDOW_MS, INFERENCE_ID_HEADER, type InjectionClassifierOptions, type InjectionReason, type LazyStoredConversation, type LlmDecayClassifierOptions, type LlmNeighborRefinerOptions, type Logger, LoggerProvider, type LoggerProviderProps, MAX_HOPS, MEDIUM_TTL_MS, type MemoriesNeedingTopicExtraction, type EmbeddingOptions as MemoryEngineEmbeddingOptions, type MemoryEngineResult, type MemoryEngineSearchOptions, type MemoryExtractedEvent, type MemoryKind, type MemoryQuarantinedEvent, type MemoryVaultSearchOptions, type MemoryVaultToolOptions, type MessageRedactionResult, type MessageSkeleton, NEVER_TTL_MS, NODE_BUDGET, NOTION_OAUTH_CONFIG, type NeighborRefiner, type NotionAuthUrlParams, type NotionClientRegistration, type NotionExchangeCodeParams, type NotionOAuthEndpoints, type NotionPKCEChallenge, type NotionRefreshTokenParams, type NotionTokenResponse, type NowSource, PAST_EVENT_GRACE_MS, PII_PATTERNS, PROFILE_DOC_VERSION, type PiiCategory, type PiiMatch, type PiiPattern, PiiRedactor, type PiiRedactorOptions, type PlatformStorage, type PortalLlmAuth, type ProfileConfigFingerprint, type ProfileDoc, type ProfileFacet, type ProfileFacetKey, type ProfileSection, type PromptPreProcessor, type PromptPreProcessorContext, type QuantizedEmbedding, type QuarantinedMemoryInfo, QueueManager, type QueueStatus, RECALL_MAX_LIMIT, RECALL_TOOL_NAME, type RankedMemory, type RecallContext, type RecallDegradation, type RecallDiagnostics, type RecallOptions, type RecallResult, type RecallToolCallbacks, type RecallToolOptions, type RecencyOptions, type RedactionResult, type ReflectOptions, type ReflectResult, RerankerUnavailableError, type ResumeStreamOptions, type ResumeStreamResult, type ResumeStreamWithStorageResult, type RetainAction, type RetainContext, type RetainOptions, type RetainResult, type RetainSource, SDK_SCHEMA_VERSION, SHORT_TTL_MS, STREAM_RESUMABLE_HEADER, type ScoreBreakdown, type ScreenResult, type ScreenedCandidate, type SearchSource, type SendMessageWithStorageArgs, type SendMessageWithStorageDetachedResult, type SendMessageWithStorageResult, type ServerToolsOptions, type ServerToolsResponse, type SignMessageFn, type StockPriceClassification, type StockPricePreProcessorOptions, type ChatCompletionUsage as StoredChatCompletionUsage, type StoredConversation, type StoredConversationMemory, type StoredMessage, type StoredMessageWithSimilarity, type StoredVaultFolder, VaultFolder as StoredVaultFolderModel, type StoredVaultMemory, VaultMemory as StoredVaultMemoryModel, StreamExpiredError, type StreamMetaEvent, type StreamResumeHandle, type SynthesizeProfileOptions, TOPICS_EXTRACTION_VERSION, TOPIC_EXTRACTION_BATCH_SIZE, type TextChunk, type TopicExtractOptions, type TopicExtractionInput, type TopicExtractionRunResult, type TurnCompleteEvent, type TurnSkippedEvent, type UpdateVaultFolderOptions, type UpdateVaultMemoryOptions, type UseChatStorageOptions, type UseChatStorageResult, type UseCreditsOptions, type UseCreditsResult, type UseModelsOptions, type UseModelsResult, VAULT_SIZE_HOP_CAP, type VaultEmbeddingCache, type VaultFolderOperationsContext, type VaultMemoryOperationsContext, type VaultSaveOperation, type VaultSearchResult, WalletPoller, type WeatherClassification, type WeatherPreProcessorOptions, type WebSearchClassification, type WebSearchPreProcessorOptions, addConversationMemoriesOp, archiveVaultMemoryOp, buildNotionAuthUrl, capHopsForDensity, chatStorageMigrations, chatStorageSchema, chunkAndEmbedAllMessages, chunkAndEmbedMessage, chunkText, classifyCryptoPrice, classifyCryptoPriceBatch, classifyDecay, classifyInjectionCandidates, classifyStockPrice, classifyStockPriceBatch, classifyWeather, classifyWeatherBatch, classifyWebSearch, classifyWebSearchBatch, clearAllEncryptionKeys, clearAllEncryptionState, clearConversationMemoriesOp, clearEncryptionKey, clearLazyTitleCache, clearServerToolsCache, consoleLogger, conversationMemoryToStored, cosineInt8, createAutoExtractor, createChunkVectorCache, createCryptoPricePreProcessor, createDecaySweeper, createLlmDecayClassifier, createLlmNeighborRefiner, createMemoryEngineTool, createMemoryVaultSearchTool, createMemoryVaultTool, createNotionTools, createPlatformCursorStore, createRecallTool, createStockPricePreProcessor, createStreamingDeAnonymizer, createVaultEmbeddingCache, createVaultFolderOp, createVaultMemoriesBatchOp, createVaultMemoryOp, createWeatherPreProcessor, createWebSearchPreProcessor, decryptConversationTitle, deleteAllVaultMemoriesForUserOp, deleteVaultFolderOp, deleteVaultMemoryOp, dequantizeEmbedding, discoverNotionOAuthEndpoints, eagerEmbedContent, embedAllMessages, embedMessage, ensureDefaultFoldersOp, exchangeNotionCode, extractAndLinkEntitiesForMemoriesOp, extractAndRetain, extractEntitiesForMemories, extractFacts, generateConversationId, generateEmbedding, generateEmbeddings, generateNotionPKCE, getAllVaultFoldersOp, getAllVaultMemoriesOp, getAllVaultMemoryContentsOp, getCachedServerTools, getConversationMemoriesOp, getConversationsByProjectLazyOp, getConversationsLazyOp, getConversationsPageOp, getDecayCandidatesRawOp, getLogger, getMemoriesNeedingTopicExtractionOp, getServerTools, getUnfiledVaultMemoriesOp, getVaultFolderMemoryCountOp, getVaultMemoryOp, hardDeleteDecayedOp, hasEncryptionKey, injectionSignatureCatalog, isPiiRedactor, isRerankerAvailable, moveMemoriesToFolderOp, noopLogger, onKeyAvailable, preEmbedVaultMemories, quantizeEmbedding, queueManager, recall, reflect, refreshNotionAccessToken, registerNotionClient, requestEncryptionKey, resolvePiiRedactor, restoreVaultMemoryOp, resumeStream, retain, screenCandidatesForInjection, sdkMigrations, sdkModelClasses, sdkSchema, searchVaultMemories, setLogger, shouldChunkMessage, stampTopicsExtractedAtOp, streamCancelPath, streamReplayPath, supersedeVaultMemoryOp, synthesizeProfile, traverseGraphLane, ttlForType, updateVaultFolderContextOp, updateVaultFolderOp, updateVaultMemoryEmbeddingOp, updateVaultMemoryOp, upsertMessageOp, useChat, useChatStorage, useCredits, useEncryption, useModels, xhrTransport };
|