kernl 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/.turbo/turbo-build.log +4 -5
  2. package/.turbo/turbo-check-types.log +1 -1
  3. package/CHANGELOG.md +36 -0
  4. package/dist/agent/__tests__/concurrency.test.js +1 -1
  5. package/dist/agent/__tests__/run.test.js +1 -1
  6. package/dist/{types/agent.d.ts → agent/types.d.ts} +2 -2
  7. package/dist/agent/types.d.ts.map +1 -0
  8. package/dist/agent.d.ts +36 -4
  9. package/dist/agent.d.ts.map +1 -1
  10. package/dist/agent.js +58 -0
  11. package/dist/api/models/thread.d.ts +1 -1
  12. package/dist/api/resources/threads/threads.d.ts +1 -1
  13. package/dist/api/resources/threads/threads.d.ts.map +1 -1
  14. package/dist/api/resources/threads/threads.js +1 -1
  15. package/dist/api/resources/threads/types.d.ts +2 -2
  16. package/dist/api/resources/threads/types.d.ts.map +1 -1
  17. package/dist/context.d.ts +4 -4
  18. package/dist/context.d.ts.map +1 -1
  19. package/dist/guardrail.d.ts +2 -2
  20. package/dist/index.d.ts +5 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +4 -3
  23. package/dist/internal.d.ts +2 -2
  24. package/dist/internal.js +1 -1
  25. package/dist/kernl/index.d.ts +1 -1
  26. package/dist/kernl/index.d.ts.map +1 -1
  27. package/dist/kernl/index.js +0 -1
  28. package/dist/kernl/kernl.d.ts +7 -18
  29. package/dist/kernl/kernl.d.ts.map +1 -1
  30. package/dist/kernl/kernl.js +29 -29
  31. package/dist/kernl/types.d.ts +91 -0
  32. package/dist/kernl/types.d.ts.map +1 -0
  33. package/dist/lib/error.d.ts +2 -2
  34. package/dist/lifecycle.d.ts +2 -2
  35. package/dist/memory/codec.d.ts +32 -0
  36. package/dist/memory/codec.d.ts.map +1 -0
  37. package/dist/memory/codec.js +97 -0
  38. package/dist/memory/codecs/domain.d.ts +34 -0
  39. package/dist/memory/codecs/domain.d.ts.map +1 -0
  40. package/dist/memory/codecs/domain.js +99 -0
  41. package/dist/memory/codecs/identity.d.ts +12 -0
  42. package/dist/memory/codecs/identity.d.ts.map +1 -0
  43. package/dist/memory/codecs/identity.js +17 -0
  44. package/dist/memory/codecs/index.d.ts +31 -0
  45. package/dist/memory/codecs/index.d.ts.map +1 -0
  46. package/dist/memory/codecs/index.js +39 -0
  47. package/dist/memory/codecs/tpuf.d.ts +38 -0
  48. package/dist/memory/codecs/tpuf.d.ts.map +1 -0
  49. package/dist/memory/codecs/tpuf.js +90 -0
  50. package/dist/memory/encoder.d.ts +29 -0
  51. package/dist/memory/encoder.d.ts.map +1 -0
  52. package/dist/memory/encoder.js +45 -0
  53. package/dist/memory/handle.d.ts +89 -0
  54. package/dist/memory/handle.d.ts.map +1 -0
  55. package/dist/memory/handle.js +128 -0
  56. package/dist/memory/index.d.ts +12 -0
  57. package/dist/memory/index.d.ts.map +1 -0
  58. package/dist/memory/index.js +7 -0
  59. package/dist/memory/indexes.d.ts +91 -0
  60. package/dist/memory/indexes.d.ts.map +1 -0
  61. package/dist/memory/indexes.js +7 -0
  62. package/dist/memory/memory.d.ts +51 -0
  63. package/dist/memory/memory.d.ts.map +1 -0
  64. package/dist/memory/memory.js +107 -0
  65. package/dist/memory/schema.d.ts +41 -0
  66. package/dist/memory/schema.d.ts.map +1 -0
  67. package/dist/memory/schema.js +112 -0
  68. package/dist/memory/store.d.ts +36 -0
  69. package/dist/memory/store.d.ts.map +1 -0
  70. package/dist/memory/store.js +4 -0
  71. package/dist/memory/types.d.ts +250 -0
  72. package/dist/memory/types.d.ts.map +1 -0
  73. package/dist/memory/types.js +4 -0
  74. package/dist/storage/base.d.ts +6 -1
  75. package/dist/storage/base.d.ts.map +1 -1
  76. package/dist/storage/in-memory.d.ts +24 -2
  77. package/dist/storage/in-memory.d.ts.map +1 -1
  78. package/dist/storage/in-memory.js +131 -0
  79. package/dist/storage/thread.d.ts +1 -1
  80. package/dist/thread/__tests__/integration.test.js +1 -1
  81. package/dist/thread/__tests__/mock.d.ts +1 -1
  82. package/dist/thread/__tests__/namespace.test.js +1 -1
  83. package/dist/thread/__tests__/thread.test.js +1 -1
  84. package/dist/thread/thread.d.ts +2 -2
  85. package/dist/thread/thread.d.ts.map +1 -1
  86. package/dist/{types/thread.d.ts → thread/types.d.ts} +2 -2
  87. package/dist/thread/types.d.ts.map +1 -0
  88. package/dist/thread/utils.d.ts +2 -2
  89. package/dist/thread/utils.d.ts.map +1 -1
  90. package/package.json +4 -2
  91. package/src/{types/agent.ts → agent/types.ts} +1 -1
  92. package/src/agent.ts +78 -2
  93. package/src/api/__tests__/threads.test.ts +2 -2
  94. package/src/api/models/thread.ts +1 -1
  95. package/src/api/resources/threads/events.ts +1 -1
  96. package/src/api/resources/threads/threads.ts +2 -2
  97. package/src/api/resources/threads/types.ts +2 -2
  98. package/src/context.ts +6 -136
  99. package/src/guardrail.ts +2 -2
  100. package/src/index.ts +35 -6
  101. package/src/internal.ts +2 -2
  102. package/src/kernl/index.ts +8 -0
  103. package/src/{kernl.ts → kernl/kernl.ts} +40 -28
  104. package/src/kernl/types.ts +106 -0
  105. package/src/lib/error.ts +2 -2
  106. package/src/lifecycle.ts +2 -2
  107. package/src/memory/codecs/domain.ts +115 -0
  108. package/src/memory/codecs/identity.ts +28 -0
  109. package/src/memory/codecs/index.ts +61 -0
  110. package/src/memory/codecs/tpuf.ts +115 -0
  111. package/src/memory/encoder.ts +56 -0
  112. package/src/memory/handle.ts +189 -0
  113. package/src/memory/index.ts +49 -0
  114. package/src/memory/indexes.ts +108 -0
  115. package/src/memory/memory.ts +143 -0
  116. package/src/memory/schema.ts +142 -0
  117. package/src/memory/store.ts +47 -0
  118. package/src/memory/types.ts +282 -0
  119. package/src/storage/__tests__/in-memory.test.ts +1 -1
  120. package/src/storage/base.ts +7 -1
  121. package/src/storage/in-memory.ts +170 -2
  122. package/src/storage/thread.ts +1 -1
  123. package/src/thread/__tests__/integration.test.ts +1 -1
  124. package/src/thread/__tests__/mock.ts +1 -1
  125. package/src/thread/__tests__/thread.test.ts +1 -1
  126. package/src/thread/thread.ts +2 -2
  127. package/src/{types/thread.ts → thread/types.ts} +1 -1
  128. package/src/thread/utils.ts +2 -2
  129. package/tsconfig.tsbuildinfo +1 -0
  130. package/dist/api/__tests__/cursor-page.test.d.ts +0 -2
  131. package/dist/api/__tests__/cursor-page.test.d.ts.map +0 -1
  132. package/dist/api/__tests__/cursor-page.test.js +0 -414
  133. package/dist/api/__tests__/offset-page.test.d.ts +0 -2
  134. package/dist/api/__tests__/offset-page.test.d.ts.map +0 -1
  135. package/dist/api/__tests__/offset-page.test.js +0 -510
  136. package/dist/api/pagination/base.d.ts +0 -48
  137. package/dist/api/pagination/base.d.ts.map +0 -1
  138. package/dist/api/pagination/base.js +0 -45
  139. package/dist/api/pagination/cursor.d.ts +0 -44
  140. package/dist/api/pagination/cursor.d.ts.map +0 -1
  141. package/dist/api/pagination/cursor.js +0 -52
  142. package/dist/api/pagination/offset.d.ts +0 -42
  143. package/dist/api/pagination/offset.d.ts.map +0 -1
  144. package/dist/api/pagination/offset.js +0 -55
  145. package/dist/kernl/threads.d.ts +0 -110
  146. package/dist/kernl/threads.d.ts.map +0 -1
  147. package/dist/kernl/threads.js +0 -126
  148. package/dist/kernl.d.ts +0 -60
  149. package/dist/kernl.d.ts.map +0 -1
  150. package/dist/kernl.js +0 -113
  151. package/dist/types/agent.d.ts.map +0 -1
  152. package/dist/types/kernl.d.ts +0 -42
  153. package/dist/types/kernl.d.ts.map +0 -1
  154. package/dist/types/thread.d.ts.map +0 -1
  155. package/src/api/__tests__/cursor-page.test.ts +0 -512
  156. package/src/api/__tests__/offset-page.test.ts +0 -624
  157. package/src/api/pagination/base.ts +0 -79
  158. package/src/api/pagination/cursor.ts +0 -86
  159. package/src/api/pagination/offset.ts +0 -89
  160. package/src/types/kernl.ts +0 -51
  161. /package/dist/{types/agent.js → agent/types.js} +0 -0
  162. /package/dist/{types/kernl.js → kernl/types.js} +0 -0
  163. /package/dist/{types/thread.js → thread/types.js} +0 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Memory index handle with lazy initialization.
