@lota-sdk/core 0.4.13 → 0.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/ai/embedding-cache.ts +17 -11
- package/src/ai-gateway/ai-gateway.ts +164 -94
- package/src/ai-gateway/index.ts +4 -1
- package/src/config/agent-defaults.ts +2 -2
- package/src/config/agent-types.ts +1 -1
- package/src/config/constants.ts +1 -1
- package/src/create-runtime.ts +259 -200
- package/src/db/cursor-pagination.ts +2 -9
- package/src/db/memory-store.ts +194 -175
- package/src/db/memory.ts +125 -71
- package/src/db/schema-fingerprint.ts +5 -4
- package/src/db/service-normalization.ts +4 -3
- package/src/db/service.ts +3 -2
- package/src/db/startup.ts +15 -16
- package/src/effect/errors.ts +161 -21
- package/src/effect/index.ts +0 -1
- package/src/embeddings/provider.ts +15 -7
- package/src/queues/autonomous-job.queue.ts +10 -22
- package/src/queues/delayed-node-promotion.queue.ts +8 -14
- package/src/queues/document-processor.queue.ts +13 -4
- package/src/queues/memory-consolidation.queue.ts +26 -14
- package/src/queues/plan-agent-heartbeat.queue.ts +48 -31
- package/src/queues/plan-scheduler.queue.ts +37 -15
- package/src/queues/queue-factory.ts +59 -35
- package/src/queues/standalone-worker.ts +3 -2
- package/src/redis/connection.ts +10 -3
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +5 -5
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/chat-message.ts +64 -1
- package/src/runtime/chat-run-orchestration.ts +33 -20
- package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
- package/src/runtime/context-compaction/context-compaction.ts +78 -66
- package/src/runtime/domain-layer.ts +19 -13
- package/src/runtime/execution-plan.ts +7 -3
- package/src/runtime/memory/memory-block.ts +3 -9
- package/src/runtime/memory/memory-scope.ts +3 -1
- package/src/runtime/plugin-resolution.ts +2 -1
- package/src/runtime/post-turn-side-effects.ts +6 -5
- package/src/runtime/retrieval-adapters.ts +8 -20
- package/src/runtime/runtime-config.ts +3 -9
- package/src/runtime/runtime-extensions.ts +2 -4
- package/src/runtime/runtime-lifecycle.ts +56 -16
- package/src/runtime/runtime-services.ts +180 -102
- package/src/runtime/runtime-worker-registry.ts +3 -1
- package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
- package/src/runtime/social-chat/social-chat-history.ts +21 -18
- package/src/runtime/social-chat/social-chat.ts +356 -223
- package/src/runtime/specialist-runner.ts +3 -1
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
- package/src/runtime/thread-turn-context.ts +142 -102
- package/src/runtime/turn-lifecycle.ts +15 -46
- package/src/services/agent-activity.service.ts +1 -1
- package/src/services/agent-executor.service.ts +107 -77
- package/src/services/autonomous-job.service.ts +354 -293
- package/src/services/background-work.service.ts +3 -3
- package/src/services/context-compaction.service.ts +7 -2
- package/src/services/document-chunk.service.ts +50 -32
- package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
- package/src/services/execution-plan/execution-plan.service.ts +162 -179
- package/src/services/feedback-loop.service.ts +5 -4
- package/src/services/graph-full-routing.ts +37 -36
- package/src/services/institutional-memory.service.ts +28 -30
- package/src/services/learned-skill.service.ts +107 -72
- package/src/services/memory/memory-errors.ts +4 -23
- package/src/services/memory/memory-org-memory.ts +10 -5
- package/src/services/memory/memory-rerank.ts +18 -6
- package/src/services/memory/memory.service.ts +170 -111
- package/src/services/memory/rerank.service.ts +29 -20
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +69 -75
- package/src/services/ownership-dispatcher.service.ts +40 -39
- package/src/services/plan/plan-agent-heartbeat.service.ts +22 -24
- package/src/services/plan/plan-agent-query.service.ts +39 -31
- package/src/services/plan/plan-completion-side-effects.ts +13 -17
- package/src/services/plan/plan-coordination.service.ts +2 -1
- package/src/services/plan/plan-cycle.service.ts +6 -5
- package/src/services/plan/plan-deadline.service.ts +57 -54
- package/src/services/plan/plan-event-delivery.service.ts +5 -4
- package/src/services/plan/plan-executor-graph.ts +18 -15
- package/src/services/plan/plan-executor.service.ts +235 -262
- package/src/services/plan/plan-run.service.ts +169 -93
- package/src/services/plan/plan-scheduler.service.ts +192 -202
- package/src/services/plan/plan-template.service.ts +1 -1
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/plan/plan-workspace.service.ts +23 -14
- package/src/services/plugin-executor.service.ts +5 -9
- package/src/services/queue-job.service.ts +117 -59
- package/src/services/recent-activity-title.service.ts +13 -12
- package/src/services/recent-activity.service.ts +6 -1
- package/src/services/social-chat-history.service.ts +29 -25
- package/src/services/system-executor.service.ts +5 -9
- package/src/services/thread/thread-active-run.ts +2 -2
- package/src/services/thread/thread-listing.ts +61 -57
- package/src/services/thread/thread-memory-block.ts +73 -48
- package/src/services/thread/thread-message.service.ts +76 -65
- package/src/services/thread/thread-record-store.ts +8 -8
- package/src/services/thread/thread-title.service.ts +10 -4
- package/src/services/thread/thread-turn-execution.ts +43 -45
- package/src/services/thread/thread-turn-preparation.service.ts +257 -135
- package/src/services/thread/thread-turn-streaming.ts +83 -92
- package/src/services/thread/thread-turn.ts +18 -16
- package/src/services/thread/thread.service.ts +135 -100
- package/src/services/user.service.ts +45 -48
- package/src/storage/attachment-parser.ts +6 -2
- package/src/storage/attachment-storage.service.ts +5 -6
- package/src/storage/generated-document-storage.service.ts +1 -1
- package/src/system-agents/context-compaction.agent.ts +10 -9
- package/src/system-agents/delegated-agent-factory.ts +30 -6
- package/src/system-agents/memory-reranker.agent.ts +10 -9
- package/src/system-agents/memory.agent.ts +10 -9
- package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
- package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
- package/src/system-agents/skill-extractor.agent.ts +13 -12
- package/src/system-agents/skill-manager.agent.ts +13 -12
- package/src/system-agents/thread-router.agent.ts +11 -7
- package/src/system-agents/title-generator.agent.ts +13 -12
- package/src/tools/fetch-webpage.tool.ts +13 -13
- package/src/tools/memory-block.tool.ts +3 -1
- package/src/tools/plan-approval.tool.ts +4 -2
- package/src/tools/read-file-parts.tool.ts +10 -4
- package/src/tools/remember-memory.tool.ts +3 -1
- package/src/tools/research-topic.tool.ts +9 -5
- package/src/tools/search-web.tool.ts +16 -16
- package/src/tools/search.tool.ts +20 -5
- package/src/tools/team-think.tool.ts +61 -38
- package/src/utils/async.ts +5 -5
- package/src/utils/errors.ts +19 -18
- package/src/utils/sse-keepalive.ts +28 -25
- package/src/workers/bootstrap.ts +75 -11
- package/src/workers/memory-consolidation.worker.ts +82 -91
- package/src/workers/organization-learning.worker.ts +14 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
- package/src/workers/skill-extraction.runner.ts +97 -61
- package/src/workers/utils/repo-structure-extractor.ts +13 -8
- package/src/workers/utils/thread-message-query.ts +24 -24
- package/src/workers/worker-utils.ts +23 -4
- package/src/effect/helpers.ts +0 -123
package/src/db/memory-store.ts
CHANGED
|
@@ -3,8 +3,9 @@ import { Schema, Duration, Effect, Metric, Schedule } from 'effect'
|
|
|
3
3
|
import { BoundQuery, eq, inside } from 'surrealdb'
|
|
4
4
|
|
|
5
5
|
import { aiLogger } from '../config/logger'
|
|
6
|
+
import { ERROR_TAGS } from '../effect/errors'
|
|
6
7
|
import { ProviderEmbeddings } from '../embeddings/provider'
|
|
7
|
-
import type {
|
|
8
|
+
import type { BackgroundWorkServiceTag } from '../services/background-work.service'
|
|
8
9
|
import { clampImportance, truncateText } from '../utils/string'
|
|
9
10
|
import { memoryQueryBuilder } from './memory-query-builder'
|
|
10
11
|
import type { RelationCounts } from './memory-store.helpers'
|
|
@@ -25,7 +26,7 @@ import type { SurrealDBError } from './service-normalization'
|
|
|
25
26
|
import { TABLES } from './tables'
|
|
26
27
|
import { isRetriableTransactionConflict } from './transaction-conflict'
|
|
27
28
|
|
|
28
|
-
type BackgroundWorker = Context.Service.Shape<typeof
|
|
29
|
+
type BackgroundWorker = Context.Service.Shape<typeof BackgroundWorkServiceTag>
|
|
29
30
|
|
|
30
31
|
const MEMORY_TABLE = TABLES.MEMORY
|
|
31
32
|
const MEMORY_HISTORY_TABLE = TABLES.MEMORY_HISTORY
|
|
@@ -45,29 +46,11 @@ const TOUCH_MEMORIES_RETRY_OPTIONS = {
|
|
|
45
46
|
while: (error: unknown) => isRetriableTransactionConflict(error),
|
|
46
47
|
} as const
|
|
47
48
|
|
|
48
|
-
class MemoryStoreError extends Schema.TaggedErrorClass<MemoryStoreError>()(
|
|
49
|
+
class MemoryStoreError extends Schema.TaggedErrorClass<MemoryStoreError>()(ERROR_TAGS.MemoryStoreError, {
|
|
49
50
|
message: Schema.String,
|
|
50
51
|
cause: Schema.Defect,
|
|
51
52
|
}) {}
|
|
52
53
|
|
|
53
|
-
function tryMemoryStorePromise<A, E, R = never>(
|
|
54
|
-
message: string,
|
|
55
|
-
thunk: () => PromiseLike<A> | Effect.Effect<A, E, R>,
|
|
56
|
-
): Effect.Effect<A, MemoryStoreError, R> {
|
|
57
|
-
return Effect.suspend(() => {
|
|
58
|
-
try {
|
|
59
|
-
const value = thunk()
|
|
60
|
-
if (Effect.isEffect(value)) {
|
|
61
|
-
return value.pipe(Effect.mapError((cause) => new MemoryStoreError({ message, cause })))
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return Effect.tryPromise({ try: () => value, catch: (cause) => new MemoryStoreError({ message, cause }) })
|
|
65
|
-
} catch (cause) {
|
|
66
|
-
return Effect.fail(new MemoryStoreError({ message, cause }))
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
|
|
71
54
|
interface EmbeddingClient {
|
|
72
55
|
embedQuery(text: string): Promise<number[]>
|
|
73
56
|
}
|
|
@@ -165,8 +148,8 @@ export class SurrealMemoryStore {
|
|
|
165
148
|
LIMIT $limit
|
|
166
149
|
`
|
|
167
150
|
|
|
168
|
-
return
|
|
169
|
-
|
|
151
|
+
return this.db
|
|
152
|
+
.query<SurrealMemoryRow>(
|
|
170
153
|
new BoundQuery(sql, {
|
|
171
154
|
scopeId: options.scopeId,
|
|
172
155
|
memoryType: options.memoryType,
|
|
@@ -174,8 +157,11 @@ export class SurrealMemoryStore {
|
|
|
174
157
|
minImportance: options.minImportance,
|
|
175
158
|
limit: options.limit,
|
|
176
159
|
}),
|
|
177
|
-
)
|
|
178
|
-
|
|
160
|
+
)
|
|
161
|
+
.pipe(
|
|
162
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to list top memories.', cause })),
|
|
163
|
+
Effect.map((rows) => rows.map((row: SurrealMemoryRow) => mapRowToMemoryRecord(row))),
|
|
164
|
+
)
|
|
179
165
|
}
|
|
180
166
|
|
|
181
167
|
listTopMemories(options: {
|
|
@@ -245,7 +231,10 @@ export class SurrealMemoryStore {
|
|
|
245
231
|
const normalized = content.trim()
|
|
246
232
|
if (!normalized) return Effect.succeed([])
|
|
247
233
|
|
|
248
|
-
return
|
|
234
|
+
return Effect.tryPromise({
|
|
235
|
+
try: () => this.embeddings.embedQuery(normalized),
|
|
236
|
+
catch: (cause) => new MemoryStoreError({ message: 'Failed to generate memory embedding.', cause }),
|
|
237
|
+
})
|
|
249
238
|
}
|
|
250
239
|
|
|
251
240
|
warmEmbedding(content: string): Effect.Effect<void, MemoryStoreError, never> {
|
|
@@ -272,33 +261,34 @@ export class SurrealMemoryStore {
|
|
|
272
261
|
WHERE id IN $memoryIds
|
|
273
262
|
`
|
|
274
263
|
|
|
275
|
-
return
|
|
276
|
-
|
|
264
|
+
return this.db
|
|
265
|
+
.query<{
|
|
277
266
|
id: RecordIdInput
|
|
278
267
|
supersedeCount: number
|
|
279
268
|
contradictCount: number
|
|
280
269
|
supportCount: number
|
|
281
270
|
contradictionTexts: string[] | null
|
|
282
|
-
}>(new BoundQuery(sql, { memoryIds: memoryRefs }))
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
271
|
+
}>(new BoundQuery(sql, { memoryIds: memoryRefs }))
|
|
272
|
+
.pipe(
|
|
273
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to fetch relation counts.', cause })),
|
|
274
|
+
Effect.map((results) => {
|
|
275
|
+
const countsMap = new Map<string, RelationCounts>()
|
|
276
|
+
for (const row of results) {
|
|
277
|
+
const rawTexts = row.contradictionTexts ?? []
|
|
278
|
+
const contradictions = rawTexts.filter(
|
|
279
|
+
(text: string | null): text is string => typeof text === 'string' && text.length > 0,
|
|
280
|
+
)
|
|
281
|
+
countsMap.set(recordIdToString(row.id, TABLES.MEMORY), {
|
|
282
|
+
supersedeCount: row.supersedeCount,
|
|
283
|
+
contradictCount: row.contradictCount,
|
|
284
|
+
supportCount: row.supportCount,
|
|
285
|
+
contradictions,
|
|
286
|
+
})
|
|
287
|
+
}
|
|
298
288
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
289
|
+
return countsMap
|
|
290
|
+
}),
|
|
291
|
+
)
|
|
302
292
|
}
|
|
303
293
|
|
|
304
294
|
private touchMemories(memoryIds: string[]): Effect.Effect<void> {
|
|
@@ -331,9 +321,8 @@ export class SurrealMemoryStore {
|
|
|
331
321
|
sql: string,
|
|
332
322
|
bindVars: Record<string, unknown>,
|
|
333
323
|
): Effect.Effect<T[], MemoryStoreError, never> {
|
|
334
|
-
return
|
|
335
|
-
|
|
336
|
-
).pipe(
|
|
324
|
+
return this.db.queryAll<unknown>(new BoundQuery(sql, bindVars)).pipe(
|
|
325
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to query memory statements.', cause })),
|
|
337
326
|
Effect.map((statements) => {
|
|
338
327
|
const finalStatement = statements.at(-1)
|
|
339
328
|
return Array.isArray(finalStatement) ? (finalStatement as T[]) : []
|
|
@@ -366,9 +355,11 @@ export class SurrealMemoryStore {
|
|
|
366
355
|
if (vectorResults.length > 0) return vectorResults
|
|
367
356
|
|
|
368
357
|
const recentLimit = Math.max(50, options.limit * 10)
|
|
369
|
-
const recent = yield*
|
|
370
|
-
|
|
371
|
-
|
|
358
|
+
const recent = yield* this.listRecentBasic({
|
|
359
|
+
scopeId: options.scopeId,
|
|
360
|
+
limit: recentLimit,
|
|
361
|
+
memoryType: options.memoryType,
|
|
362
|
+
}).pipe(Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to list recent memories.', cause })))
|
|
372
363
|
if (recent.length === 0) return []
|
|
373
364
|
|
|
374
365
|
const scoredRows = recent
|
|
@@ -425,8 +416,8 @@ export class SurrealMemoryStore {
|
|
|
425
416
|
return nearDup.id
|
|
426
417
|
}
|
|
427
418
|
|
|
428
|
-
const result = yield*
|
|
429
|
-
|
|
419
|
+
const result = yield* this.db
|
|
420
|
+
.insert<{ id: RecordIdInput }>(MEMORY_TABLE, {
|
|
430
421
|
content,
|
|
431
422
|
embedding,
|
|
432
423
|
hash,
|
|
@@ -435,8 +426,8 @@ export class SurrealMemoryStore {
|
|
|
435
426
|
metadata,
|
|
436
427
|
importance: normalizedImportance,
|
|
437
428
|
durability,
|
|
438
|
-
})
|
|
439
|
-
|
|
429
|
+
})
|
|
430
|
+
.pipe(Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to insert memory.', cause })))
|
|
440
431
|
const id = result[0]?.id ? recordIdToString(result[0].id, TABLES.MEMORY) : ''
|
|
441
432
|
yield* this.recordHistoryEffect(id, null, content, 'ADD')
|
|
442
433
|
return id
|
|
@@ -456,8 +447,8 @@ export class SurrealMemoryStore {
|
|
|
456
447
|
}
|
|
457
448
|
|
|
458
449
|
private getByHashEffect(hash: string): Effect.Effect<MemoryRecord | null, MemoryStoreError, never> {
|
|
459
|
-
return
|
|
460
|
-
|
|
450
|
+
return this.db
|
|
451
|
+
.query<SurrealMemoryRow>(
|
|
461
452
|
new BoundQuery(
|
|
462
453
|
`
|
|
463
454
|
SELECT *
|
|
@@ -467,13 +458,14 @@ export class SurrealMemoryStore {
|
|
|
467
458
|
`,
|
|
468
459
|
{ hash },
|
|
469
460
|
),
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
461
|
+
)
|
|
462
|
+
.pipe(
|
|
463
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to query memory by hash.', cause })),
|
|
464
|
+
Effect.map((rows) => {
|
|
465
|
+
const row = rows.at(0)
|
|
466
|
+
return row ? mapRowToMemoryRecord(row) : null
|
|
467
|
+
}),
|
|
468
|
+
)
|
|
477
469
|
}
|
|
478
470
|
|
|
479
471
|
getByHash(hash: string): Effect.Effect<MemoryRecord | null, MemoryStoreError, never> {
|
|
@@ -768,9 +760,8 @@ export class SurrealMemoryStore {
|
|
|
768
760
|
|
|
769
761
|
private getEffect(id: string): Effect.Effect<MemoryRecord | null, MemoryStoreError, never> {
|
|
770
762
|
const sql = `SELECT * FROM ${MEMORY_TABLE} WHERE id = $id`
|
|
771
|
-
return
|
|
772
|
-
|
|
773
|
-
).pipe(
|
|
763
|
+
return this.db.query<SurrealMemoryRow>(new BoundQuery(sql, { id: ensureRecordId(id, TABLES.MEMORY) })).pipe(
|
|
764
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to get memory.', cause })),
|
|
774
765
|
Effect.map((results) => {
|
|
775
766
|
const row = results.at(0)
|
|
776
767
|
if (!row) return null
|
|
@@ -814,9 +805,9 @@ export class SurrealMemoryStore {
|
|
|
814
805
|
updatePayload.metadata = metadata
|
|
815
806
|
}
|
|
816
807
|
|
|
817
|
-
yield*
|
|
818
|
-
|
|
819
|
-
|
|
808
|
+
yield* this.db
|
|
809
|
+
.updateWhere(MEMORY_TABLE, eq('id', ensureRecordId(id, TABLES.MEMORY)), updatePayload)
|
|
810
|
+
.pipe(Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to update memory.', cause })))
|
|
820
811
|
yield* this.recordHistoryEffect(id, existing.content, newContent, 'UPDATE')
|
|
821
812
|
}.bind(this),
|
|
822
813
|
)
|
|
@@ -836,7 +827,9 @@ export class SurrealMemoryStore {
|
|
|
836
827
|
const existing = yield* this.getEffect(id)
|
|
837
828
|
if (!existing) return
|
|
838
829
|
|
|
839
|
-
yield*
|
|
830
|
+
yield* this.db
|
|
831
|
+
.deleteById(MEMORY_TABLE, id)
|
|
832
|
+
.pipe(Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to delete memory.', cause })))
|
|
840
833
|
yield* this.recordHistoryEffect(id, existing.content, null, 'DELETE')
|
|
841
834
|
}.bind(this),
|
|
842
835
|
)
|
|
@@ -888,9 +881,10 @@ export class SurrealMemoryStore {
|
|
|
888
881
|
${limitClause}
|
|
889
882
|
`
|
|
890
883
|
|
|
891
|
-
return yield*
|
|
892
|
-
|
|
893
|
-
|
|
884
|
+
return yield* this.db.query<SurrealMemoryRow>(new BoundQuery(sql, bindVars)).pipe(
|
|
885
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to list memories.', cause })),
|
|
886
|
+
Effect.map((results) => results.map((row: SurrealMemoryRow) => mapRowToMemoryRecord(row))),
|
|
887
|
+
)
|
|
894
888
|
}.bind(this),
|
|
895
889
|
)
|
|
896
890
|
}
|
|
@@ -910,19 +904,25 @@ export class SurrealMemoryStore {
|
|
|
910
904
|
const toRef = ensureRecordId(toId, TABLES.MEMORY)
|
|
911
905
|
return Effect.gen(
|
|
912
906
|
function* (this: SurrealMemoryStore) {
|
|
913
|
-
yield*
|
|
914
|
-
|
|
915
|
-
|
|
907
|
+
yield* this.db
|
|
908
|
+
.relate(fromRef, MEMORY_RELATION_TABLE, toRef, { relationType, confidence: normalizedConfidence })
|
|
909
|
+
.pipe(
|
|
910
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to create memory relation.', cause })),
|
|
911
|
+
)
|
|
916
912
|
if (relationType !== 'supersedes') return
|
|
917
913
|
|
|
918
|
-
yield*
|
|
919
|
-
|
|
914
|
+
yield* this.db
|
|
915
|
+
.query(
|
|
920
916
|
new BoundQuery(
|
|
921
917
|
`UPDATE ${MEMORY_TABLE} SET validUntil = time::now() WHERE id = $toId AND validUntil IS NONE`,
|
|
922
918
|
{ toId: toRef },
|
|
923
919
|
),
|
|
924
|
-
)
|
|
925
|
-
|
|
920
|
+
)
|
|
921
|
+
.pipe(
|
|
922
|
+
Effect.mapError(
|
|
923
|
+
(cause) => new MemoryStoreError({ message: 'Failed to update superseded memory validity.', cause }),
|
|
924
|
+
),
|
|
925
|
+
)
|
|
926
926
|
yield* this.flagDependentsForReviewEffect(toRef)
|
|
927
927
|
}.bind(this),
|
|
928
928
|
)
|
|
@@ -938,8 +938,8 @@ export class SurrealMemoryStore {
|
|
|
938
938
|
}
|
|
939
939
|
|
|
940
940
|
private flagDependentsForReviewEffect(supersededId: RecordIdRef): Effect.Effect<void, MemoryStoreError, never> {
|
|
941
|
-
return
|
|
942
|
-
|
|
941
|
+
return this.db
|
|
942
|
+
.query<{ id: RecordIdInput }>(
|
|
943
943
|
new BoundQuery(
|
|
944
944
|
`SELECT id FROM ${MEMORY_TABLE}
|
|
945
945
|
WHERE ->${MEMORY_RELATION_TABLE}[WHERE relationType = 'depends_on']->${MEMORY_TABLE} CONTAINS $supersededId
|
|
@@ -947,23 +947,27 @@ export class SurrealMemoryStore {
|
|
|
947
947
|
AND needsReview = false`,
|
|
948
948
|
{ supersededId },
|
|
949
949
|
),
|
|
950
|
-
)
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
Effect.
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
950
|
+
)
|
|
951
|
+
.pipe(
|
|
952
|
+
Effect.mapError(
|
|
953
|
+
(cause) => new MemoryStoreError({ message: 'Failed to flag dependent memories for review.', cause }),
|
|
954
|
+
),
|
|
955
|
+
Effect.flatMap((dependents) => {
|
|
956
|
+
if (dependents.length === 0) return Effect.void
|
|
957
|
+
|
|
958
|
+
const ids = dependents.map((d: { id: RecordIdInput }) => ensureRecordId(d.id, TABLES.MEMORY))
|
|
959
|
+
return this.db.updateWhere(MEMORY_TABLE, inside('id', ids), { needsReview: true }).pipe(
|
|
960
|
+
Effect.mapError(
|
|
961
|
+
(cause) => new MemoryStoreError({ message: 'Failed to flag dependent memories for review.', cause }),
|
|
962
|
+
),
|
|
963
|
+
Effect.tap(() =>
|
|
964
|
+
Effect.sync(() => {
|
|
965
|
+
aiLogger.debug`Flagged ${dependents.length} dependent memories for review after supersede`
|
|
966
|
+
}),
|
|
967
|
+
),
|
|
968
|
+
)
|
|
969
|
+
}),
|
|
970
|
+
)
|
|
967
971
|
}
|
|
968
972
|
|
|
969
973
|
getRelatedMemories(
|
|
@@ -986,19 +990,20 @@ export class SurrealMemoryStore {
|
|
|
986
990
|
FROM ONLY $memoryId
|
|
987
991
|
`
|
|
988
992
|
|
|
989
|
-
return
|
|
990
|
-
|
|
993
|
+
return this.db
|
|
994
|
+
.query<{ relatesTo: SurrealMemoryRow[]; relatedBy: SurrealMemoryRow[] }>(
|
|
991
995
|
new BoundQuery(sql, { memoryId: ensureRecordId(memoryId, TABLES.MEMORY), relationType }),
|
|
992
|
-
)
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
996
|
+
)
|
|
997
|
+
.pipe(
|
|
998
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to get related memories.', cause })),
|
|
999
|
+
Effect.map((result) => {
|
|
1000
|
+
const data = result[0] ?? { relatesTo: [], relatedBy: [] }
|
|
1001
|
+
return {
|
|
1002
|
+
relatesTo: data.relatesTo.map((row: SurrealMemoryRow) => mapRowToMemoryRecord(row)),
|
|
1003
|
+
relatedBy: data.relatedBy.map((row: SurrealMemoryRow) => mapRowToMemoryRecord(row)),
|
|
1004
|
+
}
|
|
1005
|
+
}),
|
|
1006
|
+
)
|
|
1002
1007
|
}
|
|
1003
1008
|
|
|
1004
1009
|
findConflicts(
|
|
@@ -1019,16 +1024,17 @@ export class SurrealMemoryStore {
|
|
|
1019
1024
|
AND count(<-${MEMORY_RELATION_TABLE}[WHERE relationType = 'contradicts']) > 0
|
|
1020
1025
|
`
|
|
1021
1026
|
|
|
1022
|
-
return
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1027
|
+
return this.db
|
|
1028
|
+
.query<SurrealMemoryRow & { contradictedBy: SurrealMemoryRow[] }>(new BoundQuery(sql, { scopeId }))
|
|
1029
|
+
.pipe(
|
|
1030
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to find memory conflicts.', cause })),
|
|
1031
|
+
Effect.map((results) =>
|
|
1032
|
+
results.map((row: SurrealMemoryRow & { contradictedBy: SurrealMemoryRow[] }) => ({
|
|
1033
|
+
memory: mapRowToMemoryRecord(row),
|
|
1034
|
+
contradictedBy: row.contradictedBy.map((r: SurrealMemoryRow) => mapRowToMemoryRecord(r)),
|
|
1035
|
+
})),
|
|
1036
|
+
),
|
|
1037
|
+
)
|
|
1032
1038
|
}
|
|
1033
1039
|
|
|
1034
1040
|
private graphWalkEffect(
|
|
@@ -1057,8 +1063,8 @@ export class SurrealMemoryStore {
|
|
|
1057
1063
|
|
|
1058
1064
|
return Effect.all(
|
|
1059
1065
|
frontier.map((nodeId) =>
|
|
1060
|
-
|
|
1061
|
-
|
|
1066
|
+
this.db
|
|
1067
|
+
.query<{
|
|
1062
1068
|
outEdges: Array<{
|
|
1063
1069
|
from: RecordIdInput
|
|
1064
1070
|
to: RecordIdInput
|
|
@@ -1076,12 +1082,12 @@ export class SurrealMemoryStore {
|
|
|
1076
1082
|
->${MEMORY_RELATION_TABLE}->${MEMORY_TABLE}[WHERE archivedAt IS NONE].* AS outMemories,
|
|
1077
1083
|
<-${MEMORY_RELATION_TABLE}.{in AS from, out AS to, relationType, confidence} AS inEdges,
|
|
1078
1084
|
<-${MEMORY_RELATION_TABLE}<-${MEMORY_TABLE}[WHERE archivedAt IS NONE].* AS inMemories
|
|
1079
|
-
|
|
1085
|
+
FROM ONLY $nodeId
|
|
1080
1086
|
`,
|
|
1081
1087
|
{ nodeId: ensureRecordId(nodeId, TABLES.MEMORY) },
|
|
1082
1088
|
),
|
|
1083
|
-
)
|
|
1084
|
-
|
|
1089
|
+
)
|
|
1090
|
+
.pipe(Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to walk memory graph.', cause }))),
|
|
1085
1091
|
),
|
|
1086
1092
|
).pipe(
|
|
1087
1093
|
Effect.flatMap((results) =>
|
|
@@ -1152,9 +1158,10 @@ export class SurrealMemoryStore {
|
|
|
1152
1158
|
...(prevValue === null ? {} : { prevValue }),
|
|
1153
1159
|
...(newValue === null ? {} : { newValue }),
|
|
1154
1160
|
}
|
|
1155
|
-
return
|
|
1156
|
-
|
|
1157
|
-
|
|
1161
|
+
return this.db.insert<Record<string, unknown>>(MEMORY_HISTORY_TABLE, historyRow).pipe(
|
|
1162
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to record memory history.', cause })),
|
|
1163
|
+
Effect.asVoid,
|
|
1164
|
+
)
|
|
1158
1165
|
}
|
|
1159
1166
|
|
|
1160
1167
|
private enrichWithNeighborsEffect(
|
|
@@ -1175,39 +1182,42 @@ export class SurrealMemoryStore {
|
|
|
1175
1182
|
WHERE id IN $ids
|
|
1176
1183
|
`
|
|
1177
1184
|
|
|
1178
|
-
return
|
|
1179
|
-
|
|
1185
|
+
return this.db
|
|
1186
|
+
.query<{
|
|
1180
1187
|
id: RecordIdInput
|
|
1181
1188
|
outgoing: Array<{ id: RecordIdInput; content: string; relationType?: string }> | null
|
|
1182
1189
|
incoming: Array<{ id: RecordIdInput; content: string; relationType?: string }> | null
|
|
1183
|
-
}>(new BoundQuery(sql, { ids: topRefs }))
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1190
|
+
}>(new BoundQuery(sql, { ids: topRefs }))
|
|
1191
|
+
.pipe(
|
|
1192
|
+
Effect.mapError(
|
|
1193
|
+
(cause) => new MemoryStoreError({ message: 'Failed to enrich memories with neighbors.', cause }),
|
|
1194
|
+
),
|
|
1195
|
+
Effect.map((rows) => {
|
|
1196
|
+
const neighborMap = new Map<string, string[]>()
|
|
1197
|
+
for (const row of rows) {
|
|
1198
|
+
const rowId = recordIdToString(row.id, TABLES.MEMORY)
|
|
1199
|
+
const contexts: string[] = []
|
|
1200
|
+
const seen = new Set<string>()
|
|
1201
|
+
for (const neighbor of [...(row.outgoing ?? []), ...(row.incoming ?? [])]) {
|
|
1202
|
+
const neighborId = recordIdToString(neighbor.id, TABLES.MEMORY)
|
|
1203
|
+
if (!neighbor.content || seen.has(neighborId)) continue
|
|
1204
|
+
seen.add(neighborId)
|
|
1205
|
+
const label = neighbor.relationType ? `[${neighbor.relationType}]` : ''
|
|
1206
|
+
const truncated = truncateText(neighbor.content, 200)
|
|
1207
|
+
contexts.push(`${label} ${truncated}`.trim())
|
|
1208
|
+
}
|
|
1209
|
+
if (contexts.length > 0) {
|
|
1210
|
+
neighborMap.set(rowId, contexts)
|
|
1211
|
+
}
|
|
1201
1212
|
}
|
|
1202
|
-
}
|
|
1203
1213
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1214
|
+
return results.map((result) => {
|
|
1215
|
+
const neighbors = neighborMap.get(result.id)
|
|
1216
|
+
if (!neighbors) return result
|
|
1217
|
+
return { ...result, metadata: { ...result.metadata, relatedContext: neighbors } }
|
|
1218
|
+
})
|
|
1219
|
+
}),
|
|
1220
|
+
)
|
|
1211
1221
|
}
|
|
1212
1222
|
|
|
1213
1223
|
enrichWithNeighbors(
|
|
@@ -1221,8 +1231,8 @@ export class SurrealMemoryStore {
|
|
|
1221
1231
|
scopeId: string,
|
|
1222
1232
|
limit: number = 5,
|
|
1223
1233
|
): Effect.Effect<MemorySearchResult[], MemoryStoreError, never> {
|
|
1224
|
-
return
|
|
1225
|
-
|
|
1234
|
+
return this.db
|
|
1235
|
+
.query<BasicSearchRow>(
|
|
1226
1236
|
new BoundQuery(
|
|
1227
1237
|
`SELECT id, content, metadata
|
|
1228
1238
|
FROM ${MEMORY_TABLE}
|
|
@@ -1233,17 +1243,18 @@ export class SurrealMemoryStore {
|
|
|
1233
1243
|
LIMIT $limit`,
|
|
1234
1244
|
{ scopeId, limit },
|
|
1235
1245
|
),
|
|
1236
|
-
)
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1246
|
+
)
|
|
1247
|
+
.pipe(
|
|
1248
|
+
Effect.mapError((cause) => new MemoryStoreError({ message: 'Failed to list stale memories.', cause })),
|
|
1249
|
+
Effect.map((results) =>
|
|
1250
|
+
results.map((row: BasicSearchRow) => ({
|
|
1251
|
+
id: recordIdToString(row.id, TABLES.MEMORY),
|
|
1252
|
+
content: row.content,
|
|
1253
|
+
score: 0,
|
|
1254
|
+
metadata: { ...row.metadata, needsReview: true },
|
|
1255
|
+
})),
|
|
1256
|
+
),
|
|
1257
|
+
)
|
|
1247
1258
|
}
|
|
1248
1259
|
|
|
1249
1260
|
getStaleMemories(scopeId: string, limit: number = 5): Effect.Effect<MemorySearchResult[], MemoryStoreError, never> {
|
|
@@ -1253,12 +1264,20 @@ export class SurrealMemoryStore {
|
|
|
1253
1264
|
|
|
1254
1265
|
export function createMemoryStore(
|
|
1255
1266
|
db: SurrealDBService,
|
|
1256
|
-
options: {
|
|
1267
|
+
options: {
|
|
1268
|
+
embeddingModel: string
|
|
1269
|
+
openRouterApiKey?: string
|
|
1270
|
+
runPromise: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>
|
|
1271
|
+
},
|
|
1257
1272
|
background: BackgroundWorker,
|
|
1258
1273
|
): SurrealMemoryStore {
|
|
1259
1274
|
return new SurrealMemoryStore(
|
|
1260
1275
|
db,
|
|
1261
|
-
new ProviderEmbeddings({
|
|
1276
|
+
new ProviderEmbeddings({
|
|
1277
|
+
modelId: options.embeddingModel,
|
|
1278
|
+
openRouterApiKey: options.openRouterApiKey,
|
|
1279
|
+
runPromise: options.runPromise,
|
|
1280
|
+
}),
|
|
1262
1281
|
background,
|
|
1263
1282
|
)
|
|
1264
1283
|
}
|