mem-os-python 1.6.27__py3-none-any.whl
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.
- mem_os_python-1.6.27.dist-info/METADATA +1032 -0
- mem_os_python-1.6.27.dist-info/RECORD +253 -0
- mem_os_python-1.6.27.dist-info/WHEEL +4 -0
- mem_os_python-1.6.27.dist-info/entry_points.txt +2 -0
- mem_os_python-1.6.27.dist-info/licenses/LICENSE +21 -0
- memos/__init__.py +16 -0
- memos/_js/adapters/anthropic.d.ts +279 -0
- memos/_js/adapters/anthropic.d.ts.map +1 -0
- memos/_js/adapters/anthropic.js +348 -0
- memos/_js/adapters/anthropic.js.map +1 -0
- memos/_js/adapters/base.d.ts +10 -0
- memos/_js/adapters/base.d.ts.map +1 -0
- memos/_js/adapters/base.js +2 -0
- memos/_js/adapters/base.js.map +1 -0
- memos/_js/adapters/openai.d.ts +264 -0
- memos/_js/adapters/openai.d.ts.map +1 -0
- memos/_js/adapters/openai.js +342 -0
- memos/_js/adapters/openai.js.map +1 -0
- memos/_js/apps/explorer.d.ts +244 -0
- memos/_js/apps/explorer.d.ts.map +1 -0
- memos/_js/apps/explorer.js +738 -0
- memos/_js/apps/explorer.js.map +1 -0
- memos/_js/citations.d.ts +65 -0
- memos/_js/citations.d.ts.map +1 -0
- memos/_js/citations.js +143 -0
- memos/_js/citations.js.map +1 -0
- memos/_js/cli-browse.d.ts +3 -0
- memos/_js/cli-browse.d.ts.map +1 -0
- memos/_js/cli-browse.js +129 -0
- memos/_js/cli-browse.js.map +1 -0
- memos/_js/cli-completion.d.ts +31 -0
- memos/_js/cli-completion.d.ts.map +1 -0
- memos/_js/cli-completion.js +340 -0
- memos/_js/cli-completion.js.map +1 -0
- memos/_js/cli-help.d.ts +18 -0
- memos/_js/cli-help.d.ts.map +1 -0
- memos/_js/cli-help.js +357 -0
- memos/_js/cli-help.js.map +1 -0
- memos/_js/cli.d.ts +28 -0
- memos/_js/cli.d.ts.map +1 -0
- memos/_js/cli.js +2880 -0
- memos/_js/cli.js.map +1 -0
- memos/_js/compression.d.ts +25 -0
- memos/_js/compression.d.ts.map +1 -0
- memos/_js/compression.js +319 -0
- memos/_js/compression.js.map +1 -0
- memos/_js/confidence-machine.d.ts +99 -0
- memos/_js/confidence-machine.d.ts.map +1 -0
- memos/_js/confidence-machine.js +222 -0
- memos/_js/confidence-machine.js.map +1 -0
- memos/_js/context-pack.d.ts +506 -0
- memos/_js/context-pack.d.ts.map +1 -0
- memos/_js/context-pack.js +1435 -0
- memos/_js/context-pack.js.map +1 -0
- memos/_js/contradictions.d.ts +160 -0
- memos/_js/contradictions.d.ts.map +1 -0
- memos/_js/contradictions.js +255 -0
- memos/_js/contradictions.js.map +1 -0
- memos/_js/dashboard.d.ts +30 -0
- memos/_js/dashboard.d.ts.map +1 -0
- memos/_js/dashboard.js +302 -0
- memos/_js/dashboard.js.map +1 -0
- memos/_js/ebbinghaus.d.ts +29 -0
- memos/_js/ebbinghaus.d.ts.map +1 -0
- memos/_js/ebbinghaus.js +32 -0
- memos/_js/ebbinghaus.js.map +1 -0
- memos/_js/embedding-queue.d.ts +190 -0
- memos/_js/embedding-queue.d.ts.map +1 -0
- memos/_js/embedding-queue.js +316 -0
- memos/_js/embedding-queue.js.map +1 -0
- memos/_js/embeddings.d.ts +187 -0
- memos/_js/embeddings.d.ts.map +1 -0
- memos/_js/embeddings.js +633 -0
- memos/_js/embeddings.js.map +1 -0
- memos/_js/entity-extraction.d.ts +54 -0
- memos/_js/entity-extraction.d.ts.map +1 -0
- memos/_js/entity-extraction.js +325 -0
- memos/_js/entity-extraction.js.map +1 -0
- memos/_js/entity-index.d.ts +93 -0
- memos/_js/entity-index.d.ts.map +1 -0
- memos/_js/entity-index.js +165 -0
- memos/_js/entity-index.js.map +1 -0
- memos/_js/event-memory.d.ts +147 -0
- memos/_js/event-memory.d.ts.map +1 -0
- memos/_js/event-memory.js +290 -0
- memos/_js/event-memory.js.map +1 -0
- memos/_js/external-import.d.ts +98 -0
- memos/_js/external-import.d.ts.map +1 -0
- memos/_js/external-import.js +669 -0
- memos/_js/external-import.js.map +1 -0
- memos/_js/fidelity.d.ts +367 -0
- memos/_js/fidelity.d.ts.map +1 -0
- memos/_js/fidelity.js +911 -0
- memos/_js/fidelity.js.map +1 -0
- memos/_js/graph-expansion.d.ts +119 -0
- memos/_js/graph-expansion.d.ts.map +1 -0
- memos/_js/graph-expansion.js +213 -0
- memos/_js/graph-expansion.js.map +1 -0
- memos/_js/graph-mermaid.d.ts +13 -0
- memos/_js/graph-mermaid.d.ts.map +1 -0
- memos/_js/graph-mermaid.js +32 -0
- memos/_js/graph-mermaid.js.map +1 -0
- memos/_js/graph.d.ts +170 -0
- memos/_js/graph.d.ts.map +1 -0
- memos/_js/graph.js +482 -0
- memos/_js/graph.js.map +1 -0
- memos/_js/harness.d.ts +63 -0
- memos/_js/harness.d.ts.map +1 -0
- memos/_js/harness.js +93 -0
- memos/_js/harness.js.map +1 -0
- memos/_js/importance.d.ts +118 -0
- memos/_js/importance.d.ts.map +1 -0
- memos/_js/importance.js +144 -0
- memos/_js/importance.js.map +1 -0
- memos/_js/index.d.ts +51 -0
- memos/_js/index.d.ts.map +1 -0
- memos/_js/index.js +53 -0
- memos/_js/index.js.map +1 -0
- memos/_js/mcp.d.ts +58 -0
- memos/_js/mcp.d.ts.map +1 -0
- memos/_js/mcp.js +1853 -0
- memos/_js/mcp.js.map +1 -0
- memos/_js/memory.d.ts +1762 -0
- memos/_js/memory.d.ts.map +1 -0
- memos/_js/memory.js +6283 -0
- memos/_js/memory.js.map +1 -0
- memos/_js/procedural.d.ts +74 -0
- memos/_js/procedural.d.ts.map +1 -0
- memos/_js/procedural.js +125 -0
- memos/_js/procedural.js.map +1 -0
- memos/_js/project-config.d.ts +68 -0
- memos/_js/project-config.d.ts.map +1 -0
- memos/_js/project-config.js +59 -0
- memos/_js/project-config.js.map +1 -0
- memos/_js/proof-of-forgetting.d.ts +57 -0
- memos/_js/proof-of-forgetting.d.ts.map +1 -0
- memos/_js/proof-of-forgetting.js +80 -0
- memos/_js/proof-of-forgetting.js.map +1 -0
- memos/_js/provenance.d.ts +122 -0
- memos/_js/provenance.d.ts.map +1 -0
- memos/_js/provenance.js +182 -0
- memos/_js/provenance.js.map +1 -0
- memos/_js/quarantine-commands.d.ts +23 -0
- memos/_js/quarantine-commands.d.ts.map +1 -0
- memos/_js/quarantine-commands.js +42 -0
- memos/_js/quarantine-commands.js.map +1 -0
- memos/_js/quarantine.d.ts +120 -0
- memos/_js/quarantine.d.ts.map +1 -0
- memos/_js/quarantine.js +873 -0
- memos/_js/quarantine.js.map +1 -0
- memos/_js/redact.d.ts +12 -0
- memos/_js/redact.d.ts.map +1 -0
- memos/_js/redact.js +26 -0
- memos/_js/redact.js.map +1 -0
- memos/_js/repl.d.ts +57 -0
- memos/_js/repl.d.ts.map +1 -0
- memos/_js/repl.js +295 -0
- memos/_js/repl.js.map +1 -0
- memos/_js/rerank-local.d.ts +61 -0
- memos/_js/rerank-local.d.ts.map +1 -0
- memos/_js/rerank-local.js +210 -0
- memos/_js/rerank-local.js.map +1 -0
- memos/_js/retain-filter.d.ts +98 -0
- memos/_js/retain-filter.d.ts.map +1 -0
- memos/_js/retain-filter.js +232 -0
- memos/_js/retain-filter.js.map +1 -0
- memos/_js/retrieval.d.ts +265 -0
- memos/_js/retrieval.d.ts.map +1 -0
- memos/_js/retrieval.js +1018 -0
- memos/_js/retrieval.js.map +1 -0
- memos/_js/revert.d.ts +77 -0
- memos/_js/revert.d.ts.map +1 -0
- memos/_js/revert.js +252 -0
- memos/_js/revert.js.map +1 -0
- memos/_js/scope.d.ts +30 -0
- memos/_js/scope.d.ts.map +1 -0
- memos/_js/scope.js +71 -0
- memos/_js/scope.js.map +1 -0
- memos/_js/semantic-screen.d.ts +152 -0
- memos/_js/semantic-screen.d.ts.map +1 -0
- memos/_js/semantic-screen.js +290 -0
- memos/_js/semantic-screen.js.map +1 -0
- memos/_js/session-hooks.d.ts +127 -0
- memos/_js/session-hooks.d.ts.map +1 -0
- memos/_js/session-hooks.js +482 -0
- memos/_js/session-hooks.js.map +1 -0
- memos/_js/storage/sqlite.d.ts +427 -0
- memos/_js/storage/sqlite.d.ts.map +1 -0
- memos/_js/storage/sqlite.js +2428 -0
- memos/_js/storage/sqlite.js.map +1 -0
- memos/_js/sync.d.ts +213 -0
- memos/_js/sync.d.ts.map +1 -0
- memos/_js/sync.js +389 -0
- memos/_js/sync.js.map +1 -0
- memos/_js/tamper-log.d.ts +98 -0
- memos/_js/tamper-log.d.ts.map +1 -0
- memos/_js/tamper-log.js +327 -0
- memos/_js/tamper-log.js.map +1 -0
- memos/_js/temporal.d.ts +92 -0
- memos/_js/temporal.d.ts.map +1 -0
- memos/_js/temporal.js +536 -0
- memos/_js/temporal.js.map +1 -0
- memos/_js/time-travel.d.ts +38 -0
- memos/_js/time-travel.d.ts.map +1 -0
- memos/_js/time-travel.js +46 -0
- memos/_js/time-travel.js.map +1 -0
- memos/_js/timestamp-format.d.ts +18 -0
- memos/_js/timestamp-format.d.ts.map +1 -0
- memos/_js/timestamp-format.js +52 -0
- memos/_js/timestamp-format.js.map +1 -0
- memos/_js/triple-extraction.d.ts +31 -0
- memos/_js/triple-extraction.d.ts.map +1 -0
- memos/_js/triple-extraction.js +116 -0
- memos/_js/triple-extraction.js.map +1 -0
- memos/_js/types.d.ts +2474 -0
- memos/_js/types.d.ts.map +1 -0
- memos/_js/types.js +20 -0
- memos/_js/types.js.map +1 -0
- memos/_js/updater.d.ts +95 -0
- memos/_js/updater.d.ts.map +1 -0
- memos/_js/updater.js +268 -0
- memos/_js/updater.js.map +1 -0
- memos/_js/version.d.ts +17 -0
- memos/_js/version.d.ts.map +1 -0
- memos/_js/version.js +34 -0
- memos/_js/version.js.map +1 -0
- memos/_js/vision/clip-query-cache.d.ts +58 -0
- memos/_js/vision/clip-query-cache.d.ts.map +1 -0
- memos/_js/vision/clip-query-cache.js +79 -0
- memos/_js/vision/clip-query-cache.js.map +1 -0
- memos/_js/vision/clip.d.ts +113 -0
- memos/_js/vision/clip.d.ts.map +1 -0
- memos/_js/vision/clip.js +224 -0
- memos/_js/vision/clip.js.map +1 -0
- memos/_js/vision/fake-clip.d.ts +55 -0
- memos/_js/vision/fake-clip.d.ts.map +1 -0
- memos/_js/vision/fake-clip.js +122 -0
- memos/_js/vision/fake-clip.js.map +1 -0
- memos/_js/vision/index.d.ts +15 -0
- memos/_js/vision/index.d.ts.map +1 -0
- memos/_js/vision/index.js +13 -0
- memos/_js/vision/index.js.map +1 -0
- memos/_js/vision/store.d.ts +137 -0
- memos/_js/vision/store.d.ts.map +1 -0
- memos/_js/vision/store.js +383 -0
- memos/_js/vision/store.js.map +1 -0
- memos/adapters/__init__.py +0 -0
- memos/adapters/crewai.py +385 -0
- memos/adapters/langchain.py +301 -0
- memos/adapters/ollama.py +396 -0
- memos/server/__init__.py +0 -0
- memos/server/main.py +547 -0
- memos/server/routes.py +360 -0
memos/_js/types.d.ts
ADDED
|
@@ -0,0 +1,2474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemOS — Universal memory layer for AI agents.
|
|
3
|
+
*
|
|
4
|
+
* Core type definitions. Every interface and type in the system
|
|
5
|
+
* is defined here to provide a single source of truth for the
|
|
6
|
+
* data model across modules.
|
|
7
|
+
*
|
|
8
|
+
* @module @memos/types
|
|
9
|
+
*/
|
|
10
|
+
import type { CompressionLevel } from "./compression.js";
|
|
11
|
+
import type { ImportanceConfig } from "./importance.js";
|
|
12
|
+
/**
|
|
13
|
+
* Semantic category for a memory node.
|
|
14
|
+
*
|
|
15
|
+
* - `fact` — A discrete piece of information.
|
|
16
|
+
* - `preference` — A user or agent preference.
|
|
17
|
+
* - `context` — Conversational or situational context.
|
|
18
|
+
* - `relationship` — An observed relationship between entities.
|
|
19
|
+
* - `entity` — A named entity (person, place, thing).
|
|
20
|
+
* - `custom` — Application-defined type.
|
|
21
|
+
*/
|
|
22
|
+
export type MemoryType = "fact" | "preference" | "context" | "relationship" | "entity" | "custom";
|
|
23
|
+
/**
|
|
24
|
+
* Provenance source for a memory node.
|
|
25
|
+
*
|
|
26
|
+
* - `user_input` — Stated directly by the user. Highest trust.
|
|
27
|
+
* - `agent_inferred` — Inferred by an agent from context.
|
|
28
|
+
* - `tool_output` — Returned by a tool call (treated as untrusted
|
|
29
|
+
* for agent consumption — see the provenance-trust layer).
|
|
30
|
+
* - `external_data` — Imported from an external source.
|
|
31
|
+
* - `system` — Generated by the system itself.
|
|
32
|
+
*/
|
|
33
|
+
export type MemorySource = "user_input" | "agent_inferred" | "tool_output" | "external_data" | "system";
|
|
34
|
+
/**
|
|
35
|
+
* Default trust score for each provenance source.
|
|
36
|
+
*/
|
|
37
|
+
export declare const DEFAULT_TRUST_SCORES: Record<MemorySource, number>;
|
|
38
|
+
/**
|
|
39
|
+
* Provenance tier — the trust channel a memory arrived through, assigned
|
|
40
|
+
* at write time and stored as a column. Ordered highest-trust first:
|
|
41
|
+
*
|
|
42
|
+
* - `user-verified` — explicitly confirmed by the user. Never assigned
|
|
43
|
+
* automatically — only via an explicit `provenance` option or
|
|
44
|
+
* `verify()`.
|
|
45
|
+
* - `user` — ordinary user statements (default for
|
|
46
|
+
* `source: "user_input"` writes).
|
|
47
|
+
* - `tool-output` — results returned by tools (`source: "tool_output"`).
|
|
48
|
+
* - `chat` — model-generated content (`source: "agent_inferred"`
|
|
49
|
+
* or `"system"`).
|
|
50
|
+
* - `imported` — bulk imports (`source: "external_data"`).
|
|
51
|
+
*
|
|
52
|
+
* See `src/provenance.ts` for the default rule, trust multipliers, and
|
|
53
|
+
* the read-time trust policy.
|
|
54
|
+
*/
|
|
55
|
+
export type ProvenanceTier = "user-verified" | "user" | "tool-output" | "chat" | "imported";
|
|
56
|
+
/**
|
|
57
|
+
* A single unit of memory stored in the graph.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* Which retrieval pool a memory belongs to — the multi-granularity
|
|
61
|
+
* structure from the LongMemEval-V2 design lessons:
|
|
62
|
+
* - `event` — raw statements as they were captured (the default; every
|
|
63
|
+
* legacy memory reads as `event`).
|
|
64
|
+
* - `note` — distilled summaries produced by consolidation
|
|
65
|
+
* (`summarizeCluster` writes here).
|
|
66
|
+
* - `procedure` — workflow/procedural knowledge ("how to" steps).
|
|
67
|
+
* - `visual` — image memories (the node is `type: "fact"` with
|
|
68
|
+
* `metadata.image` present); multi-stream packs search
|
|
69
|
+
* this as a dedicated stream.
|
|
70
|
+
* Pools are queryable via `SearchFilter.pool` and searched as separate
|
|
71
|
+
* streams by multi-stream context packs.
|
|
72
|
+
*/
|
|
73
|
+
export type MemoryPool = "event" | "note" | "procedure" | "visual";
|
|
74
|
+
/**
|
|
75
|
+
* Typed multi-scope keys for isolating memories per user, agent, and run.
|
|
76
|
+
* Composed into the underlying namespace string in fixed order
|
|
77
|
+
* (`u:alice/a:coder/r:r1`) — see `src/scope.ts`. Hierarchical by default:
|
|
78
|
+
* querying `{ user: "alice" }` surfaces every memory Alice's agents and
|
|
79
|
+
* runs stored.
|
|
80
|
+
*/
|
|
81
|
+
export interface MemoryScope {
|
|
82
|
+
/** Owner of the memories (e.g. end-user id). */
|
|
83
|
+
user?: string;
|
|
84
|
+
/** Which agent produced them. */
|
|
85
|
+
agent?: string;
|
|
86
|
+
/** Which session/run produced them. */
|
|
87
|
+
run?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface MemoryNode {
|
|
90
|
+
/** Globally unique identifier (UUID v4). */
|
|
91
|
+
id: string;
|
|
92
|
+
/** Raw text content of the memory. */
|
|
93
|
+
content: string;
|
|
94
|
+
/** Extractive summary (auto-generated if not supplied). */
|
|
95
|
+
summary: string;
|
|
96
|
+
/** Semantic type of this memory. */
|
|
97
|
+
type: MemoryType;
|
|
98
|
+
/** Arbitrary metadata bag. Must be JSON-serialisable. */
|
|
99
|
+
metadata: Record<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* Base importance score in [0, 1]. Set at write time (default 0.5) or
|
|
102
|
+
* manually via `update`. Lifecycle decisions (eviction, archival)
|
|
103
|
+
* derive an *effective* importance from this base plus recency decay
|
|
104
|
+
* and access reinforcement — see `computeEffectiveImportance` in
|
|
105
|
+
* `src/importance.ts`.
|
|
106
|
+
*/
|
|
107
|
+
importance: number;
|
|
108
|
+
/** Creation timestamp (Unix ms). */
|
|
109
|
+
createdAt: number;
|
|
110
|
+
/** Last-modified timestamp (Unix ms). */
|
|
111
|
+
updatedAt: number;
|
|
112
|
+
/** Number of times this node has been retrieved. */
|
|
113
|
+
accessCount: number;
|
|
114
|
+
/** Timestamp of last access (Unix ms). */
|
|
115
|
+
lastAccessed: number;
|
|
116
|
+
/** Custom tags for categorisation. */
|
|
117
|
+
tags: string[];
|
|
118
|
+
/** TTL expiration as Unix timestamp (seconds). null = no expiration. */
|
|
119
|
+
expiresAt: number | null;
|
|
120
|
+
/** Namespace for grouping memories (experimental). */
|
|
121
|
+
namespace: string;
|
|
122
|
+
/**
|
|
123
|
+
* Temporal validity start (Unix ms). When set, the memory is
|
|
124
|
+
* considered "active" from this point in time. null = always valid.
|
|
125
|
+
*/
|
|
126
|
+
validFrom: number | null;
|
|
127
|
+
/**
|
|
128
|
+
* Temporal validity end (Unix ms). When set, the memory becomes
|
|
129
|
+
* "historical" (excluded from default search) after this time.
|
|
130
|
+
* null = no expiry. Superseded memories keep their validTo so the
|
|
131
|
+
* history is queryable via `searchTemporal`.
|
|
132
|
+
*/
|
|
133
|
+
validTo: number | null;
|
|
134
|
+
/**
|
|
135
|
+
* Where this memory originated. Used for provenance and trust
|
|
136
|
+
* scoring.
|
|
137
|
+
* - `user_input` — Stated directly by the user. Highest trust.
|
|
138
|
+
* - `agent_inferred` — Inferred by an agent from context. Lower trust.
|
|
139
|
+
* - `external_data` — Imported from an external source (API, file, etc.).
|
|
140
|
+
* - `system` — Generated by the system itself.
|
|
141
|
+
*/
|
|
142
|
+
source: MemorySource;
|
|
143
|
+
/**
|
|
144
|
+
* Provenance tier — the trust channel this memory arrived through
|
|
145
|
+
* (`user-verified` > `user` > `tool-output` > `chat` > `imported`).
|
|
146
|
+
* Assigned at write time from `CreateMemoryInput.provenance` or the
|
|
147
|
+
* source-derived default (see `resolveProvenance` in
|
|
148
|
+
* `src/provenance.ts`); backfilled as `"user"` for legacy rows.
|
|
149
|
+
*/
|
|
150
|
+
provenance: ProvenanceTier;
|
|
151
|
+
/**
|
|
152
|
+
* Write-gate quarantine flag. When the local-heuristic classifier
|
|
153
|
+
* (`screenWrite` in `src/quarantine.ts`) flags instruction-like
|
|
154
|
+
* content, the memory is still stored (add-only) but quarantined:
|
|
155
|
+
* excluded from recall by default until reviewed via
|
|
156
|
+
* `memos quarantine list|release`.
|
|
157
|
+
*/
|
|
158
|
+
quarantined: boolean;
|
|
159
|
+
/** Unix ms when the memory was quarantined. null when never. */
|
|
160
|
+
quarantinedAt: number | null;
|
|
161
|
+
/** `;`-joined quarantine signal codes. null when never quarantined. */
|
|
162
|
+
quarantineReason: string | null;
|
|
163
|
+
/**
|
|
164
|
+
* Retrieval pool this memory belongs to (multi-granularity retrieval).
|
|
165
|
+
* Optional for backward compatibility with custom storage adapters;
|
|
166
|
+
* treated as `"event"` when absent.
|
|
167
|
+
*/
|
|
168
|
+
pool?: MemoryPool;
|
|
169
|
+
/**
|
|
170
|
+
* Harness that wrote this memory (`"claude-code"`, `"cline"`,
|
|
171
|
+
* `"codex"`, `"gemini-cli"`, `"openclaw"`, or `"unknown"`). Stamped
|
|
172
|
+
* at write time by `store()` via `detectHarness()`; optional for
|
|
173
|
+
* backward compatibility with node literals and custom storage
|
|
174
|
+
* adapters — reads as `"unknown"` when absent. Legacy rows are
|
|
175
|
+
* backfilled as `"unknown"` by the schema migration.
|
|
176
|
+
*/
|
|
177
|
+
harness?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Trust score in [0, 1]. Influences retrieval ranking. Starts at
|
|
180
|
+
* 1.0 for `user_input`, 0.7 for `agent_inferred`, 0.5 for
|
|
181
|
+
* `external_data`, 0.3 for `system`. Can be manually adjusted via
|
|
182
|
+
* `memos.setTrust()`.
|
|
183
|
+
*/
|
|
184
|
+
trustScore: number;
|
|
185
|
+
/**
|
|
186
|
+
* Dynamic confidence score in [0, 1]. Reflects how sure the system
|
|
187
|
+
* is that this memory is still true, given new observations.
|
|
188
|
+
* - Reinforced when new observations agree (confidence goes up)
|
|
189
|
+
* - Revised when new observations partially conflict (confidence goes down)
|
|
190
|
+
* - Superseded when new facts clearly replace old ones (marked historical)
|
|
191
|
+
* Stored in metadata as `confidence` and `evidenceCount`.
|
|
192
|
+
*/
|
|
193
|
+
confidence?: number;
|
|
194
|
+
/** Number of evidence events (reinforces + revisions) that touched this memory. */
|
|
195
|
+
evidenceCount?: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Partial input accepted when creating a new memory.
|
|
199
|
+
* Only `content` is required; everything else has defaults.
|
|
200
|
+
*/
|
|
201
|
+
export interface CreateMemoryInput {
|
|
202
|
+
content: string;
|
|
203
|
+
type?: MemoryType;
|
|
204
|
+
metadata?: Record<string, unknown>;
|
|
205
|
+
summary?: string;
|
|
206
|
+
importance?: number;
|
|
207
|
+
/** TTL in seconds from now. */
|
|
208
|
+
ttl?: number;
|
|
209
|
+
/** Custom tags. */
|
|
210
|
+
tags?: string[];
|
|
211
|
+
/** Namespace (experimental). */
|
|
212
|
+
namespace?: string;
|
|
213
|
+
/**
|
|
214
|
+
* Typed multi-scope isolation. Composed into the namespace string
|
|
215
|
+
* (fixed order user → agent → run); takes precedence over `namespace`
|
|
216
|
+
* when both are supplied.
|
|
217
|
+
*/
|
|
218
|
+
scope?: MemoryScope;
|
|
219
|
+
/** Temporal validity start (Unix ms). null = always valid. */
|
|
220
|
+
validFrom?: number | null;
|
|
221
|
+
/** Temporal validity end (Unix ms). null = no expiry. */
|
|
222
|
+
validTo?: number | null;
|
|
223
|
+
/** Provenance source. Default: `user_input`. */
|
|
224
|
+
source?: MemorySource;
|
|
225
|
+
/**
|
|
226
|
+
* Provenance tier override (`user-verified` > `user` > `tool-output` >
|
|
227
|
+
* `chat` > `imported`). When omitted, the tier is derived from
|
|
228
|
+
* `source` via the default rule (see `resolveProvenance` in
|
|
229
|
+
* `src/provenance.ts`): `user_input` → `user`, `tool_output` →
|
|
230
|
+
* `tool-output`, `agent_inferred`/`system` → `chat`, `external_data` →
|
|
231
|
+
* `imported`. `user-verified` is never assigned automatically.
|
|
232
|
+
*/
|
|
233
|
+
provenance?: ProvenanceTier;
|
|
234
|
+
/**
|
|
235
|
+
* Write-gate screening: when true (default), the local-heuristic
|
|
236
|
+
* classifier (`screenWrite`) runs on `content` and flags
|
|
237
|
+
* instruction-like payloads by storing them quarantined (excluded
|
|
238
|
+
* from recall until reviewed). Set to false to bypass screening for
|
|
239
|
+
* trusted bulk operations. No LLM is ever consulted on this path.
|
|
240
|
+
*/
|
|
241
|
+
quarantineScreen?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Retrieval pool for this memory. Default: `"event"`. Use `"procedure"`
|
|
244
|
+
* for workflow/how-to knowledge — multi-stream context packs search
|
|
245
|
+
* procedures with a dedicated query stream.
|
|
246
|
+
*/
|
|
247
|
+
pool?: MemoryPool;
|
|
248
|
+
/**
|
|
249
|
+
* One line of write-time context describing the situation this memory
|
|
250
|
+
* was captured in (e.g. "stated while debugging the deploy pipeline").
|
|
251
|
+
* Stored in `metadata.context`, prepended to the embedded text, and
|
|
252
|
+
* included in context packs. When absent, the optional
|
|
253
|
+
* `MemOSConfig.enrichMemory` hook is consulted.
|
|
254
|
+
*/
|
|
255
|
+
context?: string;
|
|
256
|
+
/** Trust score [0, 1]. Overrides the default for the chosen source. */
|
|
257
|
+
trustScore?: number;
|
|
258
|
+
/**
|
|
259
|
+
* Harness attribution override (e.g. `"claude-code"`, `"cline"`).
|
|
260
|
+
* When omitted, `store()` stamps the harness detected from the
|
|
261
|
+
* environment via `detectHarness()` (see `src/harness.ts`).
|
|
262
|
+
*/
|
|
263
|
+
harness?: string;
|
|
264
|
+
/**
|
|
265
|
+
* Dynamic confidence score [0, 1] for the confidence state machine.
|
|
266
|
+
* Used to track how sure the system is that this memory is still true.
|
|
267
|
+
* If omitted, defaults to INITIAL_CONFIDENCE (0.5).
|
|
268
|
+
*/
|
|
269
|
+
confidence?: number;
|
|
270
|
+
/**
|
|
271
|
+
* Evidence count for the confidence state machine.
|
|
272
|
+
* Number of reinforce/revise events this memory has accumulated.
|
|
273
|
+
* If omitted, defaults to 0 for manually stored items.
|
|
274
|
+
*/
|
|
275
|
+
evidenceCount?: number;
|
|
276
|
+
/**
|
|
277
|
+
* When true, apply the Hermes-style retain pre-filter (v1.6.26) before
|
|
278
|
+
* writing: low-signal content (acknowledgements, chit-chat, near-empty
|
|
279
|
+
* text) is skipped so long-term memory isn't flooded with noise that would
|
|
280
|
+
* later bloat context-packs. Default: false (write always).
|
|
281
|
+
*/
|
|
282
|
+
filterRetain?: boolean;
|
|
283
|
+
/**
|
|
284
|
+
* When true, run the evidence state machine before writing:
|
|
285
|
+
* content that confirms an existing memory reinforces it (confidence ↑)
|
|
286
|
+
* instead of writing a duplicate node, and content that contradicts it
|
|
287
|
+
* supersedes the old version. Default: false — store() appends a new
|
|
288
|
+
* node every time (use dedupe()/consolidate() for explicit merging).
|
|
289
|
+
*/
|
|
290
|
+
evidenceLearning?: boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Reference time (Unix ms) for this write. Overrides `Date.now()` as
|
|
293
|
+
* the write timestamp AND as the reference time for deterministic
|
|
294
|
+
* temporal parsing (event extraction / update resolution). Primarily
|
|
295
|
+
* a testing hook — omit in production so the wall clock is used.
|
|
296
|
+
*/
|
|
297
|
+
now?: number;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Partial update for an existing memory node.
|
|
301
|
+
*/
|
|
302
|
+
export interface UpdateMemoryInput {
|
|
303
|
+
content?: string;
|
|
304
|
+
summary?: string;
|
|
305
|
+
type?: MemoryType;
|
|
306
|
+
metadata?: Record<string, unknown>;
|
|
307
|
+
importance?: number;
|
|
308
|
+
tags?: string[];
|
|
309
|
+
namespace?: string;
|
|
310
|
+
/** Access count for Ebbinghaus reinforcement. */
|
|
311
|
+
accessCount?: number;
|
|
312
|
+
/** Last access time (Unix ms) for Ebbinghaus reinforcement. */
|
|
313
|
+
lastAccessed?: number;
|
|
314
|
+
/** Temporal validity start (Unix ms). */
|
|
315
|
+
validFrom?: number | null;
|
|
316
|
+
/** Temporal validity end (Unix ms). */
|
|
317
|
+
validTo?: number | null;
|
|
318
|
+
/** Provenance source. */
|
|
319
|
+
source?: MemorySource;
|
|
320
|
+
/** Provenance tier override (see `CreateMemoryInput.provenance`). */
|
|
321
|
+
provenance?: ProvenanceTier;
|
|
322
|
+
/**
|
|
323
|
+
* Quarantine flag. Set explicitly by `releaseFromQuarantine` (false)
|
|
324
|
+
* or administrative quarantine (true); the write gate sets it via
|
|
325
|
+
* `store()`, not via `update()`.
|
|
326
|
+
*/
|
|
327
|
+
quarantined?: boolean;
|
|
328
|
+
/** Unix ms when the memory was quarantined. null clears it. */
|
|
329
|
+
quarantinedAt?: number | null;
|
|
330
|
+
/** `;`-joined quarantine signal codes. null clears it. */
|
|
331
|
+
quarantineReason?: string | null;
|
|
332
|
+
/** Trust score [0, 1]. */
|
|
333
|
+
trustScore?: number;
|
|
334
|
+
/** Dynamic confidence [0, 1] — updated by the evidence state machine. */
|
|
335
|
+
confidence?: number;
|
|
336
|
+
/** Evidence count — updated by the evidence state machine. */
|
|
337
|
+
evidenceCount?: number;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Semantic relation between two memory nodes.
|
|
341
|
+
*
|
|
342
|
+
* - `relates_to` — General association.
|
|
343
|
+
* - `contradicts` — The target contradicts the source.
|
|
344
|
+
* - `supports` — The target supports the source.
|
|
345
|
+
* - `derived_from` — The source was derived from the target.
|
|
346
|
+
* - `part_of` — The source is a part of the target.
|
|
347
|
+
* - `custom` — Application-defined relation.
|
|
348
|
+
*/
|
|
349
|
+
export type EdgeRelation = "relates_to" | "contradicts" | "supports" | "derived_from" | "part_of" | "temporal_precedes" | "custom";
|
|
350
|
+
/**
|
|
351
|
+
* A directed, weighted edge connecting two memory nodes.
|
|
352
|
+
*/
|
|
353
|
+
export interface MemoryEdge {
|
|
354
|
+
/** Globally unique identifier (UUID v4). */
|
|
355
|
+
id: string;
|
|
356
|
+
/** Source node ID. */
|
|
357
|
+
sourceId: string;
|
|
358
|
+
/** Target node ID. */
|
|
359
|
+
targetId: string;
|
|
360
|
+
/** Semantic relation label. */
|
|
361
|
+
relation: EdgeRelation;
|
|
362
|
+
/** Edge weight in [0, 1]. */
|
|
363
|
+
weight: number;
|
|
364
|
+
/** Arbitrary metadata bag. */
|
|
365
|
+
metadata: Record<string, unknown>;
|
|
366
|
+
/**
|
|
367
|
+
* Transaction/ingest time (Unix ms): when MemOS learned the fact.
|
|
368
|
+
* This is the bitemporal transaction-time axis — it is never renamed
|
|
369
|
+
* or repurposed; event-time validity lives in `validFrom`/`validTo`.
|
|
370
|
+
*/
|
|
371
|
+
createdAt: number;
|
|
372
|
+
/**
|
|
373
|
+
* Event-time validity start (Unix ms): when the fact became true in the
|
|
374
|
+
* world. `null`/undefined = open-ended (still true, or unknown).
|
|
375
|
+
*/
|
|
376
|
+
validFrom?: number | null;
|
|
377
|
+
/**
|
|
378
|
+
* Event-time validity end (Unix ms): when the fact ceased to be true.
|
|
379
|
+
* `null`/undefined = open-ended (still true). Supersession stamps this
|
|
380
|
+
* instead of deleting the row, preserving add-only history for as-of
|
|
381
|
+
* (time-travel) reads.
|
|
382
|
+
*/
|
|
383
|
+
validTo?: number | null;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Input for creating a new edge.
|
|
387
|
+
*/
|
|
388
|
+
export interface CreateEdgeInput {
|
|
389
|
+
sourceId: string;
|
|
390
|
+
targetId: string;
|
|
391
|
+
relation?: EdgeRelation;
|
|
392
|
+
weight?: number;
|
|
393
|
+
metadata?: Record<string, unknown>;
|
|
394
|
+
/**
|
|
395
|
+
* Event-time validity start (Unix ms): when the fact became true in the
|
|
396
|
+
* world. `null`/undefined = open-ended.
|
|
397
|
+
*/
|
|
398
|
+
validFrom?: number | null;
|
|
399
|
+
/**
|
|
400
|
+
* Event-time validity end (Unix ms): when the fact ceased to be true.
|
|
401
|
+
* `null`/undefined = open-ended.
|
|
402
|
+
*/
|
|
403
|
+
validTo?: number | null;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Filter criteria for searching memories.
|
|
407
|
+
*/
|
|
408
|
+
export interface SearchFilter {
|
|
409
|
+
/** Full-text search query. */
|
|
410
|
+
query?: string;
|
|
411
|
+
/** Filter by memory type. */
|
|
412
|
+
type?: MemoryType;
|
|
413
|
+
/** Minimum importance threshold [0, 1]. */
|
|
414
|
+
minImportance?: number;
|
|
415
|
+
/** Maximum importance threshold [0, 1]. */
|
|
416
|
+
maxImportance?: number;
|
|
417
|
+
/** Metadata key-value filter. */
|
|
418
|
+
metadata?: Record<string, unknown>;
|
|
419
|
+
/** Limit the number of results. */
|
|
420
|
+
limit?: number;
|
|
421
|
+
/** Offset for pagination. */
|
|
422
|
+
offset?: number;
|
|
423
|
+
/** Sort field. */
|
|
424
|
+
sortBy?: "importance" | "createdAt" | "updatedAt" | "accessCount" | "relevance" | "trustScore";
|
|
425
|
+
/** Sort direction. */
|
|
426
|
+
sortOrder?: "asc" | "desc";
|
|
427
|
+
/** Filter by tags (AND logic — node must have ALL specified tags). */
|
|
428
|
+
tags?: string[];
|
|
429
|
+
/** Filter by namespace. */
|
|
430
|
+
namespace?: string;
|
|
431
|
+
/**
|
|
432
|
+
* Filter by an explicit list of namespaces (OR logic). Composes with
|
|
433
|
+
* `namespace` / `namespacePrefix` (OR'd together) when several are set.
|
|
434
|
+
* Useful for multi-scope reads, e.g. `["project:alpha", "project:beta"]`.
|
|
435
|
+
*/
|
|
436
|
+
namespaces?: string[];
|
|
437
|
+
/**
|
|
438
|
+
* Scoped-space union (company-brain container semantics): when any
|
|
439
|
+
* namespace constraint is present (`namespace`, `namespaces`, or
|
|
440
|
+
* `namespacePrefix`), also include the shared `default` namespace in
|
|
441
|
+
* the result set. `MemOS.search` enables this by default for
|
|
442
|
+
* scope-filtered searches; pass `false` explicitly for the legacy
|
|
443
|
+
* exclusive behavior. Unscoped searches are unaffected.
|
|
444
|
+
*/
|
|
445
|
+
includeSharedScope?: boolean;
|
|
446
|
+
/**
|
|
447
|
+
* Typed multi-scope filter. Composed into a namespace prefix; with the
|
|
448
|
+
* default `"hierarchical"` match, `{ user: "alice" }` surfaces every
|
|
449
|
+
* memory stored under Alice's agents and runs. Set
|
|
450
|
+
* `scopeMatch: "exact"` to require the identical namespace string.
|
|
451
|
+
*/
|
|
452
|
+
scope?: MemoryScope;
|
|
453
|
+
/** How `scope` matches stored namespaces. Default `"hierarchical"`. */
|
|
454
|
+
scopeMatch?: "exact" | "hierarchical";
|
|
455
|
+
/** Internal: pre-composed LIKE prefix for namespace matching. */
|
|
456
|
+
namespacePrefix?: string;
|
|
457
|
+
/**
|
|
458
|
+
* Filter by retrieval pool. Pass a single pool or a list (OR logic).
|
|
459
|
+
* Memories written before pools existed read as `"event"`.
|
|
460
|
+
*/
|
|
461
|
+
pool?: MemoryPool | MemoryPool[];
|
|
462
|
+
/**
|
|
463
|
+
* Filter by authoring harness. Pass a harness slug stamped at write
|
|
464
|
+
* time (`"claude-code"`, `"cline"`, …) or `"all"` (default) for the
|
|
465
|
+
* behavior-preserving cross-harness recall. `"unknown"` matches
|
|
466
|
+
* unattributed rows, including everything written before harness
|
|
467
|
+
* attribution existed.
|
|
468
|
+
*/
|
|
469
|
+
harness?: string;
|
|
470
|
+
/** Filter by provenance source. */
|
|
471
|
+
source?: MemorySource;
|
|
472
|
+
/** Filter by provenance tier. */
|
|
473
|
+
provenance?: ProvenanceTier;
|
|
474
|
+
/** Minimum trust score [0, 1]. */
|
|
475
|
+
minTrustScore?: number;
|
|
476
|
+
/**
|
|
477
|
+
* Quarantine visibility. Quarantined memories are excluded from
|
|
478
|
+
* recall by default (`includeQuarantined` unset or false) — the
|
|
479
|
+
* write-gate guarantee. Pass `true` to include them (review flows);
|
|
480
|
+
* `quarantinedOnly: true` returns only quarantined rows (equivalent
|
|
481
|
+
* to `memos quarantine list`).
|
|
482
|
+
*/
|
|
483
|
+
includeQuarantined?: boolean;
|
|
484
|
+
/** When true, return only quarantined memories (the review queue). */
|
|
485
|
+
quarantinedOnly?: boolean;
|
|
486
|
+
/**
|
|
487
|
+
* When true (default), exclude memories whose `validTo` is in the
|
|
488
|
+
* past (i.e. historical/superseded memories). Set to false to
|
|
489
|
+
* include historical memories.
|
|
490
|
+
*/
|
|
491
|
+
includeHistorical?: boolean;
|
|
492
|
+
/**
|
|
493
|
+
* FTS term-join operator for the keyword leg.
|
|
494
|
+
*
|
|
495
|
+
* - `"AUTO"` (default): run the precision `AND` query first; when it
|
|
496
|
+
* matches nothing, relax to an `OR` query so partial-term matches
|
|
497
|
+
* still surface. Precise, and the historical default.
|
|
498
|
+
* - `"AND"`: every query term must appear (forced precision).
|
|
499
|
+
* - `"OR"`: any term may appear (forced recall — useful for
|
|
500
|
+
* multi-evidence retrieval where relevant facts rarely share every
|
|
501
|
+
* query term; noisier for single-fact queries).
|
|
502
|
+
*/
|
|
503
|
+
ftsOperator?: "AUTO" | "AND" | "OR";
|
|
504
|
+
/**
|
|
505
|
+
* When true, expand the top base results with their "session siblings" —
|
|
506
|
+
* other memories sharing the same `sessionId` (or `instanceId`) metadata
|
|
507
|
+
* value. Multi-hop / multi-evidence answers typically live across many turns
|
|
508
|
+
* of one conversation, so once a single relevant turn is retrieved, pulling
|
|
509
|
+
* its conversation neighbours into the ranking materially raises All-Evidence
|
|
510
|
+
* Recall. Deterministic, model-free, and ON by default (override with
|
|
511
|
+
* `config.experimental.sessionExpansion`); injected siblings are ranked
|
|
512
|
+
* behind every organically-retrieved result, so head ordering never moves
|
|
513
|
+
* and only the empty tail is filled.
|
|
514
|
+
*/
|
|
515
|
+
sessionExpansion?: boolean;
|
|
516
|
+
/** Timestamp filter (Unix ms): only return memories valid at this time. */
|
|
517
|
+
validAt?: number;
|
|
518
|
+
/**
|
|
519
|
+
* How many candidates each hybrid-search leg fetches before fusion.
|
|
520
|
+
* Overrides the default `max(limit * 4, 20)`. Higher values widen the
|
|
521
|
+
* recall pool (useful when a reranker or expansion stage narrows
|
|
522
|
+
* afterwards) at linear scan cost.
|
|
523
|
+
*/
|
|
524
|
+
candidateDepth?: number;
|
|
525
|
+
/**
|
|
526
|
+
* Drop results whose stored embedding is a near-duplicate (cosine >=
|
|
527
|
+
* `experimental.searchDedupThreshold`, default 0.95) of an
|
|
528
|
+
* already-selected result. Near-duplicates waste top-K slots that
|
|
529
|
+
* distinct evidence needs — multi-evidence questions score one node per
|
|
530
|
+
* duplicate otherwise. Off by default.
|
|
531
|
+
*/
|
|
532
|
+
semanticDedup?: boolean;
|
|
533
|
+
/**
|
|
534
|
+
* Per-query override for the PPR-lite graph expansion
|
|
535
|
+
* (`config.fusion.graphExpansion`). `true` forces expansion on for this
|
|
536
|
+
* query, `false` disables it (byte-identical to the pre-expansion
|
|
537
|
+
* pipeline); `undefined` falls back to the fusion-config default (off).
|
|
538
|
+
* `contextPack`
|
|
539
|
+
* forwards its own `graphExpansion: false` through this flag.
|
|
540
|
+
*/
|
|
541
|
+
graphExpansion?: boolean;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Options for `MemOS.semanticSearch()`. Accepts every `SearchFilter` field
|
|
545
|
+
* (namespace, tags, type, limit, …) plus the semantic similarity cutoff.
|
|
546
|
+
*/
|
|
547
|
+
export interface SemanticSearchOptions extends SearchFilter {
|
|
548
|
+
/** Minimum similarity score in [0, 1]. Default 0.1. */
|
|
549
|
+
threshold?: number;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* AI Trio context pack — a token-budgeted, ranked, namespace-filtered
|
|
553
|
+
* slice of memory intended to be folded into an LLM prompt by Guardian.
|
|
554
|
+
*
|
|
555
|
+
* See `docs/ai-trio-contracts.md` for the full contract.
|
|
556
|
+
*/
|
|
557
|
+
export interface ContextPackItem {
|
|
558
|
+
id: string;
|
|
559
|
+
content: string;
|
|
560
|
+
summary: string;
|
|
561
|
+
score: number;
|
|
562
|
+
scores?: {
|
|
563
|
+
keyword?: number;
|
|
564
|
+
semantic?: number;
|
|
565
|
+
hybrid?: number;
|
|
566
|
+
};
|
|
567
|
+
trust: string;
|
|
568
|
+
source: string;
|
|
569
|
+
/**
|
|
570
|
+
* Provenance tier of the item's channel (see `ProvenanceTier`).
|
|
571
|
+
* Present on packs built after the provenance-trust layer.
|
|
572
|
+
*/
|
|
573
|
+
provenance?: string;
|
|
574
|
+
/**
|
|
575
|
+
* True when the item comes from a low-trust channel (`imported` /
|
|
576
|
+
* `tool-output` tier, or quarantined-then-released): confirm before
|
|
577
|
+
* acting on it.
|
|
578
|
+
*/
|
|
579
|
+
untrustedSource?: boolean;
|
|
580
|
+
tags: string[];
|
|
581
|
+
updatedAt: string;
|
|
582
|
+
}
|
|
583
|
+
export interface ContextPack {
|
|
584
|
+
schema: "ai-trio.memos.context-pack.v1";
|
|
585
|
+
query: string;
|
|
586
|
+
namespace: string;
|
|
587
|
+
tokenBudget: number;
|
|
588
|
+
items: ContextPackItem[];
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* A memory node bundled with its relevance score after a search.
|
|
592
|
+
*/
|
|
593
|
+
export interface ScoredMemory {
|
|
594
|
+
node: MemoryNode;
|
|
595
|
+
/** Relevance score returned by the search algorithm. */
|
|
596
|
+
score: number;
|
|
597
|
+
/**
|
|
598
|
+
* Provenance tier of the result's channel (see `ProvenanceTier`).
|
|
599
|
+
* Stamped by `decorateTrustFlags` for agent-facing result sets.
|
|
600
|
+
*/
|
|
601
|
+
provenance?: ProvenanceTier;
|
|
602
|
+
/**
|
|
603
|
+
* Citable `[mem:hex]` token tracing the memory to its source episode
|
|
604
|
+
* (see `src/citations.ts`). Stamped by `decorateTrustFlags`.
|
|
605
|
+
*/
|
|
606
|
+
citation?: string;
|
|
607
|
+
/**
|
|
608
|
+
* True when the memory comes from a low-trust channel (`imported` /
|
|
609
|
+
* `tool-output` tier, or quarantined-then-released): the host agent
|
|
610
|
+
* should confirm it instead of silently injecting it as context.
|
|
611
|
+
* Stamped by `decorateTrustFlags`.
|
|
612
|
+
*/
|
|
613
|
+
untrustedSource?: boolean;
|
|
614
|
+
/** Optional score breakdown for hybrid retrieval. `graph` marks a
|
|
615
|
+
* node injected by the graph-expansion leg, `ppr` the normalized
|
|
616
|
+
* personalized-PageRank score from the PPR-lite expansion blend,
|
|
617
|
+
* `session` one pulled in
|
|
618
|
+
* by session-sibling expansion, `rerank` the cross-encoder score
|
|
619
|
+
* (squashed to [0,1]) from the two-stage reranking pass, `entity` the
|
|
620
|
+
* query-entity overlap (in [0,1]) that earned the entity-fusion boost,
|
|
621
|
+
* and `entityLeg` the matched-entity count from the entity-inverted-index
|
|
622
|
+
* RRF leg (recall signal, distinct from the `entity` scoring boost),
|
|
623
|
+
* and `provenance` the per-tier trust multiplier from the
|
|
624
|
+
* provenance-trust layer.
|
|
625
|
+
*
|
|
626
|
+
* `semantic` vs `semanticRaw`: `semantic` is the leg's MIN-MAX
|
|
627
|
+
* NORMALIZED contribution used for fusion — its top candidate is
|
|
628
|
+
* always exactly `1`, so it is a fusion weight and never an absolute
|
|
629
|
+
* similarity. `semanticRaw` carries the raw embedding cosine on the
|
|
630
|
+
* embedder's own scale, which is the only form that can be compared
|
|
631
|
+
* against an absolute floor such as the abstention threshold in
|
|
632
|
+
* `shouldAbstain`. Consumers fall back to `semantic` when
|
|
633
|
+
* `semanticRaw` is absent (hand-built results, custom fusion paths).
|
|
634
|
+
*/
|
|
635
|
+
scores?: {
|
|
636
|
+
keyword?: number;
|
|
637
|
+
/** Normalized semantic-leg contribution (fusion weight, 0..1). */
|
|
638
|
+
semantic?: number;
|
|
639
|
+
/** Raw embedding cosine (absolute similarity, embedder scale). */
|
|
640
|
+
semanticRaw?: number;
|
|
641
|
+
hybrid?: number;
|
|
642
|
+
graph?: number;
|
|
643
|
+
ppr?: number;
|
|
644
|
+
session?: number;
|
|
645
|
+
rerank?: number;
|
|
646
|
+
entity?: number;
|
|
647
|
+
/**
|
|
648
|
+
* Present when read-time contradiction resolution demoted this
|
|
649
|
+
* result: the demotion factor applied (see
|
|
650
|
+
* CONTRADICTION_DEMOTION_FACTOR in src/contradictions.ts).
|
|
651
|
+
*/
|
|
652
|
+
contradiction_demoted?: number;
|
|
653
|
+
entityLeg?: number;
|
|
654
|
+
/**
|
|
655
|
+
* Raw CLIP cosine from the image-memory leg (`image_embeddings`,
|
|
656
|
+
* scanned by `querySimilarImages`). Present only when Track B is
|
|
657
|
+
* enabled and the leg actually retrieved this candidate — an absent
|
|
658
|
+
* value means the image leg did not surface it, exactly like
|
|
659
|
+
* `entityLeg`.
|
|
660
|
+
*/
|
|
661
|
+
imageLeg?: number;
|
|
662
|
+
/**
|
|
663
|
+
* Present when provenance-trust weighting ran: the per-tier trust
|
|
664
|
+
* multiplier applied (`1 - strength * (1 - tierTrust)` — see
|
|
665
|
+
* `provenanceMultiplier` in src/provenance.ts).
|
|
666
|
+
*/
|
|
667
|
+
provenance?: number;
|
|
668
|
+
/**
|
|
669
|
+
* Present when retrieval-time anomaly scoring ran: the demotion
|
|
670
|
+
* factor applied because the memory's write-time attack-shape
|
|
671
|
+
* similarity was high while the query was not attack-shaped (see
|
|
672
|
+
* `applyRetrievalAnomalyScoring` in src/memory.ts).
|
|
673
|
+
*/
|
|
674
|
+
retrievalAnomaly?: number;
|
|
675
|
+
};
|
|
676
|
+
/**
|
|
677
|
+
* Relevance-gate outcome (see `RelevanceGateConfig` /
|
|
678
|
+
* `applyRelevanceGate`). Present on every result only when the gate is
|
|
679
|
+
* enabled (`retrieval.relevanceGate.enabled`, or `MEMOS_RELEVANCE_GATE=1`)
|
|
680
|
+
* — an absent field means the gate never ran, so the list is exactly
|
|
681
|
+
* what the pre-gate pipeline produced.
|
|
682
|
+
*
|
|
683
|
+
* `abstained` is `false` on every kept result: abstention empties the
|
|
684
|
+
* result list rather than marking survivors.
|
|
685
|
+
*/
|
|
686
|
+
relevanceGate?: RelevanceGateResult;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Lifecycle status of a recorded contradiction pair.
|
|
690
|
+
*
|
|
691
|
+
* - `unresolved`: detected at write time by the rule-based detector;
|
|
692
|
+
* both versions are kept (add-only). Read-time resolution demotes the
|
|
693
|
+
* older member. Sleep-time LLM adjudication (future) may resolve it.
|
|
694
|
+
* - `resolved`: a contradiction outcome was confirmed through the
|
|
695
|
+
* evidence state machine (e.g. an old memory superseded by a new
|
|
696
|
+
* version) — the pair is recorded for provenance.
|
|
697
|
+
*/
|
|
698
|
+
export type ContradictionStatus = "unresolved" | "resolved";
|
|
699
|
+
/**
|
|
700
|
+
* A recorded contradiction pair: two memory nodes about the same
|
|
701
|
+
* subject that disagree. Node ids are stored in canonical (sorted)
|
|
702
|
+
* order so the UNIQUE(node_a, node_b) constraint dedups pairs
|
|
703
|
+
* regardless of insertion order.
|
|
704
|
+
*/
|
|
705
|
+
export interface ContradictionRecord {
|
|
706
|
+
id: string;
|
|
707
|
+
nodeA: string;
|
|
708
|
+
nodeB: string;
|
|
709
|
+
detectedAt: number;
|
|
710
|
+
status: ContradictionStatus;
|
|
711
|
+
}
|
|
712
|
+
/** How to address the memory a revert applies to. */
|
|
713
|
+
export type RevertScopeInput = {
|
|
714
|
+
kind: "last";
|
|
715
|
+
} | {
|
|
716
|
+
kind: "id";
|
|
717
|
+
id: string;
|
|
718
|
+
} | {
|
|
719
|
+
kind: "entity";
|
|
720
|
+
entity: string;
|
|
721
|
+
} | {
|
|
722
|
+
kind: "text";
|
|
723
|
+
text: string;
|
|
724
|
+
};
|
|
725
|
+
/**
|
|
726
|
+
* Options for {@link MemOS.revert}.
|
|
727
|
+
*/
|
|
728
|
+
export interface RevertOptions {
|
|
729
|
+
/** Who issued the revert (free text, e.g. "user", "agent:coder"). Default "user". */
|
|
730
|
+
actor?: string;
|
|
731
|
+
/** Why the revert was issued — recorded on the audit event. */
|
|
732
|
+
reason?: string;
|
|
733
|
+
/** If true, resolve and report the target without writing anything. Default false. */
|
|
734
|
+
dryRun?: boolean;
|
|
735
|
+
/** Namespace to resolve "last"/named scopes in. Default "default". */
|
|
736
|
+
namespace?: string;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* The resolved target of a revert command, plus the other candidates
|
|
740
|
+
* that matched (empty when the scope was unambiguous). When several
|
|
741
|
+
* memories match a named target, the most recently created valid memory
|
|
742
|
+
* wins and the rest are reported here for confirmation.
|
|
743
|
+
*/
|
|
744
|
+
export interface RevertTargetResolution {
|
|
745
|
+
/** The memory that would be (or was) reverted. */
|
|
746
|
+
target: MemoryNode;
|
|
747
|
+
/** The scope that produced this target. */
|
|
748
|
+
scope: RevertScopeInput;
|
|
749
|
+
/** Other memories that matched the scope (most recent first). */
|
|
750
|
+
alternatives: ScoredMemory[];
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* The outcome of {@link MemOS.revert}.
|
|
754
|
+
*
|
|
755
|
+
* Ledger mechanics: the target's validity interval is closed at `at`
|
|
756
|
+
* (add-only — the row stays in history), the predecessor's interval is
|
|
757
|
+
* reopened (`validTo = null`), and the revert itself is recorded as an
|
|
758
|
+
* audit event node (tags `audit`/`revert`, metadata.audit === "revert").
|
|
759
|
+
*/
|
|
760
|
+
export interface RevertResult {
|
|
761
|
+
/** The reverted-away memory — now historical (validTo = at). */
|
|
762
|
+
target: MemoryNode;
|
|
763
|
+
/** The reactivated predecessor — the belief recall returns now. */
|
|
764
|
+
predecessor: MemoryNode;
|
|
765
|
+
/** ID of the audit event node, or null on dry-run. */
|
|
766
|
+
auditId: string | null;
|
|
767
|
+
/** Unix ms when the revert executed (0 on dry-run). */
|
|
768
|
+
at: number;
|
|
769
|
+
/** True when nothing was written. */
|
|
770
|
+
dryRun: boolean;
|
|
771
|
+
/** Other candidates that matched the scope. */
|
|
772
|
+
alternatives: ScoredMemory[];
|
|
773
|
+
}
|
|
774
|
+
/** Outcome feedback for a procedural lesson. */
|
|
775
|
+
export type LessonOutcome = "success" | "failure";
|
|
776
|
+
/**
|
|
777
|
+
* A procedural lesson: behavioral guidance ("check rate limits before
|
|
778
|
+
* batch calls") rather than a fact. Captured via `memorizeProcedural`,
|
|
779
|
+
* reinforced/demoted by `recordLessonOutcome`, and surfaced via
|
|
780
|
+
* `recallProcedural` / context-pack injection. Scoring is pure local
|
|
781
|
+
* math — see `src/procedural.ts`.
|
|
782
|
+
*/
|
|
783
|
+
export interface ProceduralLesson {
|
|
784
|
+
id: string;
|
|
785
|
+
/** The lesson text — an instruction for future behavior. */
|
|
786
|
+
lesson: string;
|
|
787
|
+
/** When/where the lesson applies (free text). */
|
|
788
|
+
context: string;
|
|
789
|
+
tags: string[];
|
|
790
|
+
/** Stored score in [0.01, 0.99]; read-time decay is applied lazily. */
|
|
791
|
+
score: number;
|
|
792
|
+
successCount: number;
|
|
793
|
+
failureCount: number;
|
|
794
|
+
useCount: number;
|
|
795
|
+
namespace: string;
|
|
796
|
+
createdAt: number;
|
|
797
|
+
updatedAt: number;
|
|
798
|
+
lastUsedAt: number;
|
|
799
|
+
}
|
|
800
|
+
/** Input for creating a procedural lesson (id/timestamps assigned). */
|
|
801
|
+
export interface NewProceduralLesson {
|
|
802
|
+
lesson: string;
|
|
803
|
+
context?: string;
|
|
804
|
+
tags?: string[];
|
|
805
|
+
namespace?: string;
|
|
806
|
+
/** Override the neutral 0.5 prior. Clamped to [0.01, 0.99]. */
|
|
807
|
+
initialScore?: number;
|
|
808
|
+
}
|
|
809
|
+
export type EmbeddingVector = number[];
|
|
810
|
+
/**
|
|
811
|
+
* Runtime resolution metadata for an embedding provider instance.
|
|
812
|
+
*
|
|
813
|
+
* Benchmarks (and any caller who cares about reproducibility) use this to
|
|
814
|
+
* verify that the provider they REQUESTED is the provider that actually
|
|
815
|
+
* loaded — in particular to detect the FastEmbed local-hash fallback that
|
|
816
|
+
* activates when the transformers package is missing.
|
|
817
|
+
*/
|
|
818
|
+
export interface EmbeddingRuntimeInfo {
|
|
819
|
+
/** Provider kind the caller asked for, e.g. "fastembed". */
|
|
820
|
+
requestedProvider: string;
|
|
821
|
+
/** Provider actually serving embeddings, e.g. "fastembed" or
|
|
822
|
+
* "local-hash-fallback". Equal to `requestedProvider` when no fallback. */
|
|
823
|
+
resolvedProvider: string;
|
|
824
|
+
/** Model the caller asked for. */
|
|
825
|
+
requestedModel: string;
|
|
826
|
+
/** Model actually producing vectors. */
|
|
827
|
+
resolvedModel: string;
|
|
828
|
+
/** Model revision/commit when the provider exposes one, else null. */
|
|
829
|
+
modelRevision: string | null;
|
|
830
|
+
/** Dimensions the caller declared. */
|
|
831
|
+
requestedDimensions: number;
|
|
832
|
+
/** Dimensions observed on the most recent embed call, or null before
|
|
833
|
+
* the first call. Differs from `requestedDimensions` only when the
|
|
834
|
+
* provider's real output shape disagrees with configuration. */
|
|
835
|
+
observedDimensions: number | null;
|
|
836
|
+
/** True when the requested backend did NOT load and a fallback is
|
|
837
|
+
* serving embeddings instead. Only meaningful after the first embed
|
|
838
|
+
* call (or an explicit probe) has run. */
|
|
839
|
+
fallbackActive: boolean;
|
|
840
|
+
/** Human-readable reason for the fallback, or null when none occurred. */
|
|
841
|
+
fallbackReason: string | null;
|
|
842
|
+
}
|
|
843
|
+
export interface EmbeddingProvider {
|
|
844
|
+
/** Stable provider identifier, e.g. "local-hash" or "ollama". */
|
|
845
|
+
id: string;
|
|
846
|
+
/** Human-readable embedding model name. */
|
|
847
|
+
model: string;
|
|
848
|
+
/** Expected vector dimensionality. */
|
|
849
|
+
dimensions: number;
|
|
850
|
+
/**
|
|
851
|
+
* Runtime resolution metadata (requested vs. resolved provider/model,
|
|
852
|
+
* fallback status). Optional so custom providers need not implement it;
|
|
853
|
+
* when absent, callers should assume the provider IS what it claims and
|
|
854
|
+
* there is no fallback. The built-in FastEmbed provider implements it to
|
|
855
|
+
* expose the local-hash fallback state.
|
|
856
|
+
*/
|
|
857
|
+
getRuntimeInfo?(): EmbeddingRuntimeInfo;
|
|
858
|
+
/** Generate a normalized embedding vector for a text input. */
|
|
859
|
+
embed(text: string): Promise<EmbeddingVector>;
|
|
860
|
+
/**
|
|
861
|
+
* Optional batch embedding. If the provider can batch, override this to
|
|
862
|
+
* amortize network round-trips. Default implementation calls `embed` for
|
|
863
|
+
* each text in order. The returned vectors must be in the same order as
|
|
864
|
+
* the input `texts` array.
|
|
865
|
+
*/
|
|
866
|
+
batchEmbed?(texts: string[]): Promise<EmbeddingVector[]>;
|
|
867
|
+
/**
|
|
868
|
+
* Embed a QUERY — a short natural-language retrieval question.
|
|
869
|
+
*
|
|
870
|
+
* Many modern retrieval models (e.g. the Liquid E / e5 families, and
|
|
871
|
+
* Cohere embed-v3) are trained for an asymmetric query/document setting:
|
|
872
|
+
* they expect an instruction or "query" prefix on the query side and plain
|
|
873
|
+
* text on the document side. Routing queries through a dedicated method lets
|
|
874
|
+
* providers apply that signal, which materially improves semantic recall for
|
|
875
|
+
* paraphrase questions.
|
|
876
|
+
*
|
|
877
|
+
* Optional for backward compatibility: when a provider does not implement it,
|
|
878
|
+
* `embedQuery`/`embedDocuments` default to the plain `embed` path so
|
|
879
|
+
* existing custom providers keep working unchanged.
|
|
880
|
+
*/
|
|
881
|
+
embedQuery?(text: string): Promise<EmbeddingVector>;
|
|
882
|
+
/**
|
|
883
|
+
* Embed a batch of DOCUMENTS (the persisted memory contents) for indexing.
|
|
884
|
+
*
|
|
885
|
+
* Optional; defaults to `batchEmbed`, falling back to per-text `embed`.
|
|
886
|
+
* Providers that distinguish query from document mode should use the document
|
|
887
|
+
* input type here (e.g. Cohere `search_document`).
|
|
888
|
+
*/
|
|
889
|
+
embedDocuments?(texts: string[]): Promise<EmbeddingVector[]>;
|
|
890
|
+
/**
|
|
891
|
+
* Optional image encoder. Text and CLIP vectors live in different
|
|
892
|
+
* vector spaces (and different tables), so this is an OPTIONAL extra
|
|
893
|
+
* capability, never the provider's primary contract — a text provider
|
|
894
|
+
* that also encodes images can satisfy both interfaces, and the
|
|
895
|
+
* image-embedding path must never be handed to the text embedding
|
|
896
|
+
* queue. See {@link EmbedImageProvider} for the dedicated contract.
|
|
897
|
+
*/
|
|
898
|
+
embedImage?(image: Buffer | string): Promise<EmbeddingVector>;
|
|
899
|
+
}
|
|
900
|
+
export type EmbeddingProviderKind = "local-hash" | "ollama" | "openai-compatible" | "voyage" | "cohere" | "fastembed";
|
|
901
|
+
export interface EmbeddingConfig {
|
|
902
|
+
/** Enables embedding generation. Defaults to true when semantic search is enabled. */
|
|
903
|
+
enabled?: boolean;
|
|
904
|
+
/** Built-in provider kind, or a custom provider implementation. */
|
|
905
|
+
provider?: EmbeddingProviderKind | EmbeddingProvider;
|
|
906
|
+
/** Provider model name. */
|
|
907
|
+
model?: string;
|
|
908
|
+
/** Embedding vector dimensions. */
|
|
909
|
+
dimensions?: number;
|
|
910
|
+
/** Base URL for Ollama or OpenAI-compatible embedding endpoints. */
|
|
911
|
+
baseUrl?: string;
|
|
912
|
+
/** API key for OpenAI-compatible embedding endpoints. */
|
|
913
|
+
apiKey?: string;
|
|
914
|
+
/**
|
|
915
|
+
* Prefix prepended to QUERY text before embedding. Asymmetric retrieval
|
|
916
|
+
* models are trained with a query-side instruction and silently degrade
|
|
917
|
+
* without it — e.g. Liquid LFM2.5-Embedding and e5 models expect
|
|
918
|
+
* `"query: "`. Empty (default) sends queries as-is.
|
|
919
|
+
*/
|
|
920
|
+
queryPrefix?: string;
|
|
921
|
+
/**
|
|
922
|
+
* Prefix prepended to DOCUMENT text (stored memory content) before
|
|
923
|
+
* embedding at index time. e.g. Liquid LFM2.5-Embedding and e5 models
|
|
924
|
+
* expect `"document: "`. Empty (default) sends documents as-is.
|
|
925
|
+
*/
|
|
926
|
+
documentPrefix?: string;
|
|
927
|
+
/**
|
|
928
|
+
* Which node text to embed at store time: the raw `content`, or
|
|
929
|
+
* `summary + "\n" + content` (the historical default — for short facts
|
|
930
|
+
* the extractive summary roughly doubles the embedded tokens).
|
|
931
|
+
*/
|
|
932
|
+
embedText?: "content" | "summary+content";
|
|
933
|
+
}
|
|
934
|
+
export interface EmbeddingRecordInfo {
|
|
935
|
+
model: string;
|
|
936
|
+
dimensions: number;
|
|
937
|
+
updatedAt: number;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Per-node embedding pipeline status. `pending` means the node has been
|
|
941
|
+
* stored but its embedding has not been computed yet. `ready` means the
|
|
942
|
+
* persisted vector is fresh for the current provider/model. `failed`
|
|
943
|
+
* means the most recent attempt gave up after retries.
|
|
944
|
+
*/
|
|
945
|
+
export type EmbeddingNodeStatus = "pending" | "queued" | "running" | "ready" | "failed";
|
|
946
|
+
/** Lightweight status snapshot returned by `memos.embeddingStatus()`. */
|
|
947
|
+
export interface EmbeddingNodeStatusInfo {
|
|
948
|
+
nodeId: string;
|
|
949
|
+
status: EmbeddingNodeStatus;
|
|
950
|
+
attempts: number;
|
|
951
|
+
lastError: string | null;
|
|
952
|
+
updatedAt: number;
|
|
953
|
+
}
|
|
954
|
+
/** Aggregate status returned by `memos.embeddingStatus()` (no args). */
|
|
955
|
+
export interface EmbeddingQueueStatus {
|
|
956
|
+
pending: number;
|
|
957
|
+
running: number;
|
|
958
|
+
total: number;
|
|
959
|
+
nodes: EmbeddingNodeStatusInfo[];
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Configuration for the background embedding queue. All fields are
|
|
963
|
+
* optional; sensible defaults are used when omitted.
|
|
964
|
+
*/
|
|
965
|
+
export interface EmbeddingQueueConfig {
|
|
966
|
+
/** Max concurrent in-flight embed calls. Default 2. */
|
|
967
|
+
concurrency?: number;
|
|
968
|
+
/**
|
|
969
|
+
* Max pending jobs drained into a single `batchEmbed()` call when the
|
|
970
|
+
* provider supports batching (array-input HTTP endpoints). Default 1 =
|
|
971
|
+
* one `embed()` call per job. Groups that fail fall back to per-job
|
|
972
|
+
* retries.
|
|
973
|
+
*/
|
|
974
|
+
batchSize?: number;
|
|
975
|
+
/** Max buffered jobs before `enqueue` throws. Default 10000. */
|
|
976
|
+
maxQueueSize?: number;
|
|
977
|
+
/** Max retry attempts per job before marking it failed. Default 3. */
|
|
978
|
+
maxRetries?: number;
|
|
979
|
+
/** Base backoff in ms; retries use exponential backoff. Default 250. */
|
|
980
|
+
retryBackoffMs?: number;
|
|
981
|
+
/**
|
|
982
|
+
* Coalescing delay in ms before a fully-idle queue picks up
|
|
983
|
+
* newly-enqueued work. Default 5. Lets rapid sequential `store()` calls
|
|
984
|
+
* form full `batchSize` groups instead of one single-job group each.
|
|
985
|
+
* Set to 0 for immediate microtask pickup.
|
|
986
|
+
*/
|
|
987
|
+
batchLingerMs?: number;
|
|
988
|
+
/**
|
|
989
|
+
* If true, `store()` waits for the embedding to complete before
|
|
990
|
+
* returning. Default false — background mode. Tests and the CLI
|
|
991
|
+
* `memos flush-embeddings` command benefit from this being false.
|
|
992
|
+
*/
|
|
993
|
+
synchronous?: boolean;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Full in-memory graph snapshot.
|
|
997
|
+
*/
|
|
998
|
+
export interface GraphSnapshot {
|
|
999
|
+
nodes: MemoryNode[];
|
|
1000
|
+
edges: MemoryEdge[];
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Mutation recorded in the tamper-evident log.
|
|
1004
|
+
*
|
|
1005
|
+
* - `store` — new memory written via `MemOS.store()` (including event-update
|
|
1006
|
+
* replacements and revert audit entries).
|
|
1007
|
+
* - `import` — record imported with its original identity (`importRecord`,
|
|
1008
|
+
* e.g. encrypted sync restore).
|
|
1009
|
+
* - `update` — node mutated via `updateNode` (content, tags, metadata,
|
|
1010
|
+
* validity, provenance…), TTL changes, and `setValidity`.
|
|
1011
|
+
* - `forget` — node deleted via `MemOS.forget()`.
|
|
1012
|
+
*/
|
|
1013
|
+
export type TamperOp = "store" | "import" | "update" | "forget";
|
|
1014
|
+
/**
|
|
1015
|
+
* One entry of the tamper-evident mutation log.
|
|
1016
|
+
*
|
|
1017
|
+
* Entries form a hash chain: `entryHash = sha256("memos-tamper-entry-v2"|
|
|
1018
|
+
* seq|ts|op|nodeId|nodeHash|prevHash)` where `prevHash` is the previous
|
|
1019
|
+
* entry's `entryHash` (or a fixed genesis constant for seq 1) and `seq`
|
|
1020
|
+
* is the entry's 1-based chain position, assigned atomically by the
|
|
1021
|
+
* storage adapter. `nodeHash` is `sha256` over a canonical serialization
|
|
1022
|
+
* of the node's full mutable state at mutation time (content, summary,
|
|
1023
|
+
* type, tags, importance, namespace, metadata, provenance, validity,
|
|
1024
|
+
* TTL, timestamps — everything except volatile read stats) — for
|
|
1025
|
+
* `forget` it is the hash of the deleted node's final state, so
|
|
1026
|
+
* deletions are provable too.
|
|
1027
|
+
*/
|
|
1028
|
+
export interface TamperLogEntry {
|
|
1029
|
+
seq: number;
|
|
1030
|
+
ts: number;
|
|
1031
|
+
op: TamperOp;
|
|
1032
|
+
nodeId: string;
|
|
1033
|
+
nodeHash: string;
|
|
1034
|
+
prevHash: string;
|
|
1035
|
+
entryHash: string;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* A tamper-log entry as supplied by the caller. The storage adapter
|
|
1039
|
+
* assigns the chain position atomically: it determines `prevHash` from
|
|
1040
|
+
* the current tip, allocates `seq`, and computes `entryHash` — all in
|
|
1041
|
+
* one transaction — then returns the complete stored entry.
|
|
1042
|
+
*/
|
|
1043
|
+
export type NewTamperLogEntry = Pick<TamperLogEntry, "ts" | "op" | "nodeId" | "nodeHash">;
|
|
1044
|
+
/**
|
|
1045
|
+
* Backend-agnostic storage contract.
|
|
1046
|
+
*
|
|
1047
|
+
* Any persistence backend (SQLite, Postgres, Redis, Qdrant …) must
|
|
1048
|
+
* implement this interface. The core engine only depends on this
|
|
1049
|
+
* contract, never on a concrete backend.
|
|
1050
|
+
*/
|
|
1051
|
+
export interface StorageAdapter {
|
|
1052
|
+
/** Initialise tables / indices / connections. Called once at startup. */
|
|
1053
|
+
init(): Promise<void>;
|
|
1054
|
+
/** Persist a new memory node. Returns the created node. */
|
|
1055
|
+
saveNode(node: MemoryNode): Promise<MemoryNode>;
|
|
1056
|
+
/** Retrieve a node by ID. Returns `null` when not found.
|
|
1057
|
+
* Implementations should bump the access stats (access_count,
|
|
1058
|
+
* last_accessed) on read. For side-effect-free reads, use `peekNode`. */
|
|
1059
|
+
getNode(id: string): Promise<MemoryNode | null>;
|
|
1060
|
+
/**
|
|
1061
|
+
* Side-effect-free variant of `getNode`. Does not increment
|
|
1062
|
+
* `access_count` or update `last_accessed`. Use for internal lookups
|
|
1063
|
+
* where the read is not a user-initiated retrieval — e.g. tag
|
|
1064
|
+
* updates, TTL changes, eviction candidate selection.
|
|
1065
|
+
*/
|
|
1066
|
+
peekNode?(id: string): Promise<MemoryNode | null>;
|
|
1067
|
+
/**
|
|
1068
|
+
* Delete the single least-important node, where "least important" is
|
|
1069
|
+
* defined as lowest `importance` then oldest `last_accessed`.
|
|
1070
|
+
* Returns the id of the deleted node, or null if the table is empty.
|
|
1071
|
+
* Implementations may fall back to in-memory selection if SQL is
|
|
1072
|
+
* not appropriate.
|
|
1073
|
+
*/
|
|
1074
|
+
evictLeastImportant?(): Promise<string | null>;
|
|
1075
|
+
/** Partially update a node. Returns the updated node. */
|
|
1076
|
+
updateNode(id: string, input: UpdateMemoryInput): Promise<MemoryNode | null>;
|
|
1077
|
+
/** Delete a node and all connected edges. */
|
|
1078
|
+
deleteNode(id: string): Promise<boolean>;
|
|
1079
|
+
/**
|
|
1080
|
+
* Record one read access for a node: bumps `access_count` and
|
|
1081
|
+
* `last_accessed` ONLY. Must not touch `updatedAt` or any content —
|
|
1082
|
+
* a read is not a write, and mutating `updatedAt` on read would
|
|
1083
|
+
* corrupt recency ordering, sync conflict resolution, embedding
|
|
1084
|
+
* freshness, and the tamper-evident log's content cross-check.
|
|
1085
|
+
*
|
|
1086
|
+
* Optional: adapters that don't implement it skip read telemetry
|
|
1087
|
+
* (fail-open). Never route read telemetry through `updateNode` —
|
|
1088
|
+
* see `bumpAccessCounters` in `src/memory.ts`.
|
|
1089
|
+
*/
|
|
1090
|
+
recordAccess?(id: string): void | Promise<void>;
|
|
1091
|
+
/**
|
|
1092
|
+
* Append one entry to the tamper-evident mutation log. The adapter
|
|
1093
|
+
* MUST assign the chain position atomically: determine `prevHash`
|
|
1094
|
+
* from the current tip, allocate `seq`, compute `entryHash`, and
|
|
1095
|
+
* insert — all in a single transaction — then return the complete
|
|
1096
|
+
* stored entry. Optional: adapters that don't implement the log
|
|
1097
|
+
* disable mutation logging (fail-open) and `verifyTamperLog`
|
|
1098
|
+
* reports the log as unsupported.
|
|
1099
|
+
*/
|
|
1100
|
+
appendTamperEntry?(entry: NewTamperLogEntry): Promise<TamperLogEntry>;
|
|
1101
|
+
/** Read the full tamper-evident log in sequence order. */
|
|
1102
|
+
readTamperLog?(): Promise<TamperLogEntry[]>;
|
|
1103
|
+
/** Persist a new edge. Returns the created edge. */
|
|
1104
|
+
saveEdge(edge: MemoryEdge): Promise<MemoryEdge>;
|
|
1105
|
+
/**
|
|
1106
|
+
* Batch-insert multiple edges in a single transaction.
|
|
1107
|
+
* Falls back to individual saveEdge calls when not implemented.
|
|
1108
|
+
*/
|
|
1109
|
+
saveEdgesBatch?(edges: MemoryEdge[]): Promise<MemoryEdge[]>;
|
|
1110
|
+
/** Retrieve an edge by ID. */
|
|
1111
|
+
getEdge(id: string): Promise<MemoryEdge | null>;
|
|
1112
|
+
/** Delete an edge by ID. */
|
|
1113
|
+
deleteEdge(id: string): Promise<boolean>;
|
|
1114
|
+
/**
|
|
1115
|
+
* Scoped-space inventory: every namespace with its live memory count.
|
|
1116
|
+
* Optional — adapters that cannot provide it fall back to a full scan.
|
|
1117
|
+
*/
|
|
1118
|
+
listNamespaces?(): Promise<Array<{
|
|
1119
|
+
namespace: string;
|
|
1120
|
+
count: number;
|
|
1121
|
+
}>>;
|
|
1122
|
+
/**
|
|
1123
|
+
* Close (invalidate) the currently-valid edges incident to a node by
|
|
1124
|
+
* stamping `valid_to = validTo`. Rows are updated, never deleted, so
|
|
1125
|
+
* as-of reads at earlier timestamps still return them.
|
|
1126
|
+
* Returns the number of edges closed.
|
|
1127
|
+
*/
|
|
1128
|
+
closeEdgesForNode?(nodeId: string, validTo: number): Promise<number>;
|
|
1129
|
+
/** Query nodes with filters. */
|
|
1130
|
+
queryNodes(filter: SearchFilter): Promise<ScoredMemory[]>;
|
|
1131
|
+
/**
|
|
1132
|
+
* Per-harness memory counts (`SELECT harness, COUNT(*) … GROUP BY
|
|
1133
|
+
* harness`). Optional — `MemOS.getHarnessCounts()` falls back to a
|
|
1134
|
+
* paged `queryNodes` scan when the adapter does not implement it.
|
|
1135
|
+
*/
|
|
1136
|
+
getHarnessCounts?(): Promise<HarnessCount[]>;
|
|
1137
|
+
/** Persist an embedding vector for a node. */
|
|
1138
|
+
saveEmbedding?(nodeId: string, vector: EmbeddingVector, model: string): Promise<void>;
|
|
1139
|
+
/** Return stored embedding metadata for startup backfill decisions. */
|
|
1140
|
+
getEmbeddingInfo?(nodeId: string): Promise<EmbeddingRecordInfo | null>;
|
|
1141
|
+
/**
|
|
1142
|
+
* Return stored embedding metadata for every node in one round trip.
|
|
1143
|
+
* Optional bulk counterpart to `getEmbeddingInfo` — lets startup
|
|
1144
|
+
* backfill avoid one SELECT per node. Storages that don't implement it
|
|
1145
|
+
* fall back to per-node `getEmbeddingInfo` calls.
|
|
1146
|
+
*/
|
|
1147
|
+
getAllEmbeddingInfos?(): Promise<Array<{
|
|
1148
|
+
nodeId: string;
|
|
1149
|
+
} & EmbeddingRecordInfo>>;
|
|
1150
|
+
/** Query nodes by vector similarity. */
|
|
1151
|
+
querySimilarEmbeddings?(vector: EmbeddingVector, filter: SearchFilter, limit: number, threshold?: number,
|
|
1152
|
+
/**
|
|
1153
|
+
* When provided, only rows stored by this exact model are compared.
|
|
1154
|
+
* Cosine similarity is meaningless across models, and two different
|
|
1155
|
+
* models can share a dimensionality — dimension equality alone does
|
|
1156
|
+
* not prevent silently mixing incompatible vectors.
|
|
1157
|
+
*/
|
|
1158
|
+
model?: string): Promise<ScoredMemory[]>;
|
|
1159
|
+
/** Counts of stored embeddings grouped by model (reindex/audit support). */
|
|
1160
|
+
getEmbeddingModelCounts?(): Promise<Array<{
|
|
1161
|
+
model: string;
|
|
1162
|
+
count: number;
|
|
1163
|
+
}>>;
|
|
1164
|
+
/**
|
|
1165
|
+
* Bulk-fetch stored embedding vectors for the given node ids.
|
|
1166
|
+
* Used by write-time contradiction detection to avoid one SELECT per
|
|
1167
|
+
* neighbor. Returns only ids that have a stored vector.
|
|
1168
|
+
*/
|
|
1169
|
+
getEmbeddingVectors?(nodeIds: string[]): Promise<Map<string, EmbeddingVector>>;
|
|
1170
|
+
/** Delete all embedding rows stored by the given model. Returns the count. */
|
|
1171
|
+
deleteEmbeddingsByModel?(model: string): Promise<number>;
|
|
1172
|
+
/**
|
|
1173
|
+
* Persist a CLIP image vector for a node, in the SEPARATE
|
|
1174
|
+
* `image_embeddings` space. Never mixed with text `embeddings`:
|
|
1175
|
+
* cosine similarity across the two spaces is meaningless.
|
|
1176
|
+
*/
|
|
1177
|
+
saveImageEmbedding?(nodeId: string, vector: EmbeddingVector, model: string): Promise<void>;
|
|
1178
|
+
/** Every stored image vector (node id + vector + model), for reindex/audit. */
|
|
1179
|
+
getAllImageEmbeddings?(): Promise<Array<{
|
|
1180
|
+
nodeId: string;
|
|
1181
|
+
vector: EmbeddingVector;
|
|
1182
|
+
model: string;
|
|
1183
|
+
}>>;
|
|
1184
|
+
/**
|
|
1185
|
+
* Query image memories by CLIP vector similarity. Behaves exactly like
|
|
1186
|
+
* `querySimilarEmbeddings`, except it scans `image_embeddings` and
|
|
1187
|
+
* filters on model + dimensions — a 768-d text vector must never be
|
|
1188
|
+
* scored against a 512-d CLIP query.
|
|
1189
|
+
*/
|
|
1190
|
+
querySimilarImages?(vector: EmbeddingVector, filter: SearchFilter, limit: number, threshold?: number,
|
|
1191
|
+
/** Only rows stored by this exact CLIP model are compared. */
|
|
1192
|
+
model?: string): Promise<ScoredMemory[]>;
|
|
1193
|
+
/**
|
|
1194
|
+
* Image-embedding model counts, for `memos diagnostics` / reindex
|
|
1195
|
+
* support. Grouped by model like `getEmbeddingModelCounts`.
|
|
1196
|
+
*/
|
|
1197
|
+
getImageEmbeddingModelCounts?(): Promise<Array<{
|
|
1198
|
+
model: string;
|
|
1199
|
+
count: number;
|
|
1200
|
+
}>>;
|
|
1201
|
+
/**
|
|
1202
|
+
* Delete the image-embedding row for a node (used by the node-delete
|
|
1203
|
+
* path alongside the blob unlink). Returns true when a row was removed.
|
|
1204
|
+
*/
|
|
1205
|
+
deleteImageEmbedding?(nodeId: string): Promise<boolean>;
|
|
1206
|
+
/** Query edges, optionally filtered by source/target. */
|
|
1207
|
+
queryEdges(filter?: {
|
|
1208
|
+
sourceId?: string;
|
|
1209
|
+
targetId?: string;
|
|
1210
|
+
relation?: EdgeRelation;
|
|
1211
|
+
/**
|
|
1212
|
+
* Bitemporal as-of read: when provided, only edges whose event-time
|
|
1213
|
+
* validity interval covers this Unix-ms timestamp are returned
|
|
1214
|
+
* (NULL bounds are open-ended). Default returns all edges.
|
|
1215
|
+
*/
|
|
1216
|
+
validAt?: number;
|
|
1217
|
+
}): Promise<MemoryEdge[]>;
|
|
1218
|
+
/**
|
|
1219
|
+
* Record a contradiction pair between two memory nodes. Node ids are
|
|
1220
|
+
* canonicalized (sorted) by the implementation so the UNIQUE constraint
|
|
1221
|
+
* dedups reversed pairs. Insert is idempotent (no-op when the pair is
|
|
1222
|
+
* already recorded).
|
|
1223
|
+
*/
|
|
1224
|
+
addContradiction?(nodeA: string, nodeB: string, status?: ContradictionStatus): Promise<ContradictionRecord>;
|
|
1225
|
+
/**
|
|
1226
|
+
* Return recorded contradiction pairs where node_a OR node_b is in
|
|
1227
|
+
* `nodeIds` — the bounded read-time lookup for
|
|
1228
|
+
* `resolveContradictionsAtRead`.
|
|
1229
|
+
*/
|
|
1230
|
+
getContradictionPairsFor?(nodeIds: string[]): Promise<ContradictionRecord[]>;
|
|
1231
|
+
/** Transition a contradiction record between `unresolved`/`resolved`. */
|
|
1232
|
+
updateContradictionStatus?(id: string, status: ContradictionStatus): Promise<void>;
|
|
1233
|
+
/**
|
|
1234
|
+
* Procedural lessons (optional — only SQLiteStorage implements these
|
|
1235
|
+
* today). `updateProceduralLesson` applies a partial patch (score,
|
|
1236
|
+
* counts, timestamps) and returns the updated record, or null when
|
|
1237
|
+
* the id is unknown.
|
|
1238
|
+
*/
|
|
1239
|
+
saveProceduralLesson?(lesson: NewProceduralLesson): Promise<ProceduralLesson>;
|
|
1240
|
+
getProceduralLesson?(id: string): Promise<ProceduralLesson | null>;
|
|
1241
|
+
updateProceduralLesson?(id: string, patch: Partial<ProceduralLesson>): Promise<ProceduralLesson | null>;
|
|
1242
|
+
listProceduralLessons?(namespace?: string): Promise<ProceduralLesson[]>;
|
|
1243
|
+
/**
|
|
1244
|
+
* Find memory nodes whose id (dashes ignored) starts with `prefix`
|
|
1245
|
+
* (hex, case-insensitive). Backs citation resolution; capped by the
|
|
1246
|
+
* implementation to a bounded candidate list.
|
|
1247
|
+
*/
|
|
1248
|
+
findNodesByIdPrefix?(prefix: string): Promise<MemoryNode[]>;
|
|
1249
|
+
/** Return the full graph (nodes + edges). */
|
|
1250
|
+
getGraph(): Promise<GraphSnapshot>;
|
|
1251
|
+
/**
|
|
1252
|
+
* Return the graph as of a bitemporal timestamp (unix ms): nodes and
|
|
1253
|
+
* edges whose validity interval covers `atTime`. Optional so
|
|
1254
|
+
* third-party adapters are not forced to implement history; callers
|
|
1255
|
+
* should fall back to filtering `getGraph()` with the same predicates.
|
|
1256
|
+
*/
|
|
1257
|
+
getGraphAtTime?(atTime: number): Promise<GraphSnapshot>;
|
|
1258
|
+
/**
|
|
1259
|
+
* Return every persisted embedding (node id + vector + model). Used
|
|
1260
|
+
* by memory consolidation. Implementations may return an empty
|
|
1261
|
+
* array when embeddings are not stored.
|
|
1262
|
+
*/
|
|
1263
|
+
getAllEmbeddings?(): Promise<Array<{
|
|
1264
|
+
nodeId: string;
|
|
1265
|
+
vector: EmbeddingVector;
|
|
1266
|
+
model: string;
|
|
1267
|
+
}>>;
|
|
1268
|
+
/**
|
|
1269
|
+
* Update only the `namespace` of a node. Used by `archive` to
|
|
1270
|
+
* move a memory to the `archived` namespace.
|
|
1271
|
+
*/
|
|
1272
|
+
setNodeNamespace?(id: string, namespace: string): Promise<void>;
|
|
1273
|
+
/** Set TTL on a node. */
|
|
1274
|
+
setTTL(id: string, seconds: number): Promise<void>;
|
|
1275
|
+
/** Clear TTL on a node. */
|
|
1276
|
+
clearTTL(id: string): Promise<void>;
|
|
1277
|
+
/** Delete expired nodes and return the count of deleted nodes. */
|
|
1278
|
+
sweepExpired(): Promise<number>;
|
|
1279
|
+
/** Get all nodes with a specific tag. */
|
|
1280
|
+
queryNodesByTag(tag: string): Promise<MemoryNode[]>;
|
|
1281
|
+
/**
|
|
1282
|
+
* ── item2: entity leg ── candidate generation for hybrid search: node
|
|
1283
|
+
* ids whose indexed entities intersect `entities`, ranked by number of
|
|
1284
|
+
* matched entities (desc), node id tiebreak. Backed by the
|
|
1285
|
+
* `entity_index` inverted table in the SQLite storage. Optional —
|
|
1286
|
+
* storages without an entity index omit this and the entity RRF leg
|
|
1287
|
+
* degrades to no candidates.
|
|
1288
|
+
*/
|
|
1289
|
+
searchByEntities?(entities: string[], limit: number): Promise<Array<{
|
|
1290
|
+
nodeId: string;
|
|
1291
|
+
matches: number;
|
|
1292
|
+
}>>;
|
|
1293
|
+
/** Delete all nodes and edges (batch operation). */
|
|
1294
|
+
deleteAllNodes(): Promise<void>;
|
|
1295
|
+
/** Close connections and release resources. */
|
|
1296
|
+
close(): Promise<void>;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Experimental feature flags.
|
|
1300
|
+
*/
|
|
1301
|
+
/**
|
|
1302
|
+
* Wire format a context pack will be serialized to. Used by the
|
|
1303
|
+
* budget-accounting path to charge each item what it actually costs on
|
|
1304
|
+
* the wire (TOON row scaffolding / JSON field names, citations)
|
|
1305
|
+
* instead of the legacy flat `content + summary + 4` estimate.
|
|
1306
|
+
*
|
|
1307
|
+
* See `wireFormat` on the context-pack options in
|
|
1308
|
+
* `src/context-pack.ts`.
|
|
1309
|
+
*/
|
|
1310
|
+
export type ContextPackWireFormat = "json" | "toon" | "toon-compact";
|
|
1311
|
+
/**
|
|
1312
|
+
* Compression knobs for the context-pack budget path — the switch that
|
|
1313
|
+
* finally uses `compressText` from `src/compression.ts`.
|
|
1314
|
+
*
|
|
1315
|
+
* Compression is attempted ONLY for an item that does not fit the
|
|
1316
|
+
* pack's `tokenBudget`: the compressor runs over the item's
|
|
1317
|
+
* already-debloated content (and summary) and the item is admitted when
|
|
1318
|
+
* the compressed form fits. An item that still does not fit after
|
|
1319
|
+
* compression is skipped exactly as it is today, so enabling
|
|
1320
|
+
* compression can only ever ADD items to a pack, never remove them.
|
|
1321
|
+
*
|
|
1322
|
+
* Default `enabled: false`, deliberately: `compressText` is a lossy
|
|
1323
|
+
* stopword filter ("the deploy pipeline" → "deploy pipeline"), so the
|
|
1324
|
+
* default build keeps every content byte identical to the
|
|
1325
|
+
* pre-compression pack. Opt in when you want the extra recall and
|
|
1326
|
+
* accept terser evidence text.
|
|
1327
|
+
*/
|
|
1328
|
+
export interface ContextPackCompressionOptions {
|
|
1329
|
+
/**
|
|
1330
|
+
* Try `compressText` on items that would otherwise be dropped for
|
|
1331
|
+
* not fitting the token budget. Default false — when false the
|
|
1332
|
+
* compressor is never invoked and no content is altered.
|
|
1333
|
+
*/
|
|
1334
|
+
enabled?: boolean;
|
|
1335
|
+
/**
|
|
1336
|
+
* Compression aggressiveness. `"light"` drops articles, copulas and
|
|
1337
|
+
* common prepositions; `"aggressive"` additionally drops filler
|
|
1338
|
+
* adverbs and most pronouns (roughly 40-60% of prose tokens — read
|
|
1339
|
+
* the output before trusting it). Default `"light"`.
|
|
1340
|
+
*/
|
|
1341
|
+
level?: CompressionLevel;
|
|
1342
|
+
}
|
|
1343
|
+
export interface ExperimentalConfig {
|
|
1344
|
+
/**
|
|
1345
|
+
* @deprecated No longer required — `semanticSearch()` works without it.
|
|
1346
|
+
* Kept as a shorthand for enabling embeddings with the default provider
|
|
1347
|
+
* (equivalent to `embeddings: { enabled: true }`).
|
|
1348
|
+
*/
|
|
1349
|
+
semanticSearch?: boolean;
|
|
1350
|
+
/**
|
|
1351
|
+
* Context-pack budget compression. When `enabled`, items that do not
|
|
1352
|
+
* fit `contextPack({ tokenBudget })` are first re-rendered through
|
|
1353
|
+
* `compressText` (`src/compression.ts`) and admitted if the compressed
|
|
1354
|
+
* form fits; only items that are still too big are skipped.
|
|
1355
|
+
*
|
|
1356
|
+
* Default off: compression is lossy, so the default pack contents stay
|
|
1357
|
+
* byte-identical to the pre-compression build. Read the rationale in
|
|
1358
|
+
* `ContextPackCompressionOptions`.
|
|
1359
|
+
*/
|
|
1360
|
+
packCompression?: ContextPackCompressionOptions;
|
|
1361
|
+
/** Enable graph visualization export. */
|
|
1362
|
+
graphViz?: boolean;
|
|
1363
|
+
/** Enable namespace isolation. */
|
|
1364
|
+
namespaces?: boolean;
|
|
1365
|
+
/** Enable context injection from graph neighbours. */
|
|
1366
|
+
contextInjection?: boolean;
|
|
1367
|
+
/**
|
|
1368
|
+
* Extract semantic (subject, predicate, object) triples at write time
|
|
1369
|
+
* and store them in node metadata. Enables triple-aware retrieval
|
|
1370
|
+
* boosting. Experimental, default off.
|
|
1371
|
+
*/
|
|
1372
|
+
tripleExtraction?: boolean;
|
|
1373
|
+
/**
|
|
1374
|
+
* Apply Ebbinghaus forgetting-curve reinforcement to retrieval scoring.
|
|
1375
|
+
* Memories decay exponentially by time-since-access divided by access
|
|
1376
|
+
* count; frequently-recalled memories stay strong. Experimental,
|
|
1377
|
+
* default off.
|
|
1378
|
+
*/
|
|
1379
|
+
ebbinghaus?: boolean;
|
|
1380
|
+
/**
|
|
1381
|
+
* Run the evidence state machine on every `store()`: similar existing
|
|
1382
|
+
* memories are reinforced, revised, or superseded, and near-duplicates
|
|
1383
|
+
* (similarity >= 0.92) are folded into the existing node instead of
|
|
1384
|
+
* being written again. Default: true.
|
|
1385
|
+
*/
|
|
1386
|
+
evidenceOnStore?: boolean;
|
|
1387
|
+
/**
|
|
1388
|
+
* After a node's embedding is persisted, auto-link it to its top
|
|
1389
|
+
* embedding-similarity neighbours (cosine >= 0.6, max 8 edges) in
|
|
1390
|
+
* addition to the bag-of-words links. Only active when an embedding
|
|
1391
|
+
* provider is configured. Default: true.
|
|
1392
|
+
*/
|
|
1393
|
+
embeddingAutoLink?: boolean;
|
|
1394
|
+
/**
|
|
1395
|
+
* Apply a gentle recency boost to hybrid search ranking. Off by
|
|
1396
|
+
* default: enabling it makes scores time-dependent, which relaxes the
|
|
1397
|
+
* score-reproducibility guarantee in `docs/ai-trio-contracts.md`.
|
|
1398
|
+
* Default: false.
|
|
1399
|
+
*/
|
|
1400
|
+
recencyBoost?: boolean;
|
|
1401
|
+
/**
|
|
1402
|
+
* Graph-expansion recall leg for hybrid search. After fusion, the top
|
|
1403
|
+
* `maxSeeds` results are expanded along their graph edges and unseen
|
|
1404
|
+
* neighbours are injected into the ranking at `seedScore * scoreFactor`
|
|
1405
|
+
* (capped at `maxAdded` extra nodes). Useful for multi-evidence /
|
|
1406
|
+
* multi-hop queries where related facts are connected by
|
|
1407
|
+
* `relates_to`/`supports` edges but neither retrieval leg surfaces them.
|
|
1408
|
+
* Off by default.
|
|
1409
|
+
*/
|
|
1410
|
+
graphExpansion?: {
|
|
1411
|
+
enabled?: boolean;
|
|
1412
|
+
/** How many top fused results to expand along edges. Default 3. */
|
|
1413
|
+
maxSeeds?: number;
|
|
1414
|
+
/** Maximum neighbours injected per query. Default 5. */
|
|
1415
|
+
maxAdded?: number;
|
|
1416
|
+
/** Injected neighbour score = seed score × this factor. Default 0.85. */
|
|
1417
|
+
scoreFactor?: number;
|
|
1418
|
+
};
|
|
1419
|
+
/**
|
|
1420
|
+
* Two-stage reranking: after fusion, the top `candidates` results are
|
|
1421
|
+
* re-scored by a cross-encoder and the final ranking is reordered by
|
|
1422
|
+
* relevance score. The first stage (hybrid search) exists to recall;
|
|
1423
|
+
* the reranker decides what the caller actually sees. Two providers:
|
|
1424
|
+
*
|
|
1425
|
+
* - `"endpoint"` (default when `endpoint` is set): re-scores via an HTTP
|
|
1426
|
+
* `/rerank` server (e.g. llama-server with bge-reranker-v2-m3).
|
|
1427
|
+
* - `"local"`: in-process cross-encoder through `@huggingface/transformers`
|
|
1428
|
+
* (default model `Xenova/bge-reranker-base`, ~280 MB, downloaded on
|
|
1429
|
+
* first use). No server needed; needs one of the transformers packages
|
|
1430
|
+
* installed.
|
|
1431
|
+
*
|
|
1432
|
+
* On reranker failure the fused order is kept unchanged (graceful
|
|
1433
|
+
* degradation). Off by default.
|
|
1434
|
+
*/
|
|
1435
|
+
/**
|
|
1436
|
+
* Cosine threshold for the opt-in `filter.semanticDedup` in hybrid
|
|
1437
|
+
* search. Default 0.95 — near-verbatim duplicates only.
|
|
1438
|
+
*/
|
|
1439
|
+
searchDedupThreshold?: number;
|
|
1440
|
+
/**
|
|
1441
|
+
* Round 4: RM3-lite pseudo-relevance feedback for hybrid search.
|
|
1442
|
+
* For short queries (≤3 content terms) whose primary keyword leg is
|
|
1443
|
+
* thin (<5 hits), harvest up to 3 expansion terms from the top-5
|
|
1444
|
+
* semantic hits and re-probe FTS5 as a second keyword pass. Union
|
|
1445
|
+
* semantics: expansion can only ADD candidates, never demote or
|
|
1446
|
+
* remove primary-leg results (expansion-only hits are demoted below
|
|
1447
|
+
* every primary hit, since expansion bm25 scores are not comparable
|
|
1448
|
+
* to primary-query bm25 scores).
|
|
1449
|
+
*
|
|
1450
|
+
* Off by default: PRF is a recall booster with query-drift risk —
|
|
1451
|
+
* enable it only after measuring a win on your eval (deterministic
|
|
1452
|
+
* eval was neutral: 0.9792/0.9688 with and without). `MEMOS_PRF=1`
|
|
1453
|
+
* forces it on for experiments without a config change.
|
|
1454
|
+
* Default: false.
|
|
1455
|
+
*/
|
|
1456
|
+
prfExpansion?: boolean;
|
|
1457
|
+
rerank?: {
|
|
1458
|
+
/**
|
|
1459
|
+
* Rerank provider: `"endpoint"` (HTTP `/rerank` server) or `"local"`
|
|
1460
|
+
* (in-process cross-encoder). Default: `"endpoint"` when `endpoint` is
|
|
1461
|
+
* set, `"local"` when `localModel` is set. `MEMOS_RERANK_LOCAL=1`
|
|
1462
|
+
* forces the local provider for experiments without a config change.
|
|
1463
|
+
*/
|
|
1464
|
+
provider?: "endpoint" | "local";
|
|
1465
|
+
/** Base URL of the rerank server, e.g. "http://127.0.0.1:8081".
|
|
1466
|
+
* Receives `POST {endpoint}/rerank` with
|
|
1467
|
+
* `{ model, query, documents: string[] }`. Required for the
|
|
1468
|
+
* `"endpoint"` provider; ignored for `"local"`. */
|
|
1469
|
+
endpoint?: string;
|
|
1470
|
+
/** Bearer token for the rerank endpoint, when required. */
|
|
1471
|
+
apiKey?: string;
|
|
1472
|
+
/** Model name sent to the endpoint (many local servers ignore it). */
|
|
1473
|
+
model?: string;
|
|
1474
|
+
/**
|
|
1475
|
+
* Local cross-encoder model id for `provider: "local"`.
|
|
1476
|
+
* Default `"Xenova/bge-reranker-base"`. Overridable for
|
|
1477
|
+
* experiments with `MEMOS_RERANK_MODEL`.
|
|
1478
|
+
*/
|
|
1479
|
+
localModel?: string;
|
|
1480
|
+
/**
|
|
1481
|
+
* Weight dtype for the local model (`from_pretrained` dtype, e.g.
|
|
1482
|
+
* `"q8"`, `"fp32"`). Default `"q8"` — much faster on CPU with
|
|
1483
|
+
* negligible ranking impact. Overridable with `MEMOS_RERANK_DTYPE`.
|
|
1484
|
+
*/
|
|
1485
|
+
localDtype?: string;
|
|
1486
|
+
/** How many fused candidates to submit for reranking. Default 50.
|
|
1487
|
+
* Overridable for experiments with `MEMOS_RERANK_CANDIDATES`. */
|
|
1488
|
+
candidates?: number;
|
|
1489
|
+
/**
|
|
1490
|
+
* Truncate each reranked document to this many characters before
|
|
1491
|
+
* sending. Cross-encoder relevance signal lives in the first few
|
|
1492
|
+
* hundred tokens, and capping pair length keeps the request's token
|
|
1493
|
+
* count predictable — long-tail documents otherwise force many extra
|
|
1494
|
+
* GPU waves on the rerank server. Default 0 = send full documents.
|
|
1495
|
+
*/
|
|
1496
|
+
maxDocChars?: number;
|
|
1497
|
+
/** Request timeout in ms. Default 5000. */
|
|
1498
|
+
timeoutMs?: number;
|
|
1499
|
+
};
|
|
1500
|
+
/**
|
|
1501
|
+
* Session-neighbour expansion for hybrid search. After fusion, the top
|
|
1502
|
+
* `maxSeeds` results are expanded along their same-conversation
|
|
1503
|
+
* neighbours and unseen siblings are injected into the ranking at a
|
|
1504
|
+
* decaying `seed.score * scoreFactor^k`. Multi-hop answers typically
|
|
1505
|
+
* span many turns of one session, so recovering the neighbours of a
|
|
1506
|
+
* single relevant turn raises all-evidence recall without another
|
|
1507
|
+
* embedding pass. Default: true (injected siblings sit behind every
|
|
1508
|
+
* organically-retrieved result, so head ordering never moves).
|
|
1509
|
+
*/
|
|
1510
|
+
sessionExpansion?: boolean;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Relevance-gate knobs for hybrid search (see `applyRelevanceGate` and
|
|
1514
|
+
* `shouldAbstain` in `src/retrieval.ts`).
|
|
1515
|
+
*
|
|
1516
|
+
* Two independent problems, one knob each:
|
|
1517
|
+
*
|
|
1518
|
+
* - ABSTENTION. When nothing in the store is genuinely about the query,
|
|
1519
|
+
* the honest answer is "no memories" — not "the ten least-bad rows".
|
|
1520
|
+
* A memory layer that always returns k results forces the caller to
|
|
1521
|
+
* invent an answer.
|
|
1522
|
+
* - TAIL TRIMMING. Precision@10 measured 0.099 on LoCoMo: about nine of
|
|
1523
|
+
* every ten injected items were noise. Dropping candidates that trail
|
|
1524
|
+
* the top score by a large factor costs little recall and sharply
|
|
1525
|
+
* improves a context pack's signal-to-noise ratio.
|
|
1526
|
+
*
|
|
1527
|
+
* Lives in `types.ts` (dependency-free) and is forwarded by
|
|
1528
|
+
* `MemOSConfig.retrieval.relevanceGate` into every hybrid search
|
|
1529
|
+
* (`search`, `contextPack`, `multiStreamSearch`, `recall`'s scoring
|
|
1530
|
+
* pipeline). Off unless `enabled` is true — see the field's default.
|
|
1531
|
+
*/
|
|
1532
|
+
export interface RelevanceGateConfig {
|
|
1533
|
+
/**
|
|
1534
|
+
* Master switch for the whole gate (tail trimming AND abstention).
|
|
1535
|
+
*
|
|
1536
|
+
* Defaults to `false`. This is a published SDK: an enabled gate
|
|
1537
|
+
* changes how many memories `search()` returns for the same query
|
|
1538
|
+
* (results whose fused score trails the top hit by more than
|
|
1539
|
+
* `1 - ratio` are dropped), and silently shrinking a consumer's result
|
|
1540
|
+
* count on upgrade is not a behaviour change we can make for them.
|
|
1541
|
+
* Turning it on is deliberately one explicit field:
|
|
1542
|
+
*
|
|
1543
|
+
* ```ts
|
|
1544
|
+
* new MemOS({ retrieval: { relevanceGate: { enabled: true } } })
|
|
1545
|
+
* // or, for a one-off experiment with no config change:
|
|
1546
|
+
* // MEMOS_RELEVANCE_GATE=1
|
|
1547
|
+
* ```
|
|
1548
|
+
*/
|
|
1549
|
+
enabled?: boolean;
|
|
1550
|
+
/**
|
|
1551
|
+
* Keep only candidates whose fused score is at least
|
|
1552
|
+
* `topScore * ratio`. `0` disables trimming while leaving abstention
|
|
1553
|
+
* (if configured) active.
|
|
1554
|
+
*
|
|
1555
|
+
* Default 0.15 (`DEFAULT_RELEVANCE_RATIO` in `src/retrieval.ts`) —
|
|
1556
|
+
* deliberately low: it exists to cut the long tail of "matched one
|
|
1557
|
+
* stopword" rows, not to make fine-grained relevance decisions.
|
|
1558
|
+
*/
|
|
1559
|
+
ratio?: number;
|
|
1560
|
+
/**
|
|
1561
|
+
* Abstain (return nothing) when no candidate reaches this cosine
|
|
1562
|
+
* similarity on the semantic leg AND nothing matched the query
|
|
1563
|
+
* literally. `undefined` disables abstention entirely.
|
|
1564
|
+
*
|
|
1565
|
+
* Deliberately unset by default even when the gate is enabled: the
|
|
1566
|
+
* floor is embedder-dependent — a 256-d hash embedder and a 1024-d
|
|
1567
|
+
* transformer do not share a cosine scale. Calibrate against your
|
|
1568
|
+
* embedder before switching this on (0.3 is a reasonable starting
|
|
1569
|
+
* point for paraphrase-quality embedders — see
|
|
1570
|
+
* `DEFAULT_ABSTAIN_SIMILARITY`).
|
|
1571
|
+
*/
|
|
1572
|
+
abstainSimilarity?: number;
|
|
1573
|
+
/**
|
|
1574
|
+
* Safety floor: never trim below this many results, whatever the ratio
|
|
1575
|
+
* says. Useful for callers that need at least N items to render a view.
|
|
1576
|
+
* `0` means no floor.
|
|
1577
|
+
*/
|
|
1578
|
+
minResults?: number;
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Outcome of one relevance-gate pass. Additive metadata — never breaks
|
|
1582
|
+
* the `ScoredMemory[]` shape `search()` has always returned.
|
|
1583
|
+
*
|
|
1584
|
+
* Surface points:
|
|
1585
|
+
* - `ScoredMemory.relevanceGate` on every result of a gated search;
|
|
1586
|
+
* - the `{ results, gate }` member pair returned by
|
|
1587
|
+
* `MemOS.searchWithGate()`.
|
|
1588
|
+
*/
|
|
1589
|
+
export interface RelevanceGateResult {
|
|
1590
|
+
/**
|
|
1591
|
+
* True when the gate decided nothing in the store is relevant to the
|
|
1592
|
+
* query. The result list is empty and the caller should answer from
|
|
1593
|
+
* its own knowledge rather than from memory.
|
|
1594
|
+
*/
|
|
1595
|
+
abstained: boolean;
|
|
1596
|
+
/** True when at least one candidate was dropped by tail trimming. */
|
|
1597
|
+
trimmed: boolean;
|
|
1598
|
+
/** How many fused candidates the gate removed. */
|
|
1599
|
+
dropped: number;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Tunable knobs for the hybrid-search Reciprocal Rank Fusion. Lives in
|
|
1603
|
+
* `types.ts` (dependency-free) and is re-exported by `src/retrieval.ts`,
|
|
1604
|
+
* where `fuseResults()` consumes it. `MemOSConfig.fusion` forwards these
|
|
1605
|
+
* to every hybrid search; unset fields fall back to `retrieval.ts`
|
|
1606
|
+
* defaults.
|
|
1607
|
+
*/
|
|
1608
|
+
export interface FusionOptions {
|
|
1609
|
+
/** RRF constant K. Larger K flattens rank differences. Default 60. */
|
|
1610
|
+
rrfK?: number;
|
|
1611
|
+
/** Weight of the keyword (FTS5) leg. Default 0.8. */
|
|
1612
|
+
keywordWeight?: number;
|
|
1613
|
+
/** Weight of the semantic (embedding) leg. Default 0.2. */
|
|
1614
|
+
semanticWeight?: number;
|
|
1615
|
+
/**
|
|
1616
|
+
* Lower bound of the trust multiplier (applied as
|
|
1617
|
+
* `trustFloor + trustScore * (1 - trustFloor)`). Default 0.7.
|
|
1618
|
+
* Set to 1.0 to disable trust weighting.
|
|
1619
|
+
*/
|
|
1620
|
+
trustFloor?: number;
|
|
1621
|
+
/**
|
|
1622
|
+
* Strength of the confidence/trust combined multiplier from the
|
|
1623
|
+
* evidence state machine. Default 0.35. Set to 0 to disable
|
|
1624
|
+
* confidence-aware ranking.
|
|
1625
|
+
*/
|
|
1626
|
+
confidenceWeightStrength?: number;
|
|
1627
|
+
/**
|
|
1628
|
+
* Recency half-life in ms for the tie-break among near-equal scores.
|
|
1629
|
+
* Default 30 days. Set to 0 to disable recency tie-breaking.
|
|
1630
|
+
*/
|
|
1631
|
+
recencyHalfLifeMs?: number;
|
|
1632
|
+
/**
|
|
1633
|
+
* Reference "now" for recency computations. Defaults to wall-clock;
|
|
1634
|
+
* injectable for deterministic tests/benchmarks.
|
|
1635
|
+
*/
|
|
1636
|
+
nowMs?: number;
|
|
1637
|
+
/**
|
|
1638
|
+
* Entities extracted from the query (populated automatically by
|
|
1639
|
+
* `hybridSearch` via `extractQueryEntities`). When non-empty, memories
|
|
1640
|
+
* whose tags / `metadata.entities` overlap the query entities receive a
|
|
1641
|
+
* post-fusion boost — the third retrieval signal alongside keyword and
|
|
1642
|
+
* semantic (see the 2026 fused multi-signal retrieval consensus).
|
|
1643
|
+
*/
|
|
1644
|
+
queryEntities?: string[];
|
|
1645
|
+
/**
|
|
1646
|
+
* Strength of the entity-overlap boost: `score *= 1 + entityWeight *
|
|
1647
|
+
* overlap`. Default 0.15. Set to 0 to disable entity-fused scoring.
|
|
1648
|
+
*
|
|
1649
|
+
* Distinct from `entityLegWeight`: the overlap boost only re-scores
|
|
1650
|
+
* candidates the keyword/semantic legs already retrieved, while the
|
|
1651
|
+
* entity leg adds recall (entity-matched memories the other legs
|
|
1652
|
+
* missed enter the candidate pool).
|
|
1653
|
+
*/
|
|
1654
|
+
entityWeight?: number;
|
|
1655
|
+
/**
|
|
1656
|
+
* ── item2: entity leg ── weight of the entity-inverted-index RRF leg
|
|
1657
|
+
* (the third retrieval signal alongside keyword and semantic). Applied
|
|
1658
|
+
* as `entityLegWeight / (rrfK + rank)` per candidate, so entity-matched
|
|
1659
|
+
* memories surface even when FTS and embeddings missed them. Default
|
|
1660
|
+
* 0.1 (deliberately below the semantic leg's 0.2 weight — see
|
|
1661
|
+
* DEFAULT_ENTITY_LEG_WEIGHT). Set to 0 to disable the leg.
|
|
1662
|
+
*/
|
|
1663
|
+
entityLegWeight?: number;
|
|
1664
|
+
/**
|
|
1665
|
+
* Strength of the provenance-tier trust multiplier from the
|
|
1666
|
+
* provenance-trust layer. Applied as
|
|
1667
|
+
* `score *= 1 - strength * (1 - tierTrust)` with per-tier trust from
|
|
1668
|
+
* `PROVENANCE_TRUST` (user-verified 1.0 … imported 0.72). Default
|
|
1669
|
+
* 0.5 — low-trust content is slightly down-ranked without overriding
|
|
1670
|
+
* relevance. Set to 0 to disable trust-weighted recall. Calibrated
|
|
1671
|
+
* neutral-or-better on the retrieval eval (scripts/eval-retrieval.ts).
|
|
1672
|
+
*/
|
|
1673
|
+
provenanceWeightStrength?: number;
|
|
1674
|
+
/**
|
|
1675
|
+
* Single-step PPR-lite graph expansion after fusion (HippoRAG-style,
|
|
1676
|
+
* see `src/graph-expansion.ts`). The top `graphExpansionSeeds` fused
|
|
1677
|
+
* results seed a personalized PageRank (damping 0.5, `graphExpansionHops`
|
|
1678
|
+
* hops) over graph edges plus shared-entity links; the final score
|
|
1679
|
+
* blends as `(1 - graphExpansionAlpha) * fused + graphExpansionAlpha *
|
|
1680
|
+
* pprNorm` with PPR scores normalized to [0,1]. Graph neighbours of
|
|
1681
|
+
* seeds that neither retrieval leg surfaced can enter the results this
|
|
1682
|
+
* way — the multi-hop recall win without iterative LLM retrieval.
|
|
1683
|
+
* Default: false (opt-in; expansion is off unless enabled here or per
|
|
1684
|
+
* query via `SearchFilter.graphExpansion: true`). When disabled the
|
|
1685
|
+
* pipeline is byte-identical to pre-expansion behaviour (no extra
|
|
1686
|
+
* storage reads, no score changes).
|
|
1687
|
+
*/
|
|
1688
|
+
graphExpansion?: boolean;
|
|
1689
|
+
/**
|
|
1690
|
+
* Blend weight of the normalized PPR score in the post-fusion blend.
|
|
1691
|
+
* Default 0.2.
|
|
1692
|
+
*/
|
|
1693
|
+
graphExpansionAlpha?: number;
|
|
1694
|
+
/** PPR neighbourhood depth explored from each seed. Default 2. */
|
|
1695
|
+
graphExpansionHops?: number;
|
|
1696
|
+
/** How many top fused results seed the PPR walk. Default 10. */
|
|
1697
|
+
graphExpansionSeeds?: number;
|
|
1698
|
+
/**
|
|
1699
|
+
* Fusion algorithm for combining the retrieval legs.
|
|
1700
|
+
*
|
|
1701
|
+
* - `"rrf"` — weighted Reciprocal Rank Fusion (rank-based voting,
|
|
1702
|
+
* robust to score outliers; the pre-round-4 default).
|
|
1703
|
+
* - `"convex"` — min-max normalize each leg's native scores to [0,1],
|
|
1704
|
+
* then take the weighted sum with renormalized leg weights. Lets
|
|
1705
|
+
* strong absolute scores (a near-exact embedding match, a dominant
|
|
1706
|
+
* bm25 hit) dominate instead of being flattened to rank votes.
|
|
1707
|
+
* Default `"convex"` (round 4; Sept 2026 TOIS analysis found convex
|
|
1708
|
+
* combination beats RRF in- and out-of-domain, and Weaviate made the
|
|
1709
|
+
* same switch). Set to `"rrf"` to restore the legacy behavior.
|
|
1710
|
+
*/
|
|
1711
|
+
fusionMode?: "rrf" | "convex";
|
|
1712
|
+
/**
|
|
1713
|
+
* Raw query text, used only for the deterministic adaptive leg
|
|
1714
|
+
* weighting (short-query keyword emphasis, entity-dense entity-leg
|
|
1715
|
+
* emphasis, temporal-query recency). Populated automatically by
|
|
1716
|
+
* `hybridSearch`; no effect when `adaptiveWeighting` is false.
|
|
1717
|
+
*/
|
|
1718
|
+
query?: string;
|
|
1719
|
+
/**
|
|
1720
|
+
* Master switch for the deterministic query-type adaptive weighting
|
|
1721
|
+
* (round 4). Default true. Set to false for fixed leg weights.
|
|
1722
|
+
*/
|
|
1723
|
+
adaptiveWeighting?: boolean;
|
|
1724
|
+
/**
|
|
1725
|
+
* Strength of the temporal-query recency multiplier:
|
|
1726
|
+
* `score *= 1 + temporalRecencyStrength * 0.5^(age/halfLife)`,
|
|
1727
|
+
* applied only when the query carries temporal markers (today,
|
|
1728
|
+
* yesterday, last week, …) and `adaptiveWeighting` is on. Default
|
|
1729
|
+
* 0.05 (at most a 5% nudge toward fresh memories). Set to 0 to
|
|
1730
|
+
* disable.
|
|
1731
|
+
*/
|
|
1732
|
+
temporalRecencyStrength?: number;
|
|
1733
|
+
/**
|
|
1734
|
+
* Weight of the CLIP image leg (Track B of image memory). Applied
|
|
1735
|
+
* exactly like the entity leg: `imageLegWeight / (rrfK + rank)` per
|
|
1736
|
+
* candidate in RRF mode, as a renormalized convex term otherwise.
|
|
1737
|
+
*
|
|
1738
|
+
* Defaults to {@link DEFAULT_IMAGE_LEG_WEIGHT} (0.1) whenever the leg
|
|
1739
|
+
* actually has results, so Track B contributes without any config.
|
|
1740
|
+
* The leg is inert when `vision.clip.enabled` is unset or the store
|
|
1741
|
+
* holds no image embeddings — a text-only install never pays for it
|
|
1742
|
+
* and its results are byte-identical to before. Set to 0 explicitly
|
|
1743
|
+
* to disable the leg even when images exist.
|
|
1744
|
+
*/
|
|
1745
|
+
imageLegWeight?: number;
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Configuration options for a MemOS instance.
|
|
1749
|
+
*/
|
|
1750
|
+
export interface MemOSConfig {
|
|
1751
|
+
/**
|
|
1752
|
+
* Optional write-time contextual enrichment hook (contextual retrieval,
|
|
1753
|
+
* Anthropic-style). Called once per `store()` with the memory's signals;
|
|
1754
|
+
* return a single line describing the situation the memory was captured
|
|
1755
|
+
* in (e.g. "stated while debugging the deploy pipeline"). The line is
|
|
1756
|
+
* stored in `metadata.context`, prepended to the embedded text, and
|
|
1757
|
+
* surfaced in context packs — it makes embeddings carry situational
|
|
1758
|
+
* meaning, which is the best-attested cheap retrieval upgrade in the
|
|
1759
|
+
* literature. Wire this to a local LLM (0.6B-class is enough) or any
|
|
1760
|
+
* heuristic. Errors are swallowed (fail-soft) and `opts.context`
|
|
1761
|
+
* always takes precedence over the hook.
|
|
1762
|
+
*/
|
|
1763
|
+
enrichMemory?: (input: {
|
|
1764
|
+
content: string;
|
|
1765
|
+
type: MemoryType;
|
|
1766
|
+
tags: string[];
|
|
1767
|
+
source: MemorySource;
|
|
1768
|
+
namespace: string;
|
|
1769
|
+
}) => Promise<string | null | undefined> | string | null | undefined;
|
|
1770
|
+
/**
|
|
1771
|
+
* Deployment-specific entity aliases for fused retrieval scoring,
|
|
1772
|
+
* merged over the built-in table (`BUILTIN_ENTITY_ALIASES` in
|
|
1773
|
+
* `src/entity-extraction.ts`). Keys and values are matched
|
|
1774
|
+
* case-insensitively. Use this to canonicalize domain vocabulary —
|
|
1775
|
+
* e.g. `{ "sso": "auth", "okta": "auth" }` — so memories surface no
|
|
1776
|
+
* matter which phrasing a query uses.
|
|
1777
|
+
*/
|
|
1778
|
+
entityAliases?: Record<string, string>;
|
|
1779
|
+
/**
|
|
1780
|
+
* Optional LLM distillation hook for `summarizeCluster` /
|
|
1781
|
+
* `consolidate`. Receives the contents of one cluster and returns an
|
|
1782
|
+
* abstractive summary (a runbook-style note — the highest-value
|
|
1783
|
+
* granularity in the LongMemEval-V2 ablations). When omitted or when
|
|
1784
|
+
* the hook fails/returns empty, the extractive summarizer is used.
|
|
1785
|
+
* Wire this to a local 0.6B–4B model for fully-offline consolidation.
|
|
1786
|
+
*/
|
|
1787
|
+
summarizeClusterLlm?: (input: {
|
|
1788
|
+
contents: string[];
|
|
1789
|
+
sourceIds: string[];
|
|
1790
|
+
}) => Promise<string | null | undefined> | string | null | undefined;
|
|
1791
|
+
/**
|
|
1792
|
+
* Hybrid-search fusion tuning forwarded to `fuseResults()` on every
|
|
1793
|
+
* hybrid search. Unset fields use `src/retrieval.ts` defaults
|
|
1794
|
+
* (keyword 0.8 / semantic 0.2 / K 60 — tuned for the hash baseline;
|
|
1795
|
+
* stronger embedding models generally want a higher semantic weight).
|
|
1796
|
+
*/
|
|
1797
|
+
fusion?: FusionOptions;
|
|
1798
|
+
/**
|
|
1799
|
+
* Path to the SQLite database file.
|
|
1800
|
+
* @default "~/.memos/memos.db"
|
|
1801
|
+
*/
|
|
1802
|
+
dbPath?: string;
|
|
1803
|
+
/**
|
|
1804
|
+
* Enable WAL mode for SQLite.
|
|
1805
|
+
* @default true
|
|
1806
|
+
*/
|
|
1807
|
+
wal?: boolean;
|
|
1808
|
+
/**
|
|
1809
|
+
* Maximum number of memories to keep before LRU eviction.
|
|
1810
|
+
* Set to `0` to disable eviction.
|
|
1811
|
+
* @default 0
|
|
1812
|
+
*/
|
|
1813
|
+
maxMemories?: number;
|
|
1814
|
+
/**
|
|
1815
|
+
* Auto-link threshold: minimum cosine-similarity score (bag-of-words)
|
|
1816
|
+
* between two nodes to automatically create an edge.
|
|
1817
|
+
* Set to `0` to disable auto-linking.
|
|
1818
|
+
* @default 0.3
|
|
1819
|
+
*/
|
|
1820
|
+
autoLinkThreshold?: number;
|
|
1821
|
+
/**
|
|
1822
|
+
* Custom storage adapter. When provided, `dbPath` is ignored.
|
|
1823
|
+
*/
|
|
1824
|
+
storage?: StorageAdapter;
|
|
1825
|
+
/**
|
|
1826
|
+
* Options forwarded to the built-in SQLite storage adapter when
|
|
1827
|
+
* `storage` is not supplied. `vectorCacheEntries` bounds the in-process
|
|
1828
|
+
* parsed-vector cache (default 25,000 ≈ 100MB at 1024 dims); lower it
|
|
1829
|
+
* for memory-constrained hosts.
|
|
1830
|
+
*/
|
|
1831
|
+
storageOptions?: {
|
|
1832
|
+
vectorCacheEntries?: number;
|
|
1833
|
+
/**
|
|
1834
|
+
* How the semantic leg (`querySimilarEmbeddings`) picks which stored
|
|
1835
|
+
* vectors to score.
|
|
1836
|
+
*
|
|
1837
|
+
* - `"exact"` (default): every candidate vector is scored. Cosine
|
|
1838
|
+
* results and rankings are bit-identical to the pre-optimisation
|
|
1839
|
+
* implementation — the scan only got cheaper per candidate
|
|
1840
|
+
* (cached norms, fused dot product, streaming bounded top-K,
|
|
1841
|
+
* early exit once a perfect match is found).
|
|
1842
|
+
* - `"prefilter"`: candidates are first ranked by a coarse strided
|
|
1843
|
+
* projection and only the top `max(32, limit * 4)` are exactly
|
|
1844
|
+
* rescored. Bounds the scan on large corpora, but is APPROXIMATE:
|
|
1845
|
+
* a true neighbour ranked below the cutoff is never returned.
|
|
1846
|
+
*
|
|
1847
|
+
* Only the built-in SQLite adapter reads this.
|
|
1848
|
+
*/
|
|
1849
|
+
semanticScan?: "exact" | "prefilter";
|
|
1850
|
+
};
|
|
1851
|
+
/**
|
|
1852
|
+
* Encryption key for at-rest encryption of the SQLite database.
|
|
1853
|
+
* Requires the optional `better-sqlite3-multiple-ciphers` driver
|
|
1854
|
+
* (`npm i better-sqlite3-multiple-ciphers`). Falls back to the
|
|
1855
|
+
* `MEMOS_KEY` environment variable. Only NEW databases get encrypted —
|
|
1856
|
+
* convert an existing plaintext DB with `memos encrypt`.
|
|
1857
|
+
*/
|
|
1858
|
+
cipherKey?: string;
|
|
1859
|
+
/**
|
|
1860
|
+
* Interval in seconds for the TTL expiration sweep.
|
|
1861
|
+
* Set to `0` to disable the sweep.
|
|
1862
|
+
* @default 60
|
|
1863
|
+
*/
|
|
1864
|
+
sweepInterval?: number;
|
|
1865
|
+
/**
|
|
1866
|
+
* Experimental feature flags.
|
|
1867
|
+
*/
|
|
1868
|
+
experimental?: ExperimentalConfig;
|
|
1869
|
+
/**
|
|
1870
|
+
* Effective importance model tuning. Controls how recency decay and
|
|
1871
|
+
* access reinforcement modulate the base `importance` for eviction,
|
|
1872
|
+
* archival, decay-forgetting, and (since the ranking wiring) hybrid
|
|
1873
|
+
* search ranking. See `ImportanceConfig` in `src/importance.ts`.
|
|
1874
|
+
*
|
|
1875
|
+
* OPT-IN: leaving this unset (or passing `{}`) keeps the historical
|
|
1876
|
+
* behaviour byte-for-byte — raw `node.importance` is still what
|
|
1877
|
+
* eviction, archival, and ranking use. Setting at least one field
|
|
1878
|
+
* switches those paths to `computeEffectiveImportance()`, so an old,
|
|
1879
|
+
* frequently-reinforced memory now outranks an equally-scored fresh
|
|
1880
|
+
* one, and a long-untouched memory is evicted sooner.
|
|
1881
|
+
*/
|
|
1882
|
+
importance?: ImportanceConfig;
|
|
1883
|
+
/**
|
|
1884
|
+
* Retrieval-quality knobs: the relevance gate (abstention + tail
|
|
1885
|
+
* trimming). Off by default — see `RelevanceGateConfig.enabled` for
|
|
1886
|
+
* why turning it on is an explicit opt-in.
|
|
1887
|
+
*/
|
|
1888
|
+
retrieval?: {
|
|
1889
|
+
relevanceGate?: RelevanceGateConfig;
|
|
1890
|
+
};
|
|
1891
|
+
/**
|
|
1892
|
+
* Half-life (days) for the optional recency boost in hybrid search
|
|
1893
|
+
* ranking. Only used when `experimental.recencyBoost` is enabled.
|
|
1894
|
+
* @default 14
|
|
1895
|
+
*/
|
|
1896
|
+
recencyHalfLifeDays?: number;
|
|
1897
|
+
/**
|
|
1898
|
+
* Embedding provider configuration for semantic and hybrid retrieval.
|
|
1899
|
+
*/
|
|
1900
|
+
embeddings?: EmbeddingConfig;
|
|
1901
|
+
/**
|
|
1902
|
+
* Background embedding queue tuning. Only takes effect when an
|
|
1903
|
+
* embedding provider is configured. See `EmbeddingQueueConfig`.
|
|
1904
|
+
*/
|
|
1905
|
+
embeddingQueue?: EmbeddingQueueConfig;
|
|
1906
|
+
/**
|
|
1907
|
+
* Image memory (vision) configuration. Disabled by default: a bare
|
|
1908
|
+
* `new MemOS()` changes nothing — no new vector rows, no new search
|
|
1909
|
+
* results, no new pack bytes. See {@link VisionConfig}.
|
|
1910
|
+
*/
|
|
1911
|
+
vision?: VisionConfig;
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Coarse taxonomy for what an image *is*, used for filtering and prompt
|
|
1915
|
+
* context — never as the type discriminator (see `metadata.image`).
|
|
1916
|
+
*/
|
|
1917
|
+
export type ImageKind = "photo" | "screenshot" | "chart" | "diagram" | "memo" | "meme" | "other";
|
|
1918
|
+
/**
|
|
1919
|
+
* Reference form of an image carried by context packs and API results.
|
|
1920
|
+
* The bytes are NEVER inlined — consumers resolve the `ref` through the
|
|
1921
|
+
* blob store.
|
|
1922
|
+
*/
|
|
1923
|
+
export interface ImageAttachment {
|
|
1924
|
+
/** Content-addressed key, `img_<sha256[0:12]>`. */
|
|
1925
|
+
ref: string;
|
|
1926
|
+
mime: string;
|
|
1927
|
+
width: number;
|
|
1928
|
+
height: number;
|
|
1929
|
+
kind: ImageKind;
|
|
1930
|
+
/** The caption text stored as the node's `content`. */
|
|
1931
|
+
caption: string;
|
|
1932
|
+
/** Full hex digest of the image bytes (dedupe + integrity). */
|
|
1933
|
+
sha256: string;
|
|
1934
|
+
/** Size of the stored blob in bytes. */
|
|
1935
|
+
bytes: number;
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* The vision payload stored in `MemoryNode.metadata.image`. Presence of
|
|
1939
|
+
* this key — not `type`, not a new `MemoryType` member — is what marks a
|
|
1940
|
+
* node as an image memory (same discriminator pattern as
|
|
1941
|
+
* `metadata.context`), so `MemoryType` exhaustiveness stays intact
|
|
1942
|
+
* everywhere (sync allowlist, fidelity labels, adapters, MCP schema).
|
|
1943
|
+
*/
|
|
1944
|
+
export interface ImageMemoryMeta extends ImageAttachment {
|
|
1945
|
+
/** Blob path, resolved relative to the database directory. */
|
|
1946
|
+
path: string;
|
|
1947
|
+
/** When the image was captured (not stored). Unix ms. */
|
|
1948
|
+
capturedAt: number;
|
|
1949
|
+
/** Optional conversation this image was shared in. */
|
|
1950
|
+
conversationId?: string;
|
|
1951
|
+
/** Who supplied the image. */
|
|
1952
|
+
source: ImageSource;
|
|
1953
|
+
/** Which caption production path produced the caption. */
|
|
1954
|
+
captionSource: "user" | "llm" | "generated";
|
|
1955
|
+
}
|
|
1956
|
+
/** Where an image came from. */
|
|
1957
|
+
export type ImageSource = "user" | "agent" | "tool";
|
|
1958
|
+
/**
|
|
1959
|
+
* CLIP leg status recorded in `metadata.clip.status`. Track A (caption →
|
|
1960
|
+
* text embeddings) works regardless of this value.
|
|
1961
|
+
*/
|
|
1962
|
+
export type ClipStatus = "ready" | "pending" | "failed" | "skipped";
|
|
1963
|
+
/** CLIP bookkeeping recorded in `metadata.clip` when Track B is enabled. */
|
|
1964
|
+
export interface ClipMeta {
|
|
1965
|
+
model: string;
|
|
1966
|
+
dims: number;
|
|
1967
|
+
status: ClipStatus;
|
|
1968
|
+
/** Set when `status` is `"failed"`. */
|
|
1969
|
+
error?: string;
|
|
1970
|
+
/** Unix ms of the last status transition. */
|
|
1971
|
+
updatedAt: number;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* A provider that encodes images — and optionally the CLIP text side, for
|
|
1975
|
+
* text→image queries.
|
|
1976
|
+
*
|
|
1977
|
+
* Deliberately separate from `EmbeddingProvider`: CLIP vectors live in a
|
|
1978
|
+
* different vector space (and a different table) than text embeddings, so
|
|
1979
|
+
* the image encoder must never be handed to the text embedding queue.
|
|
1980
|
+
*/
|
|
1981
|
+
export interface EmbedImageProvider {
|
|
1982
|
+
/** Stable provider identifier, e.g. `"clip"`. */
|
|
1983
|
+
readonly id: string;
|
|
1984
|
+
/** Model the provider encodes with. */
|
|
1985
|
+
readonly model: string;
|
|
1986
|
+
/** Vector dimensionality (512 for `clip-vit-base-patch32`). */
|
|
1987
|
+
readonly dimensions: number;
|
|
1988
|
+
/** Encode image bytes (or a path/URL) to a normalized vector. */
|
|
1989
|
+
embedImage(image: Buffer | string): Promise<EmbeddingVector>;
|
|
1990
|
+
/**
|
|
1991
|
+
* Encode a text query into the CLIP image space. Optional: providers
|
|
1992
|
+
* that can only do image→image search leave it unimplemented, and the
|
|
1993
|
+
* text→image leg silently degrades to the caption leg.
|
|
1994
|
+
*/
|
|
1995
|
+
embedImageText?(query: string): Promise<EmbeddingVector>;
|
|
1996
|
+
}
|
|
1997
|
+
/** Image-memory configuration. Disabled by default — a bare
|
|
1998
|
+
* `new MemOS()` is byte-for-byte unchanged. */
|
|
1999
|
+
export interface VisionConfig {
|
|
2000
|
+
/** Master switch. Default false. */
|
|
2001
|
+
enabled?: boolean;
|
|
2002
|
+
clip?: {
|
|
2003
|
+
/** Track B opt-in. Default false (no ~350 MB model download). */
|
|
2004
|
+
enabled?: boolean;
|
|
2005
|
+
/** CLIP model id. Default `"Xenova/clip-vit-base-patch32"`. */
|
|
2006
|
+
model?: string;
|
|
2007
|
+
/** Expected dimensionality. Default 512. */
|
|
2008
|
+
dimensions?: number;
|
|
2009
|
+
/**
|
|
2010
|
+
* Explicit image encoder. When omitted, `ClipEmbeddingProvider` is
|
|
2011
|
+
* constructed lazily; when the transformers package is missing,
|
|
2012
|
+
* Track B fails soft to Track A — it never throws and never
|
|
2013
|
+
* substitutes a text vector.
|
|
2014
|
+
*/
|
|
2015
|
+
embedImage?: EmbedImageProvider;
|
|
2016
|
+
};
|
|
2017
|
+
/** Blob store root. Default `<dbDir>/memos-images`. */
|
|
2018
|
+
storeRoot?: string;
|
|
2019
|
+
/** Max accepted image size in bytes. Default 5 MiB. */
|
|
2020
|
+
maxImageBytes?: number;
|
|
2021
|
+
/** Max edge length before re-encode is required. Default 4096. */
|
|
2022
|
+
maxDimension?: number;
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Every shape `addImageMemory` accepts, resolved by one normalizer.
|
|
2026
|
+
*
|
|
2027
|
+
* - `string` → a filesystem path (or a `data:` URL, detected by prefix)
|
|
2028
|
+
* - `Buffer` / `Uint8Array` → raw bytes
|
|
2029
|
+
* - `{ data, mime }` → base64 payload plus its declared MIME type
|
|
2030
|
+
* - `{ url }` → an http(s)/file/data URL to fetch
|
|
2031
|
+
*/
|
|
2032
|
+
export type ImageInput = string | Buffer | Uint8Array | {
|
|
2033
|
+
data: string;
|
|
2034
|
+
mime: string;
|
|
2035
|
+
} | {
|
|
2036
|
+
url: string;
|
|
2037
|
+
};
|
|
2038
|
+
/** Options for {@link MemOS.addImageMemory}. Extends the store options. */
|
|
2039
|
+
export interface AddImageMemoryOptions extends Omit<CreateMemoryInput, "content"> {
|
|
2040
|
+
/**
|
|
2041
|
+
* The caption stored as the node's `content` — this is what text
|
|
2042
|
+
* retrieval actually matches. When omitted, a generated placeholder
|
|
2043
|
+
* records the kind and dimensions so the memory is still findable by
|
|
2044
|
+
* tag and still resolvable.
|
|
2045
|
+
*/
|
|
2046
|
+
caption?: string;
|
|
2047
|
+
/** Coarse taxonomy override. Defaults to the MIME-derived kind. */
|
|
2048
|
+
kind?: ImageKind;
|
|
2049
|
+
/** When the image was captured (not stored). Defaults to now. */
|
|
2050
|
+
capturedAt?: number;
|
|
2051
|
+
/** Who supplied the image. Default `"user"`. */
|
|
2052
|
+
imageSource?: ImageSource;
|
|
2053
|
+
/** Optional conversation this image was shared in. */
|
|
2054
|
+
conversationId?: string;
|
|
2055
|
+
}
|
|
2056
|
+
/** What {@link MemOS.addImageMemory} returns. */
|
|
2057
|
+
export interface AddImageMemoryResult {
|
|
2058
|
+
/** The memory node (caption as content, `metadata.image` present). */
|
|
2059
|
+
node: MemoryNode;
|
|
2060
|
+
/** The image payload now carried by the node. */
|
|
2061
|
+
image: ImageMemoryMeta;
|
|
2062
|
+
/** Track B status: `ready` | `pending` | `skipped` | `failed`. */
|
|
2063
|
+
clipStatus: ClipStatus;
|
|
2064
|
+
/** True when identical bytes already existed and no new node was written. */
|
|
2065
|
+
deduped: boolean;
|
|
2066
|
+
}
|
|
2067
|
+
/** Options for {@link MemOS.searchImages}. */
|
|
2068
|
+
export interface SearchImagesOptions {
|
|
2069
|
+
limit?: number;
|
|
2070
|
+
/** Minimum CLIP cosine for the image leg. Default 0.15. */
|
|
2071
|
+
threshold?: number;
|
|
2072
|
+
namespace?: string;
|
|
2073
|
+
namespaces?: string[];
|
|
2074
|
+
/** Restrict to one image kind (e.g. `"diagram"`). */
|
|
2075
|
+
kind?: ImageKind;
|
|
2076
|
+
}
|
|
2077
|
+
/** Options for {@link MemOS.listImages}. */
|
|
2078
|
+
export interface ListImagesOptions {
|
|
2079
|
+
limit?: number;
|
|
2080
|
+
namespace?: string;
|
|
2081
|
+
}
|
|
2082
|
+
/** One image hit: the node, its fused score, and the attachment. */
|
|
2083
|
+
export interface ImageSearchResult {
|
|
2084
|
+
node: MemoryNode;
|
|
2085
|
+
score: number;
|
|
2086
|
+
image: ImageMemoryMeta;
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Export format options.
|
|
2090
|
+
*/
|
|
2091
|
+
export type ExportFormat = "json" | "markdown" | "obsidian";
|
|
2092
|
+
/**
|
|
2093
|
+
* Options for exporting memories.
|
|
2094
|
+
*/
|
|
2095
|
+
export interface ExportOptions {
|
|
2096
|
+
/** Export format. */
|
|
2097
|
+
format?: ExportFormat;
|
|
2098
|
+
/** Output directory path (for markdown/obsidian). */
|
|
2099
|
+
output?: string;
|
|
2100
|
+
/** Only export memories with this tag. */
|
|
2101
|
+
tag?: string;
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2104
|
+
* Result of an export operation.
|
|
2105
|
+
*/
|
|
2106
|
+
export interface ExportResult {
|
|
2107
|
+
/** The format used. */
|
|
2108
|
+
format: ExportFormat;
|
|
2109
|
+
/** For JSON format, the serialized data. For file-based formats, the output directory. */
|
|
2110
|
+
data: string;
|
|
2111
|
+
/** Number of memories exported. */
|
|
2112
|
+
count: number;
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Options for importing memories.
|
|
2116
|
+
*/
|
|
2117
|
+
export interface ImportOptions {
|
|
2118
|
+
/** Source format: json file path, or directory of markdown/obsidian files. */
|
|
2119
|
+
source: string;
|
|
2120
|
+
/** Import format: json, markdown, or obsidian. Auto-detected from path if omitted. */
|
|
2121
|
+
format?: ImportFormat;
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* Import format options.
|
|
2125
|
+
*/
|
|
2126
|
+
export type ImportFormat = "json" | "markdown" | "obsidian";
|
|
2127
|
+
/**
|
|
2128
|
+
* Result of an import operation.
|
|
2129
|
+
*/
|
|
2130
|
+
export interface ImportResult {
|
|
2131
|
+
/** Number of memories imported. */
|
|
2132
|
+
count: number;
|
|
2133
|
+
/** Number of edges recreated (from wikilinks in obsidian format). */
|
|
2134
|
+
edgesCreated: number;
|
|
2135
|
+
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Per-harness memory count, as returned by `getHarnessCounts()` /
|
|
2138
|
+
* `memos harness list`. Memories written before harness attribution
|
|
2139
|
+
* existed read as `"unknown"`.
|
|
2140
|
+
*/
|
|
2141
|
+
export interface HarnessCount {
|
|
2142
|
+
/** Harness slug (or `"unknown"` for unattributed rows). */
|
|
2143
|
+
harness: string;
|
|
2144
|
+
/** Number of memories stamped with this harness. */
|
|
2145
|
+
count: number;
|
|
2146
|
+
}
|
|
2147
|
+
/** Options for `MemOS.mergeHarnessDb` (`memos harness merge`). */
|
|
2148
|
+
export interface HarnessMergeOptions {
|
|
2149
|
+
/**
|
|
2150
|
+
* When true, report the merge plan without writing anything.
|
|
2151
|
+
* Default false.
|
|
2152
|
+
*/
|
|
2153
|
+
dryRun?: boolean;
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* One source node in a harness-merge plan/result. `remapped` is true
|
|
2157
|
+
* when the target DB already held a node with the same id — the source
|
|
2158
|
+
* node is imported under a fresh id and every incident edge endpoint is
|
|
2159
|
+
* rewritten to it.
|
|
2160
|
+
*/
|
|
2161
|
+
export interface HarnessMergeNode {
|
|
2162
|
+
/** Original id in the source DB. */
|
|
2163
|
+
sourceId: string;
|
|
2164
|
+
/** Id under which the node is (or would be) stored in the target. */
|
|
2165
|
+
targetId: string;
|
|
2166
|
+
/** True when the id had to be remapped (collision in the target). */
|
|
2167
|
+
remapped: boolean;
|
|
2168
|
+
/** Harness that originally wrote the memory (preserved verbatim). */
|
|
2169
|
+
harness: string;
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Outcome of `MemOS.mergeHarnessDb` (`memos harness merge --from`).
|
|
2173
|
+
* `dryRun: true` fills the plan fields and writes nothing.
|
|
2174
|
+
*/
|
|
2175
|
+
export interface HarnessMergeResult {
|
|
2176
|
+
/** Source database path. */
|
|
2177
|
+
from: string;
|
|
2178
|
+
/** When true, no writes were performed. */
|
|
2179
|
+
dryRun: boolean;
|
|
2180
|
+
/** Source nodes examined. */
|
|
2181
|
+
sourceNodes: number;
|
|
2182
|
+
/** Source edges examined. */
|
|
2183
|
+
sourceEdges: number;
|
|
2184
|
+
/** Nodes imported (or that would be imported under `dryRun`). */
|
|
2185
|
+
nodesImported: number;
|
|
2186
|
+
/** Nodes whose ids collided and were remapped. */
|
|
2187
|
+
nodesRemapped: number;
|
|
2188
|
+
/** Edges imported (or that would be imported under `dryRun`). */
|
|
2189
|
+
edgesImported: number;
|
|
2190
|
+
/** Edges skipped because their endpoints were missing. */
|
|
2191
|
+
edgesSkipped: number;
|
|
2192
|
+
/** Per-node import plan (id mapping + preserved harness). */
|
|
2193
|
+
nodes: HarnessMergeNode[];
|
|
2194
|
+
/** Milliseconds the merge took. */
|
|
2195
|
+
durationMs: number;
|
|
2196
|
+
}
|
|
2197
|
+
/** Events emitted by the MemoryManager. */
|
|
2198
|
+
export type MemOSEvent = "node:created" | "node:updated" | "node:deleted" | "edge:created" | "edge:deleted" | "link:auto" | "eviction" | "ttl:expired" | "embedding:queued" | "embedding:started" | "embedding:complete" | "embedding:failed" | "embedding:retry" | "consolidation:complete" | "trust:changed" | "validity:changed" | "facts:extracted" | "memory:reinforced" | "memory:superseded" | "memory:reverted" | "contradiction:detected";
|
|
2199
|
+
/** Event listener signature. */
|
|
2200
|
+
export type MemOSEventListener = (data: unknown) => void;
|
|
2201
|
+
/** Options for {@link MemOS.dedupe}. */
|
|
2202
|
+
export interface DedupeOptions {
|
|
2203
|
+
/** Cosine-similarity threshold above which two memories are merged. Default 0.92. */
|
|
2204
|
+
threshold?: number;
|
|
2205
|
+
/** Only consider memories in this namespace. Default "default". */
|
|
2206
|
+
namespace?: string;
|
|
2207
|
+
/** If true, do not delete or merge anything. Default false. */
|
|
2208
|
+
dryRun?: boolean;
|
|
2209
|
+
/** Cap on how many pairs to compare. Default 10_000. */
|
|
2210
|
+
maxPairs?: number;
|
|
2211
|
+
}
|
|
2212
|
+
/** A single merge performed by `dedupe()`. */
|
|
2213
|
+
export interface DedupeMerge {
|
|
2214
|
+
/** The id of the surviving node. */
|
|
2215
|
+
kept: string;
|
|
2216
|
+
/** The ids of the nodes merged into the survivor and removed. */
|
|
2217
|
+
removed: string[];
|
|
2218
|
+
/** The reason this merge was selected. */
|
|
2219
|
+
reason: string;
|
|
2220
|
+
/** Mean cosine similarity across the cluster that was merged. */
|
|
2221
|
+
similarity: number;
|
|
2222
|
+
}
|
|
2223
|
+
/** Result returned by {@link MemOS.dedupe}. */
|
|
2224
|
+
export interface DedupeResult {
|
|
2225
|
+
merges: DedupeMerge[];
|
|
2226
|
+
/** Number of distinct clusters that were detected. */
|
|
2227
|
+
clustersFound: number;
|
|
2228
|
+
dryRun: boolean;
|
|
2229
|
+
durationMs: number;
|
|
2230
|
+
}
|
|
2231
|
+
/** Options for {@link MemOS.archive}. */
|
|
2232
|
+
export interface ArchiveOptions {
|
|
2233
|
+
/** Move memories older than this many days. Default 90. */
|
|
2234
|
+
afterDays?: number;
|
|
2235
|
+
/** Cap on importance for archival. Default 0.3. */
|
|
2236
|
+
importanceBelow?: number;
|
|
2237
|
+
/** Only consider memories in this namespace. Default "default". */
|
|
2238
|
+
namespace?: string;
|
|
2239
|
+
/** If true, do not actually move anything. Default false. */
|
|
2240
|
+
dryRun?: boolean;
|
|
2241
|
+
}
|
|
2242
|
+
/** A single archive operation. */
|
|
2243
|
+
export interface ArchiveMove {
|
|
2244
|
+
/** The id of the moved memory. */
|
|
2245
|
+
id: string;
|
|
2246
|
+
/** Reason it was archived (e.g. "stale_and_low_importance"). */
|
|
2247
|
+
reason: string;
|
|
2248
|
+
}
|
|
2249
|
+
/** Result returned by {@link MemOS.archive}. */
|
|
2250
|
+
export interface ArchiveResult {
|
|
2251
|
+
moves: ArchiveMove[];
|
|
2252
|
+
dryRun: boolean;
|
|
2253
|
+
durationMs: number;
|
|
2254
|
+
}
|
|
2255
|
+
/** Options for {@link MemOS.summarizeCluster}. */
|
|
2256
|
+
export interface SummarizeClusterOptions {
|
|
2257
|
+
/** Namespace to cluster. Default "default". */
|
|
2258
|
+
namespace?: string;
|
|
2259
|
+
/** Minimum cluster size to summarize. Default 3. */
|
|
2260
|
+
minClusterSize?: number;
|
|
2261
|
+
/** Cosine-similarity threshold for cluster membership. Default 0.78. */
|
|
2262
|
+
threshold?: number;
|
|
2263
|
+
/** If true, do not actually create summary nodes. Default false. */
|
|
2264
|
+
dryRun?: boolean;
|
|
2265
|
+
}
|
|
2266
|
+
/** A cluster that was summarized. */
|
|
2267
|
+
export interface ClusterSummary {
|
|
2268
|
+
/** The id of the newly-created summary node. Null when `dryRun: true`. */
|
|
2269
|
+
summaryId: string | null;
|
|
2270
|
+
/** The ids of the source nodes that the summary was derived from. */
|
|
2271
|
+
derivedFrom: string[];
|
|
2272
|
+
/** The generated summary text. */
|
|
2273
|
+
summary: string;
|
|
2274
|
+
}
|
|
2275
|
+
/** Result returned by {@link MemOS.summarizeCluster}. */
|
|
2276
|
+
export interface SummarizeClusterResult {
|
|
2277
|
+
clusters: ClusterSummary[];
|
|
2278
|
+
dryRun: boolean;
|
|
2279
|
+
durationMs: number;
|
|
2280
|
+
}
|
|
2281
|
+
/** Options for {@link MemOS.consolidate}. */
|
|
2282
|
+
export interface ConsolidateOptions {
|
|
2283
|
+
/** Cosine threshold for dedupe. Default 0.92. */
|
|
2284
|
+
dedupeThreshold?: number;
|
|
2285
|
+
/** Move memories older than this many days. Default 90. */
|
|
2286
|
+
archiveAfterDays?: number;
|
|
2287
|
+
/** Cap on importance for archival. Default 0.3. */
|
|
2288
|
+
archiveImportanceBelow?: number;
|
|
2289
|
+
/** Only consider memories in this namespace. Default "default". */
|
|
2290
|
+
namespace?: string;
|
|
2291
|
+
/** If true, do not actually mutate anything. Default false. */
|
|
2292
|
+
dryRun?: boolean;
|
|
2293
|
+
/** Generate cluster summaries in addition to dedupe + archive. Default true. */
|
|
2294
|
+
summarize?: boolean;
|
|
2295
|
+
/** Minimum cluster size for summarization. Default 3. */
|
|
2296
|
+
minClusterSize?: number;
|
|
2297
|
+
/** Cosine threshold for cluster membership. Default 0.78. */
|
|
2298
|
+
clusterThreshold?: number;
|
|
2299
|
+
/**
|
|
2300
|
+
* Run decay-based forgetting (algorithmic forgetting with an exponential
|
|
2301
|
+
* recency half-life). Superseded memories are marked historical
|
|
2302
|
+
* (`validTo = now`) — never deleted — so they stay queryable via
|
|
2303
|
+
* `searchTemporal`. Default true.
|
|
2304
|
+
*/
|
|
2305
|
+
decay?: boolean;
|
|
2306
|
+
/** Exponential half-life (days) for the recency term of the retention score. Default 60. */
|
|
2307
|
+
decayHalfLifeDays?: number;
|
|
2308
|
+
/** Retention scores below this supersede the memory. Default 0.2. */
|
|
2309
|
+
minRetentionScore?: number;
|
|
2310
|
+
/** Only memories created more than this many days ago are eligible. Default 60. */
|
|
2311
|
+
olderThanDays?: number;
|
|
2312
|
+
}
|
|
2313
|
+
/**
|
|
2314
|
+
* A single memory superseded by the decay-forgetting stage of
|
|
2315
|
+
* `consolidate()`.
|
|
2316
|
+
*/
|
|
2317
|
+
export interface DecaySuperseded {
|
|
2318
|
+
/** The superseded memory's id. */
|
|
2319
|
+
id: string;
|
|
2320
|
+
/** The retention score that fell below `minRetentionScore`. */
|
|
2321
|
+
score: number;
|
|
2322
|
+
}
|
|
2323
|
+
/** Options for {@link MemOS.forgetByDecay}. */
|
|
2324
|
+
export interface DecayForgettingOptions {
|
|
2325
|
+
/** Only consider memories in this namespace. Default "default". */
|
|
2326
|
+
namespace?: string;
|
|
2327
|
+
/** Exponential half-life (days) for the recency term. Default 60. */
|
|
2328
|
+
halfLifeDays?: number;
|
|
2329
|
+
/** Retention scores below this supersede the memory. Default 0.2. */
|
|
2330
|
+
minScore?: number;
|
|
2331
|
+
/** Only memories untouched for this many days are eligible. Default 60. */
|
|
2332
|
+
olderThanDays?: number;
|
|
2333
|
+
/** If true, do not actually supersede anything. Default false. */
|
|
2334
|
+
dryRun?: boolean;
|
|
2335
|
+
}
|
|
2336
|
+
/** Result returned by {@link MemOS.forgetByDecay}. */
|
|
2337
|
+
export interface DecayForgetResult {
|
|
2338
|
+
superseded: DecaySuperseded[];
|
|
2339
|
+
dryRun: boolean;
|
|
2340
|
+
durationMs: number;
|
|
2341
|
+
}
|
|
2342
|
+
/** Options for {@link MemOS.history}. */
|
|
2343
|
+
export interface HistoryOptions {
|
|
2344
|
+
/** Minimum similarity for related versions. Default 0.8. */
|
|
2345
|
+
threshold?: number;
|
|
2346
|
+
/** Cap on related versions per direction. Default 10. */
|
|
2347
|
+
limit?: number;
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* The full version timeline for one memory: the memory itself, the older
|
|
2351
|
+
* versions it replaced, the newer versions that replaced it, and any
|
|
2352
|
+
* consolidated notes derived from it. Supersession is inferred from
|
|
2353
|
+
* content similarity plus temporal-validity windows — superseded versions
|
|
2354
|
+
* are historical (`validTo` set), the live version is not.
|
|
2355
|
+
*/
|
|
2356
|
+
export interface MemoryHistory {
|
|
2357
|
+
node: MemoryNode;
|
|
2358
|
+
/** Older versions this memory replaced (oldest first). */
|
|
2359
|
+
supersedes: ScoredMemory[];
|
|
2360
|
+
/** Newer versions that replaced this memory (newest first). */
|
|
2361
|
+
supersededBy: ScoredMemory[];
|
|
2362
|
+
/** Notes (note pool) derived from this memory via `derived_from` edges. */
|
|
2363
|
+
derivedNotes: MemoryNode[];
|
|
2364
|
+
/** All graph edges touching this memory. */
|
|
2365
|
+
edges: MemoryEdge[];
|
|
2366
|
+
}
|
|
2367
|
+
/** Result returned by {@link MemOS.consolidate}. */
|
|
2368
|
+
export interface ConsolidateResult {
|
|
2369
|
+
merges: DedupeMerge[];
|
|
2370
|
+
moves: ArchiveMove[];
|
|
2371
|
+
clusters: ClusterSummary[];
|
|
2372
|
+
/** Memories superseded by decay-based forgetting. */
|
|
2373
|
+
decayed: DecaySuperseded[];
|
|
2374
|
+
dryRun: boolean;
|
|
2375
|
+
durationMs: number;
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* A single message in a conversation, used by `extractFacts`.
|
|
2379
|
+
*/
|
|
2380
|
+
export interface ConversationMessage {
|
|
2381
|
+
/** Speaker role: "user", "assistant", or "system". */
|
|
2382
|
+
role: "user" | "assistant" | "system";
|
|
2383
|
+
/** Message text content. */
|
|
2384
|
+
content: string;
|
|
2385
|
+
/** Optional speaker name for multi-user conversations. */
|
|
2386
|
+
speaker?: string;
|
|
2387
|
+
}
|
|
2388
|
+
/**
|
|
2389
|
+
* A candidate fact extracted from a conversation.
|
|
2390
|
+
*/
|
|
2391
|
+
export interface ExtractedFact {
|
|
2392
|
+
/** The extracted fact text. */
|
|
2393
|
+
content: string;
|
|
2394
|
+
/** Inferred memory type. */
|
|
2395
|
+
type: MemoryType;
|
|
2396
|
+
/** Confidence in [0, 1]. */
|
|
2397
|
+
confidence: number;
|
|
2398
|
+
/** Optional tags inferred from context. */
|
|
2399
|
+
tags: string[];
|
|
2400
|
+
/** Source of the fact — usually `user_input` or `agent_inferred`. */
|
|
2401
|
+
source: MemorySource;
|
|
2402
|
+
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Options for {@link MemOS.extractFacts}.
|
|
2405
|
+
*/
|
|
2406
|
+
export interface ExtractFactsOptions {
|
|
2407
|
+
/**
|
|
2408
|
+
* If true, automatically store all extracted facts with confidence >=
|
|
2409
|
+
* `minConfidence`. Default false — returns candidates without storing.
|
|
2410
|
+
*/
|
|
2411
|
+
autoStore?: boolean;
|
|
2412
|
+
/** Only auto-store facts with confidence >= this threshold. Default 0.6. */
|
|
2413
|
+
minConfidence?: number;
|
|
2414
|
+
/** Namespace to store facts in (when autoStore is true). */
|
|
2415
|
+
namespace?: string;
|
|
2416
|
+
/**
|
|
2417
|
+
* When true, skip facts that are near-duplicates of existing memories.
|
|
2418
|
+
* Default true.
|
|
2419
|
+
*/
|
|
2420
|
+
dedupe?: boolean;
|
|
2421
|
+
/** Cosine similarity threshold for dedup. Default 0.85. */
|
|
2422
|
+
dedupeThreshold?: number;
|
|
2423
|
+
}
|
|
2424
|
+
/** Result returned by {@link MemOS.extractFacts}. */
|
|
2425
|
+
export interface ExtractFactsResult {
|
|
2426
|
+
/** Extracted fact candidates. */
|
|
2427
|
+
facts: ExtractedFact[];
|
|
2428
|
+
/** IDs of stored nodes (when `autoStore` is true). */
|
|
2429
|
+
storedIds: string[];
|
|
2430
|
+
/** Number of facts skipped as near-duplicates. */
|
|
2431
|
+
duplicates: number;
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Detailed diagnostics snapshot returned by `memos.diagnostics()`.
|
|
2435
|
+
*/
|
|
2436
|
+
export interface DiagnosticsResult {
|
|
2437
|
+
/** Total number of stored memories. */
|
|
2438
|
+
totalNodes: number;
|
|
2439
|
+
/** Total number of edges. */
|
|
2440
|
+
totalEdges: number;
|
|
2441
|
+
/** Number of memories with embeddings. */
|
|
2442
|
+
nodesWithEmbeddings: number;
|
|
2443
|
+
/** Number of memories with temporal validity set. */
|
|
2444
|
+
nodesWithValidity: number;
|
|
2445
|
+
/** Number of historical (superseded) memories. */
|
|
2446
|
+
historicalNodes: number;
|
|
2447
|
+
/** Number of memories with a TTL. */
|
|
2448
|
+
nodesWithTTL: number;
|
|
2449
|
+
/** Number of expired memories pending sweep. */
|
|
2450
|
+
expiredNodes: number;
|
|
2451
|
+
/** Average importance across all nodes. */
|
|
2452
|
+
avgImportance: number;
|
|
2453
|
+
/** Average trust score across all nodes. */
|
|
2454
|
+
avgTrustScore: number;
|
|
2455
|
+
/** Breakdown of nodes by provenance source. */
|
|
2456
|
+
bySource: Record<string, number>;
|
|
2457
|
+
/** Breakdown of nodes by memory type. */
|
|
2458
|
+
byType: Record<string, number>;
|
|
2459
|
+
/** Breakdown of nodes by namespace. */
|
|
2460
|
+
byNamespace: Record<string, number>;
|
|
2461
|
+
/** Database file size in bytes. */
|
|
2462
|
+
dbSizeBytes: number;
|
|
2463
|
+
/** Embedding queue status, if configured. */
|
|
2464
|
+
embeddingQueue?: EmbeddingQueueStatus;
|
|
2465
|
+
/** Whether the storage adapter supports optional methods. */
|
|
2466
|
+
storageCapabilities: {
|
|
2467
|
+
peekNode: boolean;
|
|
2468
|
+
evictLeastImportant: boolean;
|
|
2469
|
+
saveEmbedding: boolean;
|
|
2470
|
+
getAllEmbeddings: boolean;
|
|
2471
|
+
setNodeNamespace: boolean;
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
//# sourceMappingURL=types.d.ts.map
|