3
+ */
4
+
5
+ import {
6
+ planQuery,
7
+ type SearchIndex,
8
+ type IndexHandle,
9
+ type QueryInput,
10
+ type SearchHit,
11
+ type DocumentPatch,
12
+ type UpsertResult,
13
+ type PatchResult,
14
+ type DeleteResult,
15
+ type FieldSchema,
16
+ type SearchCapabilities,
17
+ type UnknownDocument,
18
+ } from "@kernl-sdk/retrieval";
19
+
20
+ import { getAdapterCodecs, type AdapterCodecs } from "./codecs";
21
+ import type { IndexMemoryRecord } from "./types";
22
+
23
+ /**
24
+ * Configuration for MemoryIndexHandle.
25
+ */
26
+ export interface MemoryIndexHandleConfig {
27
+ /**
28
+ * The underlying search index backend.
29
+ */
30
+ index: SearchIndex;
31
+
32
+ /**
33
+ * Index identifier (e.g., "kernl_memories_index").
34
+ */
35
+ indexId: string;
36
+
37
+ /**
38
+ * Field schema for the memory index.
39
+ */
40
+ schema: Record<string, FieldSchema>;
41
+
42
+ /**
43
+ * Backend-specific options passed to SearchIndex.createIndex().
44
+ */
45
+ providerOptions?: Record<string, unknown>;
46
+ }
47
+
48
+ /**
49
+ * Domain-aware index handle for memory records with lazy initialization.
50
+ *
51
+ * - Wraps a SearchIndex and ensures the memory index is created before any operation.
52
+ * - Normalizes the idiosyncrasies of search adapters (capabilities, weird rules, ... - e.g. tpuf requires vector fields named
53
+ * literally 'vector' - dumb shit like this..)
54
+ */
55
+ export class MemoryIndexHandle implements IndexHandle<IndexMemoryRecord> {
56
+ readonly id: string;
57
+
58
+ private readonly index: SearchIndex;
59
+ private readonly schema: Record<string, FieldSchema>;
60
+ private readonly caps: SearchCapabilities;
61
+ private readonly codecs: AdapterCodecs;
62
+ private readonly providerOptions?: Record<string, unknown>;
63
+
64
+ private initPromise: Promise<void> | null = null;
65
+
66
+ constructor(config: MemoryIndexHandleConfig) {
67
+ this.index = config.index;
68
+ this.id = config.indexId;
69
+ this.schema = config.schema;
70
+ this.caps = this.index.capabilities();
71
+ this.codecs = getAdapterCodecs(this.index.id);
72
+ this.providerOptions = config.providerOptions;
73
+ }
74
+
75
+ /**
76
+ * Ensure memory index exists (lazy initialization).
77
+ *
78
+ * Safe to call multiple times - initialization only runs once.
79
+ */
80
+ private async ensureInit(): Promise<void> {
81
+ if (!this.initPromise) {
82
+ this.initPromise = this.createIndex().catch((err) => {
83
+ this.initPromise = null;
84
+ throw err;
85
+ });
86
+ }
87
+ await this.initPromise;
88
+ }
89
+
90
+ /**
91
+ * Create the memory index if it doesn't exist.
92
+ */
93
+ private async createIndex(): Promise<void> {
94
+ try {
95
+ await this.index.createIndex({
96
+ id: this.id,
97
+ schema: this.codecs.schema.encode(this.schema),
98
+ providerOptions: this.providerOptions,
99
+ });
100
+ } catch (err: any) {
101
+ // (TODO): we should probably enforce a stricter contract w/ tighter error types
102
+ //
103
+ // Ignore "already exists" errors
104
+ if (
105
+ err.message?.includes("already exists") ||
106
+ err.message?.includes("AlreadyExists")
107
+ ) {
108
+ return;
109
+ }
110
+ throw err;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Search for memory records matching the query.
116
+ *
117
+ * Adapts the query to backend capabilities, degrading gracefully
118
+ * when hybrid or multi-signal queries aren't supported.
119
+ */
120
+ async query(input: QueryInput): Promise<SearchHit<IndexMemoryRecord>[]> {
121
+ const { input: planned } = planQuery(input, this.caps);
122
+ const q = this.codecs.query.encode(planned);
123
+ const handle = await this.handle();
124
+ const hits = await handle.query(q);
125
+
126
+ // decode hits back to IndexMemoryRecord format
127
+ return hits.map((hit) => ({
128
+ ...hit,
129
+ document: hit.document
130
+ ? this.codecs.doc.decode(hit.document as UnknownDocument)
131
+ : undefined,
132
+ }));
133
+ }
134
+
135
+ /**
136
+ * Insert or update memory records in the index.
137
+ */
138
+ async upsert(
139
+ docs: IndexMemoryRecord | IndexMemoryRecord[],
140
+ ): Promise<UpsertResult> {
141
+ const arr = Array.isArray(docs) ? docs : [docs];
142
+ const encoded = arr.map((doc) => this.codecs.doc.encode(doc));
143
+ const handle = await this.handle();
144
+ return handle.upsert(encoded);
145
+ }
146
+
147
+ /**
148
+ * Partially update memory records without re-indexing vectors.
149
+ *
150
+ * Note: Patches don't include vector fields so we cast directly.
151
+ * Metadata field type mismatch (JSONObject vs FieldValue) is handled at runtime.
152
+ */
153
+ async patch(
154
+ patches:
155
+ | DocumentPatch<IndexMemoryRecord>
156
+ | DocumentPatch<IndexMemoryRecord>[],
157
+ ): Promise<PatchResult> {
158
+ const handle = await this.handle();
159
+ return handle.patch(patches as DocumentPatch<UnknownDocument>[]);
160
+ }
161
+
162
+ /**
163
+ * Remove memory records from the index.
164
+ */
165
+ async delete(ids: string | string[]): Promise<DeleteResult> {
166
+ const handle = await this.handle();
167
+ return handle.delete(ids);
168
+ }
169
+
170
+ /**
171
+ * Add a new field to the index schema.
172
+ *
173
+ * @throws Always throws - dynamic schema modification not supported
174
+ */
175
+ async addField(_field: string, _schema: FieldSchema): Promise<void> {
176
+ throw new Error("addField not supported for MemoryIndexHandle");
177
+ }
178
+
179
+ /**
180
+ * Get an initialized underlying index handle.
181
+ *
182
+ * Returns a handle typed for UnknownDocument since we encode/decode
183
+ * through the adapter codecs for backend-specific field mapping.
184
+ */
185
+ private async handle(): Promise<IndexHandle<UnknownDocument>> {
186
+ await this.ensureInit();
187
+ return this.index.index<UnknownDocument>(this.id);
188
+ }
189
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Memory module.
3
+ */
4
+
5
+ export { Memory } from "./memory";
6
+ export { MemoryByteEncoder } from "./encoder";
7
+ export { buildMemoryIndexSchema } from "./schema";
8
+ export { MemoryIndexHandle } from "./handle";
9
+ export type { MemoryIndexHandleConfig } from "./handle";
10
+
11
+ export type {
12
+ // Byte types
13
+ TextByte,
14
+ ImageByte,
15
+ AudioByte,
16
+ VideoByte,
17
+ MemoryByte,
18
+ IndexableByte,
19
+ MemoryByteCodec,
20
+ // Core types
21
+ MemoryScope,
22
+ MemoryKind,
23
+ NewMemory,
24
+ AgentMemoryCreate,
25
+ MemoryConfig,
26
+ MemoryReindexParams,
27
+ MemoryRecord,
28
+ MemoryRecordUpdate,
29
+ MemoryFilter,
30
+ MemoryListOptions,
31
+ MemorySearchQuery,
32
+ IndexMemoryRecord,
33
+ IndexMemoryRecordPatch,
34
+ WorkingMemorySnapshot,
35
+ ShortTermMemorySnapshot,
36
+ } from "./types";
37
+
38
+ export type { MemoryStore } from "./store";
39
+
40
+ export type {
41
+ MemoryIndexBase,
42
+ MemorySearchIndex,
43
+ MemoryGraphIndex,
44
+ MemoryArchiveIndex,
45
+ GraphTraversalQuery,
46
+ GraphTraversalResult,
47
+ ArchiveQuery,
48
+ ArchiveResult,
49
+ } from "./indexes";
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Memory index interfaces.
3
+ *
4
+ * Indexes are projections of the primary store (DB) that enable
5
+ * specialized query patterns (vector search, graph traversal, archival).
6
+ */
7
+
8
+ import type { SearchHit } from "@kernl-sdk/retrieval";
9
+
10
+ import type {
11
+ MemoryRecord,
12
+ MemoryRecordUpdate,
13
+ MemorySearchQuery,
14
+ IndexMemoryRecord,
15
+ } from "./types";
16
+
17
+ /**
18
+ * Base interface for memory indexes.
19
+ *
20
+ * All indexes share common lifecycle operations (index, patch, delete)
21
+ * but differ in their query interface.
22
+ */
23
+ export interface MemoryIndexBase<TQuery, TResult> {
24
+ readonly id: string /* provider id - "pgvector" | "turbopuffer", ... */;
25
+
26
+ /**
27
+ * Query the index.
28
+ */
29
+ query(query: TQuery): Promise<TResult>;
30
+
31
+ /**
32
+ * Index one or more memory records (idempotent upsert).
33
+ */
34
+ index(memories: MemoryRecord | MemoryRecord[]): Promise<void>;
35
+
36
+ /**
37
+ * Partially update one or more records' projections.
38
+ */
39
+ update(updates: MemoryRecordUpdate | MemoryRecordUpdate[]): Promise<void>;
40
+
41
+ /**
42
+ * Remove one or more records from this index (DB row remains).
43
+ */
44
+ delete(ids: string | string[]): Promise<void>;
45
+
46
+ /**
47
+ * Index warming (optional).
48
+ */
49
+ warm(index: string): Promise<void>;
50
+ }
51
+
52
+ /**
53
+ * Memory search index - vector/semantic search over memories.
54
+ */
55
+ export interface MemorySearchIndex
56
+ extends MemoryIndexBase<MemorySearchQuery, SearchHit<IndexMemoryRecord>[]> {}
57
+
58
+ /**
59
+ * Graph traversal query (stub).
60
+ */
61
+ export interface GraphTraversalQuery {
62
+ // TODO: define graph query params
63
+ depth?: number;
64
+ }
65
+
66
+ /**
67
+ * Graph traversal result (stub).
68
+ */
69
+ export interface GraphTraversalResult {
70
+ // TODO: define graph result shape
71
+ nodes: Array<{ id: string; record?: MemoryRecord }>;
72
+ edges: Array<{ from: string; to: string; relation: string }>;
73
+ }
74
+
75
+ /**
76
+ * Memory graph index - relationship/graph traversal over memories (stub).
77
+ */
78
+ export interface MemoryGraphIndex
79
+ extends MemoryIndexBase<GraphTraversalQuery, GraphTraversalResult> {
80
+ /**
81
+ * Explicit traversal API (alias for query).
82
+ */
83
+ traverse(query: GraphTraversalQuery): Promise<GraphTraversalResult>;
84
+ }
85
+
86
+ /**
87
+ * Archive query (stub).
88
+ */
89
+ export interface ArchiveQuery {
90
+ // TODO: define archive query params
91
+ before?: number;
92
+ collections?: string[];
93
+ }
94
+
95
+ /**
96
+ * Archive result (stub).
97
+ */
98
+ export interface ArchiveResult {
99
+ // TODO: define archive result shape
100
+ id: string;
101
+ uri: string;
102
+ }
103
+
104
+ /**
105
+ * Memory archive index - cold storage/archival backend (stub).
106
+ */
107
+ export interface MemoryArchiveIndex
108
+ extends MemoryIndexBase<ArchiveQuery, ArchiveResult[]> {}
@@ -0,0 +1,143 @@
1
+ import type { IndexHandle, SearchHit } from "@kernl-sdk/retrieval";
2
+ import type { AsyncCodec } from "@kernl-sdk/shared/lib";
3
+
4
+ import type { MemoryStore } from "./store";
5
+ import type {
6
+ NewMemory,
7
+ MemoryRecord,
8
+ MemoryRecordUpdate,
9
+ MemoryScope,
10
+ MemoryConfig,
11
+ MemorySearchQuery,
12
+ MemoryByteCodec,
13
+ IndexMemoryRecord,
14
+ WorkingMemorySnapshot,
15
+ ShortTermMemorySnapshot,
16
+ MemoryReindexParams,
17
+ } from "./types";
18
+ import { MEMORY_FILTER, PATCH_CODEC, recordCodec } from "./codecs";
19
+
20
+ /**
21
+ * Memory is the primary memory abstraction for agents.
22
+ *
23
+ * Sits above storage/index layers + owns cognitive policy, eviction/TTL, consolidation.
24
+ *
25
+ * - L1 / wmem: active working set exposed to the model
26
+ * - L2 / smem: bounded recent context with a TTL
27
+ * - L3 / lmem: durable, structured long-term store
28
+ *
29
+ * Delegates persistence to storage adapters and optional indexes as
30
+ * _projections_ of the primary memory store.
31
+ */
32
+ export class Memory {
33
+ private readonly store: MemoryStore;
34
+ private readonly _search: IndexHandle<IndexMemoryRecord> | null;
35
+
36
+ private readonly encoder: MemoryByteCodec;
37
+ private readonly rcodec: AsyncCodec<MemoryRecord, IndexMemoryRecord>;
38
+
39
+ constructor(config: MemoryConfig) {
40
+ this.store = config.store;
41
+ this._search = config.search ?? null;
42
+
43
+ // TODO: default encoder using text-embedding-3-small
44
+ this.encoder = config.encoder;
45
+ this.rcodec = recordCodec(config.encoder);
46
+ }
47
+
48
+ /**
49
+ * Create a new memory record.
50
+ * Writes to primary store first, then indexes if configured.
51
+ */
52
+ async create(memory: NewMemory): Promise<MemoryRecord> {
53
+ const record = await this.store.create(memory);
54
+
55
+ // index into search if avail
56
+ if (this._search) {
57
+ const indexed = await this.rcodec.encode(record);
58
+ await this._search.upsert(indexed);
59
+ }
60
+
61
+ return record;
62
+ }
63
+
64
+ /**
65
+ * Update an existing memory record.
66
+ * Updates primary store, then re-indexes or patches search index.
67
+ */
68
+ async update(update: MemoryRecordUpdate): Promise<MemoryRecord> {
69
+ const record = await this.store.update(update.id, update);
70
+ if (!this._search) return record;
71
+
72
+ if (update.content) {
73
+ const indexed = await this.rcodec.encode(record); // content changed → full re-index with new embeddings
74
+ await this._search.upsert(indexed);
75
+ } else {
76
+ const patch = PATCH_CODEC.encode(update); // metadata only → cheap patch
77
+ await this._search.patch(patch);
78
+ }
79
+
80
+ return record;
81
+ }
82
+
83
+ /**
84
+ * Semantic/metadata search across memories.
85
+ *
86
+ * Sends rich query with both text and vector - the index handle
87
+ * adapts based on backend capabilities (e.g. drops text for pgvector).
88
+ */
89
+ async search(q: MemorySearchQuery): Promise<SearchHit<IndexMemoryRecord>[]> {
90
+ if (!this._search) {
91
+ throw new Error("search index not configured");
92
+ }
93
+
94
+ const tvec = await this.encoder.embed(q.query);
95
+
96
+ return this._search.query({
97
+ query: [{ text: q.query, tvec }],
98
+ filter: q.filter ? MEMORY_FILTER.encode(q.filter) : undefined,
99
+ topK: q.limit ?? 20,
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Repair indexing for a memory without modifying the DB row.
105
+ */
106
+ async reindex(params: MemoryReindexParams): Promise<void> {
107
+ const record = await this.store.get(params.id);
108
+ if (!record) {
109
+ throw new Error(`memory not found: ${params.id}`);
110
+ }
111
+
112
+ const indexes = params.indexes ?? ["search", "graph", "archive"];
113
+
114
+ if (indexes.includes("search") && this._search) {
115
+ const indexed = await this.rcodec.encode(record);
116
+ await this._search.upsert(indexed);
117
+ }
118
+ }
119
+
120
+ /* --- (TODO): unclear what the shape of these should be.. --- */
121
+
122
+ /**
123
+ * Load working memory (L1) - wmem-pinned memories for the scope.
124
+ */
125
+ async loadWorkingMemory(scope: MemoryScope): Promise<WorkingMemorySnapshot> {
126
+ const records = await this.store.list({
127
+ filter: { scope, wmem: true },
128
+ });
129
+ return { scope, records };
130
+ }
131
+
132
+ /**
133
+ * Load short-term memory (L2) - active smem for the scope.
134
+ */
135
+ async loadShortTermMemory(
136
+ scope: MemoryScope,
137
+ ): Promise<ShortTermMemorySnapshot> {
138
+ const records = await this.store.list({
139
+ filter: { scope, smem: true },
140
+ });
141
+ return { scope, records };
142
+ }
143
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Memory index schema builder.
3
+ */
4
+
5
+ import type { FieldSchema } from "@kernl-sdk/retrieval";
6
+
7
+ /**
8
+ * Options for building memory index schema.
9
+ */
10
+ export interface MemoryIndexSchemaOptions {
11
+ /**
12
+ * Vector dimensions for embeddings.
13
+ * @default 1536 (OpenAI text-embedding-3-small)
14
+ */
15
+ dimensions?: number;
16
+
17
+ /**
18
+ * Similarity metric for vector search.
19
+ * @default "cosine"
20
+ */
21
+ similarity?: "cosine" | "euclidean" | "dot_product";
22
+ }
23
+
24
+ /**
25
+ * Build a canonical memory index schema for vector search.
26
+ *
27
+ * Returns a schema Record that can be used with SearchIndex.createIndex().
28
+ *
29
+ * Schema includes all fields from IndexMemoryRecord:
30
+ * - id: primary key
31
+ * - namespace, entityId, agentId: scope fields (filterable, nullable)
32
+ * - kind, collection: memory attributes (filterable)
33
+ * - timestamp, createdAt, updatedAt: timestamps (filterable + sortable)
34
+ * - text: content text (full-text search)
35
+ * - tvec, ivec, avec, vvec: vector embeddings for text/image/audio/video
36
+ * - metadata: structured metadata (object)
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const schema = buildMemoryIndexSchema({ dimensions: 1536 });
41
+ * const handle = new MemoryIndexHandle({ index: vector, indexId: "kernl_memories", schema });
42
+ * ```
43
+ */
44
+ export function buildMemoryIndexSchema(
45
+ options: MemoryIndexSchemaOptions = {},
46
+ ): Record<string, FieldSchema> {
47
+ const dimensions = options.dimensions ?? 1536;
48
+ const similarity = options.similarity ?? "cosine";
49
+
50
+ const schema: Record<string, FieldSchema> = {
51
+ id: {
52
+ type: "string",
53
+ pk: true,
54
+ sortable: true,
55
+ },
56
+
57
+ // scope fields (filterable, nullable)
58
+ namespace: {
59
+ type: "string",
60
+ filterable: true,
61
+ optional: true,
62
+ },
63
+ entityId: {
64
+ type: "string",
65
+ filterable: true,
66
+ optional: true,
67
+ },
68
+ agentId: {
69
+ type: "string",
70
+ filterable: true,
71
+ optional: true,
72
+ },
73
+
74
+ // memory attributes (filterable)
75
+ kind: {
76
+ type: "string",
77
+ filterable: true,
78
+ },
79
+ collection: {
80
+ type: "string",
81
+ filterable: true,
82
+ },
83
+
84
+ // timestamps (filterable + sortable) - store as bigint to safely handle
85
+ // millisecond Unix epoch values without overflow in SQL backends.
86
+ timestamp: {
87
+ type: "bigint",
88
+ filterable: true,
89
+ sortable: true,
90
+ },
91
+ createdAt: {
92
+ type: "bigint",
93
+ filterable: true,
94
+ sortable: true,
95
+ },
96
+ updatedAt: {
97
+ type: "bigint",
98
+ filterable: true,
99
+ sortable: true,
100
+ },
101
+
102
+ // content fields
103
+ text: {
104
+ type: "string",
105
+ fts: true,
106
+ optional: true,
107
+ },
108
+
109
+ // vector fields for different modalities
110
+ tvec: {
111
+ type: "vector",
112
+ dimensions,
113
+ similarity,
114
+ optional: true,
115
+ },
116
+ ivec: {
117
+ type: "vector",
118
+ dimensions,
119
+ similarity,
120
+ optional: true,
121
+ },
122
+ avec: {
123
+ type: "vector",
124
+ dimensions,
125
+ similarity,
126
+ optional: true,
127
+ },
128
+ vvec: {
129
+ type: "vector",
130
+ dimensions,
131
+ similarity,
132
+ optional: true,
133
+ },
134
+
135
+ metadata: {
136
+ type: "object",
137
+ optional: true,
138
+ },
139
+ };
140
+
141
+ return schema;
142
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Memory store interface.
3
+ */
4
+
5
+ import type {
6
+ MemoryRecord,
7
+ NewMemory,
8
+ MemoryRecordUpdate,
9
+ MemoryListOptions,
10
+ } from "./types";
11
+
12
+ /**
13
+ * Memory persistence store.
14
+ *
15
+ * Follows the same pattern as ThreadStore - simple CRUD operations.
16
+ */
17
+ export interface MemoryStore {
18
+ /**
19
+ * Get a memory by ID.
20
+ */
21
+ get(id: string): Promise<MemoryRecord | null>;
22
+
23
+ /**
24
+ * List memories matching the filter.
25
+ */
26
+ list(options?: MemoryListOptions): Promise<MemoryRecord[]>;
27
+
28
+ /**
29
+ * Create a new memory.
30
+ */
31
+ create(memory: NewMemory): Promise<MemoryRecord>;
32
+
33
+ /**
34
+ * Update an existing memory.
35
+ */
36
+ update(id: string, patch: MemoryRecordUpdate): Promise<MemoryRecord>;
37
+
38
+ /**
39
+ * Delete a memory.
40
+ */
41
+ delete(id: string): Promise<void>;
42
+
43
+ /**
44
+ * Delete multiple memories.
45
+ */
46
+ mdelete(ids: string[]): Promise<void>;
47
+ }