@lota-sdk/core 0.4.8 → 0.4.10
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 +11 -12
- package/src/ai/embedding-cache.ts +96 -22
- package/src/ai-gateway/ai-gateway.ts +766 -223
- package/src/config/agent-defaults.ts +189 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/background-processing.ts +1 -1
- package/src/config/constants.ts +8 -2
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +299 -19
- package/src/config/thread-defaults.ts +40 -20
- package/src/create-runtime.ts +200 -449
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +868 -601
- package/src/db/memory.ts +396 -280
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +288 -0
- package/src/db/service.ts +912 -779
- package/src/db/startup.ts +153 -68
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +96 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +123 -0
- package/src/effect/index.ts +24 -0
- package/src/effect/layers.ts +238 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +46 -0
- package/src/effect/services.ts +61 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +128 -83
- package/src/index.ts +48 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +117 -73
- package/src/queues/context-compaction.queue.ts +50 -17
- package/src/queues/delayed-node-promotion.queue.ts +46 -17
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +26 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
- package/src/queues/plan-scheduler.queue.ts +97 -33
- package/src/queues/post-chat-memory.queue.ts +56 -26
- package/src/queues/queue-factory.ts +227 -59
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +45 -11
- package/src/redis/connection.ts +182 -113
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +20 -0
- package/src/redis/stream-context.ts +92 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +5 -2
- package/src/runtime/agent-stream-helpers.ts +24 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +16 -4
- package/src/runtime/helper-model.ts +139 -48
- package/src/runtime/index.ts +7 -8
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/memory/memory-scope.ts +53 -0
- package/src/runtime/plugin-resolution.ts +124 -25
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +177 -130
- package/src/runtime/retrieval-adapters.ts +40 -6
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +23 -25
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
- package/src/runtime/social-chat/social-chat.ts +630 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +183 -111
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +253 -149
- package/src/services/artifact.service.ts +231 -149
- package/src/services/attachment.service.ts +171 -115
- package/src/services/autonomous-job.service.ts +890 -491
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +13 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +151 -88
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +278 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +101 -168
- package/src/services/graph-full-routing.ts +193 -0
- package/src/services/index.ts +19 -21
- package/src/services/institutional-memory.service.ts +213 -125
- package/src/services/learned-skill.service.ts +368 -260
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +50 -0
- package/src/services/memory/memory-preseeded.ts +86 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
- package/src/services/memory/memory.service.ts +674 -0
- package/src/services/memory/rerank.service.ts +201 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +29 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +153 -77
- package/src/services/ownership-dispatcher.service.ts +456 -263
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +169 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +405 -0
- package/src/services/plan/plan-deadline.service.ts +533 -0
- package/src/services/plan/plan-event-delivery.service.ts +266 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +522 -0
- package/src/services/plan/plan-executor-helpers.ts +307 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1737 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +637 -0
- package/src/services/plan/plan-scheduler.service.ts +379 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +36 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +131 -0
- package/src/services/plugin-executor.service.ts +102 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +288 -231
- package/src/services/recent-activity-title.service.ts +73 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +190 -122
- package/src/services/system-executor.service.ts +96 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +385 -0
- package/src/services/thread/thread-listing.ts +199 -0
- package/src/services/thread/thread-memory-block.ts +130 -0
- package/src/services/thread/thread-message.service.ts +379 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
- package/src/services/thread/thread-turn-streaming.ts +403 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +376 -0
- package/src/services/thread/thread.service.ts +344 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +334 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +3 -3
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +3 -3
- package/src/system-agents/memory.agent.ts +3 -3
- package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
- package/src/system-agents/skill-extractor.agent.ts +3 -3
- package/src/system-agents/skill-manager.agent.ts +3 -3
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +3 -3
- package/src/tools/execution-plan.tool.ts +241 -171
- package/src/tools/fetch-webpage.tool.ts +29 -18
- package/src/tools/firecrawl-client.ts +26 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +57 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +33 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +125 -84
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +25 -22
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +111 -20
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +164 -52
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +74 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/config/search.ts +0 -3
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/agent-types.ts +0 -1
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/memory-scope.ts +0 -43
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -914
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/rerank.service.ts +0 -156
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -1,18 +1,67 @@
|
|
|
1
1
|
import type { SandboxedJob } from 'bullmq'
|
|
2
|
+
import { Schema, Effect } from 'effect'
|
|
2
3
|
import { BoundQuery, eq, inside } from 'surrealdb'
|
|
3
4
|
|
|
4
5
|
import { MEMORY } from '../config/constants'
|
|
5
6
|
import { serverLogger } from '../config/logger'
|
|
6
7
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
7
8
|
import type { RecordIdInput } from '../db/record-id'
|
|
8
|
-
import {
|
|
9
|
+
import type { SurrealDBService } from '../db/service'
|
|
9
10
|
import { TABLES } from '../db/tables'
|
|
11
|
+
import { effectTryPromise } from '../effect/helpers'
|
|
12
|
+
import { DatabaseServiceTag } from '../effect/services'
|
|
10
13
|
import type { MemoryConsolidationJob } from '../queues/memory-consolidation.queue'
|
|
14
|
+
import { nowDate, unsafeDateFrom } from '../utils/date-time'
|
|
15
|
+
import { getErrorMessage } from '../utils/errors'
|
|
11
16
|
import { initializeSandboxedWorkerRuntime } from './bootstrap'
|
|
12
17
|
import { toSandboxedWorkerError } from './utils/sandbox-error'
|
|
13
18
|
import { createTracedWorkerProcessor } from './worker-utils'
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
class MemoryConsolidationError extends Schema.TaggedErrorClass<MemoryConsolidationError>()('MemoryConsolidationError', {
|
|
21
|
+
stage: Schema.Literals([
|
|
22
|
+
'read-memories',
|
|
23
|
+
'read-neighbors',
|
|
24
|
+
'archive-memory',
|
|
25
|
+
'relate-memories',
|
|
26
|
+
'write-history',
|
|
27
|
+
'load-stale',
|
|
28
|
+
'update-stale',
|
|
29
|
+
'load-middle-nodes',
|
|
30
|
+
'read-existing-relation',
|
|
31
|
+
'write-relation',
|
|
32
|
+
'update-middle-node',
|
|
33
|
+
'decay-standard',
|
|
34
|
+
'decay-ephemeral',
|
|
35
|
+
'cleanup-relations',
|
|
36
|
+
'load-scope-ids',
|
|
37
|
+
]),
|
|
38
|
+
message: Schema.String,
|
|
39
|
+
cause: Schema.Defect,
|
|
40
|
+
}) {}
|
|
41
|
+
|
|
42
|
+
function toMemoryConsolidationError(
|
|
43
|
+
stage: MemoryConsolidationError['stage'],
|
|
44
|
+
cause: unknown,
|
|
45
|
+
): MemoryConsolidationError {
|
|
46
|
+
return new MemoryConsolidationError({ stage, message: getErrorMessage(cause), cause })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function tryMemoryConsolidationEffect<A, R = never>(
|
|
50
|
+
stage: MemoryConsolidationError['stage'],
|
|
51
|
+
thunk: () => PromiseLike<A> | Effect.Effect<A, unknown, R>,
|
|
52
|
+
): Effect.Effect<A, MemoryConsolidationError, R> {
|
|
53
|
+
return effectTryPromise(thunk, (cause) => toMemoryConsolidationError(stage, cause))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const runtime = await initializeSandboxedWorkerRuntime()
|
|
57
|
+
|
|
58
|
+
const memoryConsolidationDatabaseService: SurrealDBService = await runtime.runPromise(
|
|
59
|
+
Effect.service(DatabaseServiceTag),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
function db(): SurrealDBService {
|
|
63
|
+
return memoryConsolidationDatabaseService
|
|
64
|
+
}
|
|
16
65
|
|
|
17
66
|
const MEMORY_TABLE = TABLES.MEMORY
|
|
18
67
|
const MEMORY_RELATION_TABLE = TABLES.MEMORY_RELATION
|
|
@@ -39,272 +88,311 @@ function isContentSubsumed(shorter: string, longer: string): boolean {
|
|
|
39
88
|
return aWords.size > 0 && overlap / aWords.size >= 0.8
|
|
40
89
|
}
|
|
41
90
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const memories = memoryRows.map((row) => ({ ...row, id: toMemoryId(row.id) }))
|
|
62
|
-
if (memories.length < 2) return 0
|
|
63
|
-
|
|
64
|
-
const archived = new Set<string>()
|
|
65
|
-
let mergeCount = 0
|
|
66
|
-
|
|
67
|
-
for (const memory of memories) {
|
|
68
|
-
if (archived.has(memory.id)) continue
|
|
69
|
-
|
|
70
|
-
const candidateLimit = 20
|
|
71
|
-
const neighborStatements = await databaseService.queryAll<{
|
|
72
|
-
id: RecordIdInput
|
|
73
|
-
content: string
|
|
74
|
-
importance: number
|
|
75
|
-
similarity: number
|
|
76
|
-
updatedAt?: string | Date | number | null
|
|
77
|
-
createdAt: string | Date | number
|
|
78
|
-
}>(
|
|
79
|
-
new BoundQuery(
|
|
80
|
-
`LET $candidateRows = (
|
|
81
|
-
SELECT
|
|
82
|
-
id,
|
|
83
|
-
vector::distance::knn() AS distance
|
|
84
|
-
FROM ${MEMORY_TABLE}
|
|
85
|
-
WHERE scopeId = $scopeId
|
|
86
|
-
AND archivedAt IS NONE
|
|
87
|
-
AND id != $memoryId
|
|
88
|
-
AND embedding <|${candidateLimit}|> $embedding
|
|
89
|
-
ORDER BY distance ASC
|
|
90
|
-
LIMIT ${candidateLimit}
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
SELECT
|
|
94
|
-
id,
|
|
95
|
-
content,
|
|
96
|
-
importance,
|
|
97
|
-
updatedAt,
|
|
98
|
-
createdAt,
|
|
99
|
-
vector::similarity::cosine(embedding, $embedding) AS similarity
|
|
100
|
-
FROM ${MEMORY_TABLE}
|
|
101
|
-
WHERE id IN $candidateRows.id
|
|
102
|
-
ORDER BY similarity DESC
|
|
103
|
-
LIMIT ${candidateLimit}`,
|
|
104
|
-
{ scopeId, memoryId: ensureRecordId(memory.id, TABLES.MEMORY), embedding: memory.embedding },
|
|
91
|
+
function deduplicateScopeEffect(scopeId: string) {
|
|
92
|
+
return Effect.gen(function* () {
|
|
93
|
+
const memoryRows = yield* tryMemoryConsolidationEffect('read-memories', () =>
|
|
94
|
+
db().query<{
|
|
95
|
+
id: RecordIdInput
|
|
96
|
+
content: string
|
|
97
|
+
importance: number
|
|
98
|
+
embedding: number[]
|
|
99
|
+
updatedAt?: string | Date | number | null
|
|
100
|
+
createdAt: string | Date | number
|
|
101
|
+
}>(
|
|
102
|
+
new BoundQuery(
|
|
103
|
+
`SELECT id, content, importance, embedding, updatedAt, createdAt
|
|
104
|
+
FROM ${MEMORY_TABLE}
|
|
105
|
+
WHERE scopeId = $scopeId AND archivedAt IS NONE
|
|
106
|
+
ORDER BY createdAt DESC
|
|
107
|
+
LIMIT $limit`,
|
|
108
|
+
{ scopeId, limit: MAX_MEMORIES_PER_SCOPE },
|
|
109
|
+
),
|
|
105
110
|
),
|
|
106
111
|
)
|
|
107
112
|
|
|
108
|
-
const
|
|
113
|
+
const memories = memoryRows.map((row) => ({ ...row, id: toMemoryId(row.id) }))
|
|
114
|
+
if (memories.length < 2) return 0
|
|
115
|
+
|
|
116
|
+
const archived = new Set<string>()
|
|
117
|
+
let mergeCount = 0
|
|
118
|
+
|
|
119
|
+
for (const memory of memories) {
|
|
120
|
+
if (archived.has(memory.id)) continue
|
|
121
|
+
|
|
122
|
+
const candidateLimit = 20
|
|
123
|
+
const neighborStatements = yield* tryMemoryConsolidationEffect('read-neighbors', () =>
|
|
124
|
+
db().queryAll<{
|
|
125
|
+
id: RecordIdInput
|
|
126
|
+
content: string
|
|
127
|
+
importance: number
|
|
128
|
+
similarity: number
|
|
129
|
+
updatedAt?: string | Date | number | null
|
|
130
|
+
createdAt: string | Date | number
|
|
131
|
+
}>(
|
|
132
|
+
new BoundQuery(
|
|
133
|
+
`LET $candidateRows = (
|
|
134
|
+
SELECT
|
|
135
|
+
id,
|
|
136
|
+
vector::distance::knn() AS distance
|
|
137
|
+
FROM ${MEMORY_TABLE}
|
|
138
|
+
WHERE scopeId = $scopeId
|
|
139
|
+
AND archivedAt IS NONE
|
|
140
|
+
AND id != $memoryId
|
|
141
|
+
AND embedding <|${candidateLimit}|> $embedding
|
|
142
|
+
ORDER BY distance ASC
|
|
143
|
+
LIMIT ${candidateLimit}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
SELECT
|
|
147
|
+
id,
|
|
148
|
+
content,
|
|
149
|
+
importance,
|
|
150
|
+
updatedAt,
|
|
151
|
+
createdAt,
|
|
152
|
+
vector::similarity::cosine(embedding, $embedding) AS similarity
|
|
153
|
+
FROM ${MEMORY_TABLE}
|
|
154
|
+
WHERE id IN $candidateRows.id
|
|
155
|
+
ORDER BY similarity DESC
|
|
156
|
+
LIMIT ${candidateLimit}`,
|
|
157
|
+
{ scopeId, memoryId: ensureRecordId(memory.id, TABLES.MEMORY), embedding: memory.embedding },
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
)
|
|
109
161
|
|
|
110
|
-
|
|
111
|
-
if (neighbor.similarity < SOFT_SIMILARITY_THRESHOLD) break
|
|
112
|
-
if (archived.has(neighbor.id)) continue
|
|
162
|
+
const neighbors = (neighborStatements.at(-1) ?? []).map((row) => ({ ...row, id: toMemoryId(row.id) }))
|
|
113
163
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
memory.content.length <= neighbor.content.length
|
|
118
|
-
? [memory.content, neighbor.content]
|
|
119
|
-
: [neighbor.content, memory.content]
|
|
120
|
-
if (!isContentSubsumed(shorter, longer)) continue
|
|
121
|
-
}
|
|
164
|
+
for (const neighbor of neighbors) {
|
|
165
|
+
if (neighbor.similarity < SOFT_SIMILARITY_THRESHOLD) break
|
|
166
|
+
if (archived.has(neighbor.id)) continue
|
|
122
167
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
await databaseService.relate(
|
|
132
|
-
ensureRecordId(winner.id, TABLES.MEMORY),
|
|
133
|
-
MEMORY_RELATION_TABLE,
|
|
134
|
-
ensureRecordId(loser.id, TABLES.MEMORY),
|
|
135
|
-
{ relationType: RELATION_SUPERSEDES, confidence: 1.0 },
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
await databaseService.query(
|
|
139
|
-
new BoundQuery(
|
|
140
|
-
`UPDATE ${MEMORY_TABLE}
|
|
141
|
-
SET archivedAt = time::now(), validUntil = time::now()
|
|
142
|
-
WHERE id = $loserId AND archivedAt IS NONE`,
|
|
143
|
-
{ loserId: ensureRecordId(loser.id, TABLES.MEMORY) },
|
|
144
|
-
),
|
|
145
|
-
)
|
|
168
|
+
const isHardMatch = neighbor.similarity >= HARD_SIMILARITY_THRESHOLD
|
|
169
|
+
if (!isHardMatch) {
|
|
170
|
+
const [shorter, longer] =
|
|
171
|
+
memory.content.length <= neighbor.content.length
|
|
172
|
+
? [memory.content, neighbor.content]
|
|
173
|
+
: [neighbor.content, memory.content]
|
|
174
|
+
if (!isContentSubsumed(shorter, longer)) continue
|
|
175
|
+
}
|
|
146
176
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
177
|
+
const memoryCreatedAt = unsafeDateFrom(memory.createdAt).getTime()
|
|
178
|
+
const neighborCreatedAt = unsafeDateFrom(neighbor.createdAt).getTime()
|
|
179
|
+
const keepMemory =
|
|
180
|
+
memory.importance > neighbor.importance ||
|
|
181
|
+
(memory.importance === neighbor.importance && memoryCreatedAt >= neighborCreatedAt)
|
|
182
|
+
const winner = keepMemory ? memory : neighbor
|
|
183
|
+
const loser = keepMemory ? neighbor : memory
|
|
184
|
+
|
|
185
|
+
yield* tryMemoryConsolidationEffect('archive-memory', () =>
|
|
186
|
+
db().query(
|
|
187
|
+
new BoundQuery(
|
|
188
|
+
`UPDATE ${MEMORY_TABLE}
|
|
189
|
+
SET archivedAt = time::now(), validUntil = time::now()
|
|
190
|
+
WHERE id = $loserId AND archivedAt IS NONE`,
|
|
191
|
+
{ loserId: ensureRecordId(loser.id, TABLES.MEMORY) },
|
|
192
|
+
),
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
yield* tryMemoryConsolidationEffect('relate-memories', () =>
|
|
196
|
+
db().relate(
|
|
197
|
+
ensureRecordId(winner.id, TABLES.MEMORY),
|
|
198
|
+
MEMORY_RELATION_TABLE,
|
|
199
|
+
ensureRecordId(loser.id, TABLES.MEMORY),
|
|
200
|
+
{ relationType: RELATION_SUPERSEDES, confidence: 1.0 },
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
yield* tryMemoryConsolidationEffect('write-history', () =>
|
|
204
|
+
db().insert<Record<string, unknown>>(MEMORY_HISTORY_TABLE, {
|
|
205
|
+
memoryId: ensureRecordId(loser.id, TABLES.MEMORY),
|
|
206
|
+
prevValue: loser.content,
|
|
207
|
+
event: 'DELETE',
|
|
208
|
+
}),
|
|
209
|
+
)
|
|
152
210
|
|
|
153
|
-
|
|
154
|
-
|
|
211
|
+
archived.add(loser.id)
|
|
212
|
+
mergeCount++
|
|
155
213
|
|
|
156
|
-
|
|
214
|
+
if (!keepMemory) continue
|
|
215
|
+
}
|
|
157
216
|
}
|
|
158
|
-
}
|
|
159
217
|
|
|
160
|
-
|
|
218
|
+
return mergeCount
|
|
219
|
+
})
|
|
161
220
|
}
|
|
162
221
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
222
|
+
function pruneStaleMemoriesEffect() {
|
|
223
|
+
return Effect.gen(function* () {
|
|
224
|
+
const stale = yield* tryMemoryConsolidationEffect('load-stale', () =>
|
|
225
|
+
db().query<{ id: RecordIdInput }>(
|
|
226
|
+
new BoundQuery(
|
|
227
|
+
`SELECT id FROM ${MEMORY_TABLE}
|
|
228
|
+
WHERE accessCount = 0
|
|
229
|
+
AND createdAt < time::now() - 90d
|
|
230
|
+
AND archivedAt IS NONE
|
|
231
|
+
AND importance < 0.5
|
|
232
|
+
LIMIT 200`,
|
|
233
|
+
),
|
|
234
|
+
),
|
|
235
|
+
)
|
|
174
236
|
|
|
175
|
-
|
|
237
|
+
if (stale.length === 0) return 0
|
|
176
238
|
|
|
177
|
-
|
|
178
|
-
|
|
239
|
+
const staleIds = stale.map((row) => ensureRecordId(row.id, TABLES.MEMORY))
|
|
240
|
+
yield* tryMemoryConsolidationEffect('update-stale', () =>
|
|
241
|
+
db().updateWhere(MEMORY_TABLE, inside('id', staleIds), { archivedAt: nowDate() }),
|
|
242
|
+
)
|
|
179
243
|
|
|
180
|
-
|
|
244
|
+
return stale.length
|
|
245
|
+
})
|
|
181
246
|
}
|
|
182
247
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
)
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
let collapsed = 0
|
|
203
|
-
|
|
204
|
-
for (const node of middleNodes) {
|
|
205
|
-
const predecessors = node.predecessors
|
|
206
|
-
const successors = node.successors
|
|
207
|
-
|
|
208
|
-
for (const predId of predecessors) {
|
|
209
|
-
for (const succId of successors) {
|
|
210
|
-
const predRef = ensureRecordId(predId, TABLES.MEMORY)
|
|
211
|
-
const succRef = ensureRecordId(succId, TABLES.MEMORY)
|
|
212
|
-
const existing = await databaseService.query<{ id: RecordIdInput }>(
|
|
213
|
-
new BoundQuery(
|
|
214
|
-
`SELECT id FROM ${MEMORY_RELATION_TABLE}
|
|
215
|
-
WHERE in = $predId AND out = $succId AND relationType = '${RELATION_SUPERSEDES}'
|
|
216
|
-
LIMIT 1`,
|
|
217
|
-
{ predId: predRef, succId: succRef },
|
|
218
|
-
),
|
|
219
|
-
)
|
|
248
|
+
function collapseSupersededChainEffect() {
|
|
249
|
+
return Effect.gen(function* () {
|
|
250
|
+
const middleNodes = yield* tryMemoryConsolidationEffect('load-middle-nodes', () =>
|
|
251
|
+
db().query<{ middleId: RecordIdInput; predecessors: RecordIdInput[]; successors: RecordIdInput[] }>(
|
|
252
|
+
new BoundQuery(
|
|
253
|
+
`SELECT
|
|
254
|
+
id AS middleId,
|
|
255
|
+
<-${MEMORY_RELATION_TABLE}[WHERE relationType = '${RELATION_SUPERSEDES}']<-${MEMORY_TABLE}.id AS predecessors,
|
|
256
|
+
->${MEMORY_RELATION_TABLE}[WHERE relationType = '${RELATION_SUPERSEDES}']->${MEMORY_TABLE}.id AS successors
|
|
257
|
+
FROM ${MEMORY_TABLE}
|
|
258
|
+
WHERE archivedAt IS NONE
|
|
259
|
+
AND count(->${MEMORY_RELATION_TABLE}[WHERE relationType = '${RELATION_SUPERSEDES}']) > 0
|
|
260
|
+
AND count(<-${MEMORY_RELATION_TABLE}[WHERE relationType = '${RELATION_SUPERSEDES}']) > 0
|
|
261
|
+
LIMIT ${MEMORY.MAX_KNN_LIMIT}`,
|
|
262
|
+
),
|
|
263
|
+
),
|
|
264
|
+
)
|
|
220
265
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
266
|
+
let collapsed = 0
|
|
267
|
+
|
|
268
|
+
for (const node of middleNodes) {
|
|
269
|
+
for (const predId of node.predecessors) {
|
|
270
|
+
for (const succId of node.successors) {
|
|
271
|
+
const predRef = ensureRecordId(predId, TABLES.MEMORY)
|
|
272
|
+
const succRef = ensureRecordId(succId, TABLES.MEMORY)
|
|
273
|
+
const existing = yield* tryMemoryConsolidationEffect('read-existing-relation', () =>
|
|
274
|
+
db().query<{ id: RecordIdInput }>(
|
|
275
|
+
new BoundQuery(
|
|
276
|
+
`SELECT id FROM ${MEMORY_RELATION_TABLE}
|
|
277
|
+
WHERE in = $predId AND out = $succId AND relationType = '${RELATION_SUPERSEDES}'
|
|
278
|
+
LIMIT 1`,
|
|
279
|
+
{ predId: predRef, succId: succRef },
|
|
280
|
+
),
|
|
281
|
+
),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
if (existing.length === 0) {
|
|
285
|
+
yield* tryMemoryConsolidationEffect('write-relation', () =>
|
|
286
|
+
db().relate(predRef, MEMORY_RELATION_TABLE, succRef, {
|
|
287
|
+
relationType: RELATION_SUPERSEDES,
|
|
288
|
+
confidence: 1.0,
|
|
289
|
+
}),
|
|
290
|
+
)
|
|
291
|
+
}
|
|
226
292
|
}
|
|
227
293
|
}
|
|
228
|
-
}
|
|
229
294
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
295
|
+
yield* tryMemoryConsolidationEffect('update-middle-node', () =>
|
|
296
|
+
db().updateWhere(MEMORY_TABLE, eq('id', ensureRecordId(node.middleId, TABLES.MEMORY)), {
|
|
297
|
+
archivedAt: nowDate(),
|
|
298
|
+
}),
|
|
299
|
+
)
|
|
233
300
|
|
|
234
|
-
|
|
235
|
-
|
|
301
|
+
collapsed++
|
|
302
|
+
}
|
|
236
303
|
|
|
237
|
-
|
|
304
|
+
return collapsed
|
|
305
|
+
})
|
|
238
306
|
}
|
|
239
307
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
308
|
+
function decayImportanceEffect() {
|
|
309
|
+
return Effect.gen(function* () {
|
|
310
|
+
const [standardResult, ephemeralResult] = yield* Effect.all([
|
|
311
|
+
tryMemoryConsolidationEffect('decay-standard', () =>
|
|
312
|
+
db().query<{ count: number }>(
|
|
313
|
+
new BoundQuery(
|
|
314
|
+
`UPDATE ${MEMORY_TABLE}
|
|
315
|
+
SET importance = math::max([0.1, importance * 0.95])
|
|
316
|
+
WHERE lastAccessedAt IS NOT NONE
|
|
317
|
+
AND lastAccessedAt < time::now() - 30d
|
|
318
|
+
AND archivedAt IS NONE
|
|
319
|
+
AND importance > 0.1
|
|
320
|
+
AND (durability = 'standard' OR durability IS NONE)
|
|
321
|
+
RETURN count() AS count`,
|
|
322
|
+
),
|
|
323
|
+
),
|
|
324
|
+
),
|
|
325
|
+
tryMemoryConsolidationEffect('decay-ephemeral', () =>
|
|
326
|
+
db().query<{ count: number }>(
|
|
327
|
+
new BoundQuery(
|
|
328
|
+
`UPDATE ${MEMORY_TABLE}
|
|
329
|
+
SET importance = math::max([0.1, importance * 0.85])
|
|
330
|
+
WHERE lastAccessedAt IS NOT NONE
|
|
331
|
+
AND lastAccessedAt < time::now() - 30d
|
|
332
|
+
AND archivedAt IS NONE
|
|
333
|
+
AND importance > 0.1
|
|
334
|
+
AND durability = 'ephemeral'
|
|
335
|
+
RETURN count() AS count`,
|
|
336
|
+
),
|
|
337
|
+
),
|
|
338
|
+
),
|
|
339
|
+
])
|
|
266
340
|
|
|
267
|
-
|
|
341
|
+
return (standardResult[0]?.count ?? 0) + (ephemeralResult[0]?.count ?? 0)
|
|
342
|
+
})
|
|
268
343
|
}
|
|
269
344
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
345
|
+
function cleanupOrphanedRelationsEffect() {
|
|
346
|
+
return Effect.gen(function* () {
|
|
347
|
+
const result = yield* tryMemoryConsolidationEffect('cleanup-relations', () =>
|
|
348
|
+
db().query<{ count: number }>(
|
|
349
|
+
new BoundQuery(
|
|
350
|
+
`DELETE ${MEMORY_RELATION_TABLE}
|
|
351
|
+
WHERE in.archivedAt IS NOT NONE OR out.archivedAt IS NOT NONE
|
|
352
|
+
RETURN count() AS count`,
|
|
353
|
+
),
|
|
354
|
+
),
|
|
355
|
+
)
|
|
356
|
+
return result[0]?.count ?? 0
|
|
357
|
+
})
|
|
279
358
|
}
|
|
280
359
|
|
|
281
|
-
const handler =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const pruned = await pruneStaleMemories()
|
|
298
|
-
const collapsed = await collapseSupersedeCh()
|
|
299
|
-
const decayed = await decayImportance()
|
|
300
|
-
const orphaned = await cleanupOrphanedRelations()
|
|
360
|
+
const handler = (job: SandboxedJob<MemoryConsolidationJob>) =>
|
|
361
|
+
Effect.runPromise(
|
|
362
|
+
Effect.gen(function* () {
|
|
363
|
+
const targetScope = job.data.scopeId
|
|
364
|
+
|
|
365
|
+
let totalMerged = 0
|
|
366
|
+
if (targetScope) {
|
|
367
|
+
totalMerged = yield* deduplicateScopeEffect(targetScope)
|
|
368
|
+
} else {
|
|
369
|
+
const scopeIds = yield* tryMemoryConsolidationEffect('load-scope-ids', () =>
|
|
370
|
+
db().query<string>(
|
|
371
|
+
new BoundQuery(`SELECT VALUE scopeId FROM ${MEMORY_TABLE} WHERE archivedAt IS NONE GROUP BY scopeId`),
|
|
372
|
+
),
|
|
373
|
+
)
|
|
301
374
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
375
|
+
const results = yield* Effect.forEach(scopeIds, deduplicateScopeEffect, { concurrency: 2 })
|
|
376
|
+
totalMerged = results.reduce((a, b) => a + b, 0)
|
|
377
|
+
}
|
|
378
|
+
const [pruned, collapsed, decayed, orphaned] = yield* Effect.all([
|
|
379
|
+
pruneStaleMemoriesEffect(),
|
|
380
|
+
collapseSupersededChainEffect(),
|
|
381
|
+
decayImportanceEffect(),
|
|
382
|
+
cleanupOrphanedRelationsEffect(),
|
|
383
|
+
])
|
|
384
|
+
|
|
385
|
+
yield* Effect.sync(() => {
|
|
386
|
+
serverLogger.info`Memory consolidation complete (merged: ${totalMerged}, pruned: ${pruned}, collapsed: ${collapsed}, decayed: ${decayed}, orphaned relations: ${orphaned})`
|
|
387
|
+
})
|
|
388
|
+
}).pipe(
|
|
389
|
+
Effect.catch((error) => {
|
|
390
|
+
const serialized = toSandboxedWorkerError(error, 'Memory consolidation job failed')
|
|
391
|
+
return Effect.sync(() => {
|
|
392
|
+
serverLogger.error`${serialized.message}`
|
|
393
|
+
}).pipe(Effect.andThen(Effect.fail(serialized)))
|
|
394
|
+
}),
|
|
395
|
+
),
|
|
396
|
+
)
|
|
309
397
|
|
|
310
398
|
export default createTracedWorkerProcessor('memory-consolidation', handler)
|