@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.
Files changed (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -0,0 +1,674 @@
1
+ import { Context, Schema, Effect, Layer } from 'effect'
2
+
3
+ import { aiLogger } from '../../config/logger'
4
+ import type { Memory } from '../../db/memory'
5
+ import { isUniqueIndexConflict } from '../../db/memory-store.helpers'
6
+ import type {
7
+ AddOptions,
8
+ ExtractedFact,
9
+ MemoryListScalar,
10
+ MemoryRecord,
11
+ MemorySearchResult,
12
+ MemoryType,
13
+ RelationType,
14
+ } from '../../db/memory-types'
15
+ import { DatabaseServiceTag, RuntimeConfigServiceTag } from '../../effect/services'
16
+ import { withOrgMemoryLockEffect } from '../../redis/org-memory-lock'
17
+ import type { HelperModelRuntime } from '../../runtime/helper-model'
18
+ import { HelperModelTag } from '../../runtime/helper-model'
19
+ import { ORG_SCOPE_PREFIX, agentScopeId, scopeId } from '../../runtime/memory/memory-scope'
20
+ import {
21
+ countScopedRetrievalCandidates,
22
+ executeScopedRetrieval,
23
+ scopedRetrievalToMap,
24
+ } from '../../runtime/retrieval-adapters'
25
+ import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
26
+ import { clampImportance, compactWhitespace } from '../../utils/string'
27
+ import { BackgroundWorkService } from '../background-work.service'
28
+ import type { MemoryServiceError } from './memory-errors'
29
+ import { tryMemoryPromise as memoryTryPromise } from './memory-errors'
30
+ import type { MemoryRerankOutput } from './memory-rerank'
31
+ import { formatMemoryResults, formatRerankedResults, getCandidateLimit } from './memory-utils'
32
+ export type { MemoryRerankOutput } from './memory-rerank'
33
+ import {
34
+ buildConversationMessages,
35
+ isRoutableAgentName,
36
+ resolveAgentScopeNames,
37
+ shouldSkipExtractedFacts,
38
+ } from './memory-conversation'
39
+ import { getOrgMemory, makeOrgMemoryCache } from './memory-org-memory'
40
+ import type { OrgMemoryCache } from './memory-org-memory'
41
+ import { getTopMemoriesSection } from './memory-preseeded'
42
+ import { rerankCandidates, rerankCandidatesMultiScope } from './memory-rerank'
43
+ import type { makeRerankService } from './rerank.service'
44
+ import { RerankServiceTag } from './rerank.service'
45
+
46
+ const ORG_MEMORY_TYPE = 'fact'
47
+ const MAX_MEMORY_RESULTS_PER_SCOPE = 10
48
+ const ONBOARDING_MEMORY_MAX_FACTS = 16
49
+ const ONBOARDING_MEMORY_EXTRACTION_PROMPT =
50
+ 'Onboarding mode is active. Extract multiple concrete startup facts from user-provided context: company mission, product capabilities, customer segments, pricing, traction, go-to-market plans, roadmap, team composition, technical stack, risks, and referenced URLs. Prefer one fact per concrete claim.'
51
+ const DIRECT_MEMORY_ASSESSMENT_PROMPT =
52
+ 'The user is submitting a direct memory candidate. Keep the wording faithful. Return one fact only when the statement is durable enough for memory; otherwise return no facts.'
53
+
54
+ class InvalidAgentNameError extends Schema.TaggedErrorClass<InvalidAgentNameError>()('InvalidAgentNameError', {
55
+ agentName: Schema.String,
56
+ }) {}
57
+
58
+ type EffectSuccess<T> = T extends Effect.Effect<infer A, infer _E, infer _R> ? A : Awaited<T>
59
+
60
+ function searchMemoriesEffect({
61
+ runtimeConfig,
62
+ helperModelRuntime,
63
+ resolveReranker,
64
+ query,
65
+ memory,
66
+ scopeId,
67
+ memoryType,
68
+ fastMode = true,
69
+ }: {
70
+ runtimeConfig: ResolvedLotaRuntimeConfig
71
+ helperModelRuntime: HelperModelRuntime
72
+ resolveReranker: () => ReturnType<typeof makeRerankService>
73
+ query: string
74
+ memory: Memory
75
+ scopeId: string
76
+ memoryType: MemoryType
77
+ fastMode?: boolean
78
+ }): Effect.Effect<string, MemoryServiceError> {
79
+ return Effect.gen(function* () {
80
+ const limit = runtimeConfig.memory.searchK
81
+ const candidateLimit = fastMode ? limit : getCandidateLimit(limit)
82
+ const candidates = yield* memoryTryPromise('Failed to search memory candidates.', () =>
83
+ memory.searchCandidates(query, {
84
+ scopeId,
85
+ limit: candidateLimit,
86
+ memoryType,
87
+ fastMode,
88
+ includeNeighborContext: !fastMode,
89
+ }),
90
+ )
91
+
92
+ aiLogger.debug`Memory search candidates (scopeId: ${scopeId}, candidates: ${candidates.length})`
93
+
94
+ if (candidates.length === 0) {
95
+ return 'No stored memories.'
96
+ }
97
+
98
+ if (fastMode || candidates.length <= limit) {
99
+ aiLogger.debug`Skipping reranking (candidates: ${candidates.length} <= limit: ${limit})`
100
+ return formatMemoryResults(candidates.slice(0, limit))
101
+ }
102
+
103
+ const reranked = yield* rerankCandidates(
104
+ runtimeConfig,
105
+ helperModelRuntime,
106
+ query,
107
+ candidates,
108
+ limit,
109
+ resolveReranker(),
110
+ )
111
+ return formatRerankedResults(reranked, candidates, limit)
112
+ })
113
+ }
114
+
115
+ function applyPreparedScopeUpdatesEffect(params: {
116
+ orgId: string
117
+ orgMemory: Memory
118
+ preparedUpdates: EffectSuccess<ReturnType<Memory['prepareFactsToScopes']>>
119
+ acquireLock?: boolean
120
+ }) {
121
+ return Effect.gen(function* () {
122
+ if (params.preparedUpdates.length === 0) {
123
+ return
124
+ }
125
+
126
+ if (params.acquireLock === false) {
127
+ yield* memoryTryPromise('Failed to apply prepared memory scope updates.', () =>
128
+ params.orgMemory.applyPreparedScopeUpdates(params.preparedUpdates),
129
+ )
130
+ return
131
+ }
132
+
133
+ yield* withOrgMemoryLockEffect(params.orgId, () =>
134
+ memoryTryPromise('Failed to apply prepared memory scope updates.', () =>
135
+ params.orgMemory.applyPreparedScopeUpdates(params.preparedUpdates),
136
+ ),
137
+ )
138
+ })
139
+ }
140
+
141
+ function formatBatchedResults(
142
+ reranked: MemoryRerankOutput | null,
143
+ candidatesByScopeTag: Map<string, MemorySearchResult[]>,
144
+ limit: number,
145
+ agentName?: string,
146
+ ): string {
147
+ if (reranked && reranked.sections.length > 0) {
148
+ const allCandidates = Array.from(candidatesByScopeTag.values()).flat()
149
+ return formatRerankedResults(reranked, allCandidates, limit)
150
+ }
151
+
152
+ const sections: string[] = []
153
+
154
+ if (agentName) {
155
+ const agentCandidates = candidatesByScopeTag.get(`agent:${agentName}`) ?? []
156
+ if (agentCandidates.length > 0) {
157
+ sections.push(`Agent memory (${agentName}):\n${formatMemoryResults(agentCandidates.slice(0, limit))}`)
158
+ } else {
159
+ sections.push(`Agent memory (${agentName}):\nNo stored memories.`)
160
+ }
161
+ }
162
+
163
+ const orgCandidates = candidatesByScopeTag.get('org') ?? []
164
+ if (orgCandidates.length > 0) {
165
+ sections.push(
166
+ `${agentName ? 'Global org memory' : 'Organization memory'}:\n${formatMemoryResults(orgCandidates.slice(0, limit))}`,
167
+ )
168
+ } else {
169
+ sections.push(`${agentName ? 'Global org memory' : 'Organization memory'}:\nNo stored memories.`)
170
+ }
171
+
172
+ return sections.join('\n\n')
173
+ }
174
+
175
+ interface MemoryServiceDeps {
176
+ runtimeConfig: ResolvedLotaRuntimeConfig
177
+ rerankService: ReturnType<typeof makeRerankService>
178
+ helperModelRuntime: HelperModelRuntime
179
+ orgMemoryCache: OrgMemoryCache
180
+ }
181
+
182
+ export function createMemoryService(deps: MemoryServiceDeps) {
183
+ const { runtimeConfig, helperModelRuntime, orgMemoryCache } = deps
184
+ const resolveRerankService = () => deps.rerankService
185
+ const service = {
186
+ searchOrganizationMemories: Effect.fn('MemoryService.searchOrganizationMemories')(function* (
187
+ orgId: string,
188
+ query: string,
189
+ ) {
190
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
191
+ aiLogger.debug`Organization memory search requested (orgId: ${orgId}, scopeId: ${orgScopeId}, queryLength: ${query.length})`
192
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
193
+ const results = yield* searchMemoriesEffect({
194
+ runtimeConfig,
195
+ helperModelRuntime,
196
+ resolveReranker: resolveRerankService,
197
+ query,
198
+ memory,
199
+ scopeId: orgScopeId,
200
+ memoryType: ORG_MEMORY_TYPE,
201
+ })
202
+ aiLogger.debug`Organization memory search completed (resultLength: ${results.length}, preview: ${results.slice(0, 100)})`
203
+ return results
204
+ }),
205
+
206
+ getStaleMemories(orgId: string) {
207
+ return Effect.gen(function* () {
208
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
209
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
210
+ return yield* memoryTryPromise('Failed to load stale memories.', () => memory.getStaleMemories(orgScopeId))
211
+ }).pipe(
212
+ Effect.withSpan('MemoryService.getStaleMemories'),
213
+ Effect.map((stale) => {
214
+ if (stale.length === 0) return ''
215
+ const items = stale.map((m) => `- [NEEDS REVIEW] ${m.content}`).join('\n')
216
+ return `Memories flagged for review (parent fact was superseded):\n${items}`
217
+ }),
218
+ Effect.catch((error) => {
219
+ aiLogger.error`Failed to get stale memories: ${error.cause}`
220
+ return Effect.succeed('')
221
+ }),
222
+ )
223
+ },
224
+
225
+ searchOrganizationMemoriesRaw: Effect.fn('MemoryService.searchOrganizationMemoriesRaw')(function* (
226
+ orgId: string,
227
+ query: string,
228
+ options?: { fastMode?: boolean; limit?: number },
229
+ ) {
230
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
231
+ aiLogger.debug`searchOrganizationMemoriesRaw - orgId: "${orgId}", scopeId: "${orgScopeId}"`
232
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
233
+ const fastMode = options?.fastMode ?? true
234
+ const searchK = runtimeConfig.memory.searchK
235
+ const limit = options?.limit ?? (fastMode ? Math.min(searchK, 4) : searchK)
236
+ const candidates = yield* memoryTryPromise('Failed to search organization memories.', () =>
237
+ memory.searchCandidates(query, {
238
+ scopeId: orgScopeId,
239
+ limit,
240
+ memoryType: ORG_MEMORY_TYPE,
241
+ fastMode,
242
+ includeNeighborContext: !fastMode,
243
+ }),
244
+ )
245
+ aiLogger.debug`Organization memory search (raw) completed (candidates: ${candidates.length})`
246
+ return formatMemoryResults(candidates)
247
+ }),
248
+
249
+ searchAgentMemories: Effect.fn('MemoryService.searchAgentMemories')(function* (
250
+ orgId: string,
251
+ agentName: string,
252
+ query: string,
253
+ ) {
254
+ if (!isRoutableAgentName(agentName)) {
255
+ aiLogger.debug`Agent memory search skipped - invalid agentName: ${agentName}`
256
+ return 'No stored memories.'
257
+ }
258
+
259
+ const scoped = yield* agentScopeId(orgId, agentName)
260
+ aiLogger.debug`Agent memory search requested (orgId: ${orgId}, agentName: ${agentName}, scopeId: ${scoped}, queryLength: ${query.length})`
261
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
262
+ const results = yield* searchMemoriesEffect({
263
+ runtimeConfig,
264
+ helperModelRuntime,
265
+ resolveReranker: resolveRerankService,
266
+ query,
267
+ memory,
268
+ scopeId: scoped,
269
+ memoryType: ORG_MEMORY_TYPE,
270
+ })
271
+ aiLogger.debug`Agent memory search completed (agentName: ${agentName}, resultLength: ${results.length}, preview: ${results.slice(0, 100)})`
272
+ return results
273
+ }),
274
+
275
+ listOrganizationMemoryRecords: Effect.fn('MemoryService.listOrganizationMemoryRecords')(function* (params: {
276
+ orgId: string
277
+ limit?: number
278
+ memoryType?: MemoryType
279
+ metadataEquals?: Record<string, MemoryListScalar>
280
+ metadataNotEquals?: Record<string, MemoryListScalar>
281
+ sort?: 'createdAtAsc' | 'createdAtDesc'
282
+ }) {
283
+ const { orgId, ...listOptions } = params
284
+ const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
285
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
286
+ return yield* memoryTryPromise('Failed to list organization memory records.', () =>
287
+ orgMemory.list({ scopeId: orgScopeId, ...listOptions }),
288
+ )
289
+ }),
290
+
291
+ getTopMemories(params: { orgId: string; agentName?: string; limit?: number }) {
292
+ return getTopMemoriesSection({
293
+ orgMemoryCache,
294
+ orgId: params.orgId,
295
+ agentName: params.agentName,
296
+ limit: params.limit,
297
+ })
298
+ },
299
+
300
+ searchAllMemoriesBatched: Effect.fn('MemoryService.searchAllMemoriesBatched')(function* ({
301
+ orgId,
302
+ agentName,
303
+ query,
304
+ fastMode = true,
305
+ allowMultiScopeRerank = true,
306
+ }: {
307
+ orgId: string
308
+ agentName?: string
309
+ query: string
310
+ fastMode?: boolean
311
+ allowMultiScopeRerank?: boolean
312
+ }) {
313
+ const limit = Math.min(runtimeConfig.memory.searchK, MAX_MEMORY_RESULTS_PER_SCOPE)
314
+ const candidateLimit = fastMode ? limit : getCandidateLimit(limit)
315
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
316
+ const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
317
+
318
+ const retrievalTasks = [
319
+ {
320
+ scopeTag: 'org',
321
+ retrieve: () =>
322
+ orgMemory.searchCandidates(query, {
323
+ scopeId: orgScopeId,
324
+ limit: candidateLimit,
325
+ memoryType: ORG_MEMORY_TYPE,
326
+ fastMode,
327
+ includeNeighborContext: !fastMode,
328
+ }),
329
+ },
330
+ ]
331
+
332
+ if (isRoutableAgentName(agentName)) {
333
+ const agentScoped = yield* agentScopeId(orgId, agentName)
334
+ retrievalTasks.push({
335
+ scopeTag: `agent:${agentName}`,
336
+ retrieve: () =>
337
+ orgMemory.searchCandidates(query, {
338
+ scopeId: agentScoped,
339
+ limit: candidateLimit,
340
+ memoryType: ORG_MEMORY_TYPE,
341
+ fastMode,
342
+ includeNeighborContext: !fastMode,
343
+ }),
344
+ })
345
+ }
346
+
347
+ const results = yield* memoryTryPromise('Failed to execute scoped memory retrieval.', () =>
348
+ executeScopedRetrieval(retrievalTasks),
349
+ )
350
+ const totalCandidates = countScopedRetrievalCandidates(results)
351
+ aiLogger.debug`Batched memory search candidates (scopes: ${results.length}, total: ${totalCandidates})`
352
+
353
+ if (totalCandidates === 0) {
354
+ return 'No stored memories.'
355
+ }
356
+
357
+ const candidatesByScopeTag = scopedRetrievalToMap(results)
358
+ if (fastMode || !allowMultiScopeRerank) {
359
+ return formatBatchedResults(null, candidatesByScopeTag, limit, agentName)
360
+ }
361
+
362
+ const reranked = yield* rerankCandidatesMultiScope(
363
+ runtimeConfig,
364
+ helperModelRuntime,
365
+ query,
366
+ results,
367
+ limit,
368
+ resolveRerankService(),
369
+ )
370
+ return formatBatchedResults(reranked, candidatesByScopeTag, limit, agentName)
371
+ }),
372
+
373
+ createOrganizationMemory({
374
+ orgId,
375
+ content,
376
+ memoryType,
377
+ metadata,
378
+ importance,
379
+ durability,
380
+ }: {
381
+ orgId: string
382
+ content: string
383
+ memoryType: MemoryType
384
+ metadata?: Record<string, unknown>
385
+ importance?: number
386
+ durability?: MemoryRecord['durability']
387
+ }) {
388
+ return Effect.gen(function* () {
389
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
390
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
391
+ return yield* memoryTryPromise('Failed to create organization memory.', () =>
392
+ memory.insert(content, {
393
+ scopeId: orgScopeId,
394
+ memoryType,
395
+ importance: importance ?? 1,
396
+ durability,
397
+ metadata: { orgId, ...metadata },
398
+ }),
399
+ )
400
+ }).pipe(
401
+ Effect.withSpan('MemoryService.createOrganizationMemory'),
402
+ Effect.tap(() =>
403
+ Effect.gen(function* () {
404
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
405
+ aiLogger.debug`createOrganizationMemory - orgId: "${orgId}", scopeId: "${orgScopeId}", content preview: "${content.slice(0, 50)}"`
406
+ }),
407
+ ),
408
+ Effect.catch((error) => {
409
+ if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
410
+ aiLogger.debug`Organization memory already exists (hash conflict)`
411
+ return Effect.succeed('')
412
+ }
413
+ return Effect.fail(error)
414
+ }),
415
+ )
416
+ },
417
+
418
+ addOrganizationMemoryRelation: Effect.fn('MemoryService.addOrganizationMemoryRelation')(function* ({
419
+ orgId,
420
+ fromMemoryId,
421
+ toMemoryId,
422
+ relationType,
423
+ confidence,
424
+ }: {
425
+ orgId: string
426
+ fromMemoryId: string
427
+ toMemoryId: string
428
+ relationType: RelationType
429
+ confidence?: number
430
+ }) {
431
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
432
+ return yield* memoryTryPromise('Failed to add organization memory relation.', () =>
433
+ memory.addRelation(fromMemoryId, toMemoryId, relationType, confidence),
434
+ )
435
+ }),
436
+
437
+ createAgentMemory({
438
+ orgId,
439
+ agentName,
440
+ content,
441
+ memoryType,
442
+ metadata,
443
+ importance,
444
+ }: {
445
+ orgId: string
446
+ agentName: string
447
+ content: string
448
+ memoryType: MemoryType
449
+ metadata?: Record<string, unknown>
450
+ importance?: number
451
+ }) {
452
+ if (!isRoutableAgentName(agentName)) {
453
+ return Effect.fail(new InvalidAgentNameError({ agentName }))
454
+ }
455
+
456
+ return Effect.gen(function* () {
457
+ const scoped = yield* agentScopeId(orgId, agentName)
458
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
459
+ return yield* memoryTryPromise('Failed to create agent memory.', () =>
460
+ memory.insert(content, {
461
+ scopeId: scoped,
462
+ memoryType,
463
+ importance: importance ?? 1,
464
+ metadata: { orgId, agentName, memoryScope: 'agent', ...metadata },
465
+ }),
466
+ )
467
+ }).pipe(
468
+ Effect.withSpan('MemoryService.createAgentMemory'),
469
+ Effect.catch((error) => {
470
+ if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
471
+ aiLogger.debug`Agent memory already exists (hash conflict)`
472
+ return Effect.succeed('')
473
+ }
474
+ return Effect.fail(error)
475
+ }),
476
+ )
477
+ },
478
+
479
+ assessMemoryCandidate: Effect.fn('MemoryService.assessMemoryCandidate')(function* (params: {
480
+ orgId: string
481
+ content: string
482
+ }) {
483
+ const trimmed = compactWhitespace(params.content)
484
+ if (!trimmed) return null
485
+
486
+ const memory = yield* getOrgMemory(orgMemoryCache, params.orgId)
487
+ const facts = yield* memoryTryPromise('Failed to assess direct memory candidate.', () =>
488
+ memory.extractFactsFromMessages([{ role: 'user', content: trimmed }], {
489
+ maxFacts: 1,
490
+ customPrompt: DIRECT_MEMORY_ASSESSMENT_PROMPT,
491
+ }),
492
+ )
493
+ if (facts.length === 0) return null
494
+ const fact = facts[0]
495
+
496
+ return {
497
+ classification: fact.classification,
498
+ durability: fact.durability,
499
+ importance: clampImportance(fact.importance),
500
+ rationale: fact.rationale,
501
+ }
502
+ }),
503
+
504
+ updateOrganizationMemoryById: Effect.fn('MemoryService.updateOrganizationMemoryById')(function* ({
505
+ orgId,
506
+ memoryId,
507
+ content,
508
+ }: {
509
+ orgId: string
510
+ memoryId: string
511
+ content: string
512
+ }) {
513
+ const memory = yield* getOrgMemory(orgMemoryCache, orgId)
514
+ return yield* memoryTryPromise('Failed to update organization memory.', () =>
515
+ memory.updateMemory(memoryId, content),
516
+ )
517
+ }),
518
+
519
+ addExtractedFactsToScopes: Effect.fn('MemoryService.addExtractedFactsToScopes')(function* (params: {
520
+ orgId: string
521
+ facts: ExtractedFact[]
522
+ source: string
523
+ sourceMetadata?: Record<string, unknown>
524
+ agentNames?: string[]
525
+ acquireLock?: boolean
526
+ }) {
527
+ if (params.facts.length === 0) return
528
+
529
+ const orgMemory = yield* getOrgMemory(orgMemoryCache, params.orgId)
530
+ const orgScopeIdValue = yield* scopeId(ORG_SCOPE_PREFIX, params.orgId)
531
+ const scopes: AddOptions[] = [
532
+ {
533
+ scopeId: orgScopeIdValue,
534
+ memoryType: ORG_MEMORY_TYPE,
535
+ metadata: { orgId: params.orgId, source: params.source, ...params.sourceMetadata },
536
+ },
537
+ ]
538
+
539
+ for (const scopedAgentName of resolveAgentScopeNames(undefined, params.agentNames ?? [])) {
540
+ const agentScopedValue = yield* agentScopeId(params.orgId, scopedAgentName)
541
+ scopes.push({
542
+ scopeId: agentScopedValue,
543
+ memoryType: ORG_MEMORY_TYPE,
544
+ metadata: {
545
+ orgId: params.orgId,
546
+ source: params.source,
547
+ ...params.sourceMetadata,
548
+ agentName: scopedAgentName,
549
+ memoryScope: 'agent',
550
+ },
551
+ })
552
+ }
553
+
554
+ const preparedUpdates = yield* memoryTryPromise('Failed to prepare extracted facts for memory scopes.', () =>
555
+ orgMemory.prepareFactsToScopes(params.facts, scopes),
556
+ )
557
+ yield* applyPreparedScopeUpdatesEffect({
558
+ orgId: params.orgId,
559
+ orgMemory,
560
+ preparedUpdates,
561
+ acquireLock: params.acquireLock,
562
+ })
563
+ }),
564
+
565
+ addConversationMemories: Effect.fn('MemoryService.addConversationMemories')(function* ({
566
+ orgId,
567
+ input,
568
+ output,
569
+ sourceId,
570
+ source = 'chat',
571
+ sourceMetadata,
572
+ onboardStatus,
573
+ agentName,
574
+ historyMessages = [],
575
+ memoryBlock,
576
+ attachmentContext,
577
+ agentNames = [],
578
+ }: {
579
+ orgId: string
580
+ input: string
581
+ output: string
582
+ sourceId?: string
583
+ source?: string
584
+ sourceMetadata?: Record<string, unknown>
585
+ onboardStatus?: string
586
+ agentName?: string
587
+ historyMessages?: Array<{ role: 'user' | 'agent'; content: string; agentName?: string }>
588
+ memoryBlock?: string
589
+ attachmentContext?: string
590
+ agentNames?: string[]
591
+ }) {
592
+ const { messages, normalizedInput, sanitizedOutput } = buildConversationMessages({
593
+ input,
594
+ output,
595
+ historyMessages,
596
+ memoryBlock,
597
+ attachmentContext,
598
+ })
599
+
600
+ if (!normalizedInput || !sanitizedOutput || messages.length === 0) {
601
+ aiLogger.debug`Skipping memory add - empty input or output`
602
+ return
603
+ }
604
+
605
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
606
+ aiLogger.debug`addConversationMemories - orgId: "${orgId}", scopeId: "${orgScopeId}", sourceId: ${sourceId ?? 'none'}`
607
+
608
+ const onboardingActive = onboardStatus !== undefined && onboardStatus !== 'completed'
609
+ const extractionConfig = onboardingActive
610
+ ? { maxFacts: ONBOARDING_MEMORY_MAX_FACTS, customPrompt: ONBOARDING_MEMORY_EXTRACTION_PROMPT }
611
+ : undefined
612
+ const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
613
+
614
+ const scopes: AddOptions[] = [
615
+ {
616
+ scopeId: orgScopeId,
617
+ memoryType: ORG_MEMORY_TYPE,
618
+ metadata: { orgId, source, ...(sourceId ? { sourceId } : {}), ...sourceMetadata },
619
+ },
620
+ ]
621
+
622
+ for (const scopedAgentName of resolveAgentScopeNames(agentName, agentNames)) {
623
+ const agentId = yield* agentScopeId(orgId, scopedAgentName)
624
+ scopes.push({
625
+ scopeId: agentId,
626
+ memoryType: ORG_MEMORY_TYPE,
627
+ metadata: {
628
+ orgId,
629
+ agentName: scopedAgentName,
630
+ memoryScope: 'agent',
631
+ source,
632
+ ...(sourceId ? { sourceId } : {}),
633
+ ...sourceMetadata,
634
+ },
635
+ })
636
+ }
637
+
638
+ const extractedFacts = yield* memoryTryPromise('Failed to extract conversation memories.', () =>
639
+ orgMemory.extractFactsFromMessages(messages, extractionConfig),
640
+ )
641
+ if (shouldSkipExtractedFacts(extractedFacts)) {
642
+ aiLogger.debug`Skipping transient conversation memory`
643
+ return
644
+ }
645
+
646
+ const preparedUpdates = yield* memoryTryPromise('Failed to prepare conversation memories for scopes.', () =>
647
+ orgMemory.prepareFactsToScopes(extractedFacts, scopes),
648
+ )
649
+ yield* applyPreparedScopeUpdatesEffect({ orgId, orgMemory, preparedUpdates })
650
+ if (preparedUpdates.length > 0) {
651
+ aiLogger.debug`Conversation memories added to ${scopes.length} scope(s) from ${messages.length} message(s)`
652
+ }
653
+ }),
654
+ }
655
+
656
+ return service
657
+ }
658
+
659
+ export class MemoryServiceTag extends Context.Service<MemoryServiceTag, ReturnType<typeof createMemoryService>>()(
660
+ '@lota-sdk/core/MemoryService',
661
+ ) {}
662
+
663
+ export const MemoryServiceLive = Layer.effect(
664
+ MemoryServiceTag,
665
+ Effect.gen(function* () {
666
+ const db = yield* DatabaseServiceTag
667
+ const runtimeConfig = yield* RuntimeConfigServiceTag
668
+ const rerankService = yield* RerankServiceTag
669
+ const helperModelRuntime = yield* HelperModelTag
670
+ const background = yield* BackgroundWorkService
671
+ const orgMemoryCache = yield* makeOrgMemoryCache({ db, runtimeConfig, helperModelRuntime, background })
672
+ return createMemoryService({ runtimeConfig, rerankService, helperModelRuntime, orgMemoryCache })
673
+ }),
674
+ )