@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,95 @@
1
+ import { getAgentRoster } from '../../config/agent-defaults'
2
+ import type { ExtractedFact, Message } from '../../db/memory-types'
3
+ import { sanitizeAgentOutputForMemory } from '../../runtime/llm-content'
4
+ import { compactWhitespace, truncateText } from '../../utils/string'
5
+
6
+ const MAX_CONVERSATION_HISTORY_MESSAGES = 24
7
+ const MAX_CONVERSATION_MESSAGE_CHARS = 1_200
8
+ const MAX_CONVERSATION_MEMORY_BLOCK_CHARS = 2_000
9
+ const MAX_CONVERSATION_ATTACHMENT_CONTEXT_CHARS = 6_000
10
+ const LOW_VALUE_MEMORY_IMPORTANCE_THRESHOLD = 0.45
11
+
12
+ export const isRoutableAgentName = (value?: string): value is string =>
13
+ Boolean(value && getAgentRoster().includes(value))
14
+
15
+ function normalizeConversationText(value: string, maxChars: number): string {
16
+ const normalized = compactWhitespace(value)
17
+ if (!normalized) return ''
18
+ return truncateText(normalized, maxChars)
19
+ }
20
+
21
+ export function resolveAgentScopeNames(agentName?: string, agentNames: string[] = []): string[] {
22
+ const unique = new Set<string>()
23
+ if (typeof agentName === 'string' && agentName.trim()) {
24
+ unique.add(agentName.trim())
25
+ }
26
+ for (const candidate of agentNames) {
27
+ if (typeof candidate !== 'string') continue
28
+ const normalized = candidate.trim()
29
+ if (!normalized) continue
30
+ unique.add(normalized)
31
+ }
32
+ return [...unique].filter((name) => isRoutableAgentName(name))
33
+ }
34
+
35
+ export function buildConversationMessages(params: {
36
+ input: string
37
+ output: string
38
+ historyMessages: Array<{ role: 'user' | 'agent'; content: string; agentName?: string }>
39
+ memoryBlock?: string
40
+ attachmentContext?: string
41
+ }): { messages: Message[]; normalizedInput: string; sanitizedOutput: string } {
42
+ const normalizedInput = normalizeConversationText(params.input, MAX_CONVERSATION_MESSAGE_CHARS)
43
+ const sanitizedOutput = normalizeConversationText(
44
+ sanitizeAgentOutputForMemory(params.output),
45
+ MAX_CONVERSATION_MESSAGE_CHARS,
46
+ )
47
+ if (!normalizedInput || !sanitizedOutput) {
48
+ return { messages: [], normalizedInput, sanitizedOutput }
49
+ }
50
+
51
+ const messages: Message[] = []
52
+
53
+ const normalizedHistory: Message[] = params.historyMessages
54
+ .map((message): Message | null => {
55
+ const role: Message['role'] = message.role === 'agent' ? 'agent' : 'user'
56
+ const normalized = normalizeConversationText(
57
+ role === 'agent' ? sanitizeAgentOutputForMemory(message.content) : message.content,
58
+ MAX_CONVERSATION_MESSAGE_CHARS,
59
+ )
60
+ if (!normalized) return null
61
+ return { role, content: normalized }
62
+ })
63
+ .filter((message): message is Message => message !== null)
64
+ .slice(-MAX_CONVERSATION_HISTORY_MESSAGES)
65
+
66
+ messages.push(...normalizedHistory)
67
+
68
+ const normalizedMemoryBlock = normalizeConversationText(params.memoryBlock ?? '', MAX_CONVERSATION_MEMORY_BLOCK_CHARS)
69
+ if (normalizedMemoryBlock) {
70
+ messages.push({ role: 'user', content: `Thread memory block:\n${normalizedMemoryBlock}` })
71
+ }
72
+
73
+ const normalizedAttachmentContext = normalizeConversationText(
74
+ params.attachmentContext ?? '',
75
+ MAX_CONVERSATION_ATTACHMENT_CONTEXT_CHARS,
76
+ )
77
+ if (normalizedAttachmentContext) {
78
+ messages.push({ role: 'user', content: `Attachment context:\n${normalizedAttachmentContext}` })
79
+ }
80
+
81
+ messages.push({ role: 'user', content: normalizedInput })
82
+ messages.push({ role: 'agent', content: sanitizedOutput })
83
+
84
+ return { messages, normalizedInput, sanitizedOutput }
85
+ }
86
+
87
+ export function shouldSkipExtractedFacts(facts: ExtractedFact[]): boolean {
88
+ if (facts.length === 0) return true
89
+ return facts.every(
90
+ (fact) =>
91
+ fact.durability === 'ephemeral' &&
92
+ fact.importance <= LOW_VALUE_MEMORY_IMPORTANCE_THRESHOLD &&
93
+ fact.classification !== 'durable',
94
+ )
95
+ }
@@ -0,0 +1,27 @@
1
+ import { Effect, Schema } from 'effect'
2
+
3
+ export class MemoryServiceError extends Schema.TaggedErrorClass<MemoryServiceError>()('MemoryServiceError', {
4
+ message: Schema.String,
5
+ cause: Schema.Defect,
6
+ }) {}
7
+
8
+ export function tryMemoryPromise<A, E, R = never>(
9
+ message: string,
10
+ thunk: () => PromiseLike<A> | Effect.Effect<A, E, R>,
11
+ ): Effect.Effect<A, MemoryServiceError, R> {
12
+ return Effect.suspend(() => {
13
+ try {
14
+ const value = thunk()
15
+ if (Effect.isEffect(value)) {
16
+ return value.pipe(Effect.mapError((cause) => new MemoryServiceError({ message, cause })))
17
+ }
18
+
19
+ return Effect.tryPromise({
20
+ try: () => Promise.resolve(value),
21
+ catch: (cause) => new MemoryServiceError({ message, cause }),
22
+ })
23
+ } catch (cause) {
24
+ return Effect.fail(new MemoryServiceError({ message, cause }))
25
+ }
26
+ })
27
+ }
@@ -0,0 +1,50 @@
1
+ import type { Context } from 'effect'
2
+ import { Cache, Duration, Effect } from 'effect'
3
+
4
+ import { aiLogger } from '../../config/logger'
5
+ import { Memory } from '../../db/memory'
6
+ import type { SurrealDBService } from '../../db/service'
7
+ import type { HelperModelRuntime } from '../../runtime/helper-model'
8
+ import { ORG_SCOPE_PREFIX, scopeId } from '../../runtime/memory/memory-scope'
9
+ import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
10
+ import { createOrgMemoryAgent, ORG_MEMORY_PROMPT } from '../../system-agents/memory.agent'
11
+ import type { BackgroundWorkService } from '../background-work.service'
12
+
13
+ const MAX_ORG_MEMORY_CLIENTS = 128
14
+
15
+ interface OrgMemoryDeps {
16
+ db: SurrealDBService
17
+ runtimeConfig: ResolvedLotaRuntimeConfig
18
+ helperModelRuntime: HelperModelRuntime
19
+ background: Context.Service.Shape<typeof BackgroundWorkService>
20
+ }
21
+
22
+ export type OrgMemoryCache = Cache.Cache<string, Memory>
23
+
24
+ export function makeOrgMemoryCache(deps: OrgMemoryDeps) {
25
+ return Cache.make({
26
+ lookup: (cacheKey: string) =>
27
+ Effect.sync(() => {
28
+ aiLogger.debug`Memory client created and cached for ${cacheKey}`
29
+ return new Memory(
30
+ {
31
+ db: deps.db,
32
+ runtimeConfig: deps.runtimeConfig,
33
+ helperModelRuntime: deps.helperModelRuntime,
34
+ background: deps.background,
35
+ },
36
+ { createAgent: createOrgMemoryAgent },
37
+ { customPrompt: ORG_MEMORY_PROMPT },
38
+ )
39
+ }),
40
+ capacity: MAX_ORG_MEMORY_CLIENTS,
41
+ timeToLive: Duration.infinity,
42
+ })
43
+ }
44
+
45
+ export function getOrgMemory(cache: OrgMemoryCache, orgId: string) {
46
+ return Effect.gen(function* () {
47
+ const key = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
48
+ return yield* Cache.get(cache, key)
49
+ })
50
+ }
@@ -0,0 +1,86 @@
1
+ import { Effect } from 'effect'
2
+
3
+ import type { Memory } from '../../db/memory'
4
+ import type { MemoryRecord } from '../../db/memory-types'
5
+ import type { MemoryScopeError } from '../../runtime/memory/memory-scope'
6
+ import { ORG_SCOPE_PREFIX, agentScopeId, scopeId } from '../../runtime/memory/memory-scope'
7
+ import { compactWhitespace, truncateText } from '../../utils/string'
8
+ import type { OrgMemoryCache } from './memory-org-memory'
9
+ import { getOrgMemory } from './memory-org-memory'
10
+
11
+ const PRESEEDED_MEMORY_LIMIT = 5
12
+ const PRESEEDED_MEMORY_MAX_CHARS = 300
13
+ const PRESEEDED_MIN_IMPORTANCE = 0.7
14
+ const PRESEEDED_MEMORY_DURABILITY: MemoryRecord['durability'] = 'core'
15
+ type PreSeededMemoryError = Effect.Error<ReturnType<Memory['listTopMemories']>> | MemoryScopeError
16
+
17
+ function normalizePreSeededMemoryText(value: string): string {
18
+ return truncateText(compactWhitespace(value), PRESEEDED_MEMORY_MAX_CHARS)
19
+ }
20
+
21
+ function formatPreSeededMemoriesSection(memories: MemoryRecord[]): string | undefined {
22
+ const lines = memories
23
+ .map((memory) => normalizePreSeededMemoryText(memory.content))
24
+ .filter((line) => line.length > 0)
25
+ .map((line) => `- ${line}`)
26
+ if (lines.length === 0) return undefined
27
+
28
+ return ['<pre-seeded-memories>', ...lines, '</pre-seeded-memories>'].join('\n')
29
+ }
30
+
31
+ export function getTopMemoriesSection(params: {
32
+ orgMemoryCache: OrgMemoryCache
33
+ orgId: string
34
+ agentName?: string
35
+ limit?: number
36
+ }): Effect.Effect<string | undefined, PreSeededMemoryError, never> {
37
+ return Effect.gen(function* () {
38
+ const orgMemory = yield* getOrgMemory(params.orgMemoryCache, params.orgId)
39
+ const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, params.orgId)
40
+ const requestedLimit = params.limit ?? PRESEEDED_MEMORY_LIMIT
41
+ const limit = Math.max(1, Math.min(requestedLimit, PRESEEDED_MEMORY_LIMIT))
42
+
43
+ const agentScopedId =
44
+ params.agentName && params.agentName.trim() ? yield* agentScopeId(params.orgId, params.agentName) : undefined
45
+
46
+ const [orgTopMemories, agentTopMemories] = yield* Effect.all([
47
+ orgMemory.listTopMemories({
48
+ scopeId: orgScopeId,
49
+ limit,
50
+ memoryType: 'fact',
51
+ durability: PRESEEDED_MEMORY_DURABILITY,
52
+ minImportance: PRESEEDED_MIN_IMPORTANCE,
53
+ }),
54
+ agentScopedId
55
+ ? orgMemory.listTopMemories({
56
+ scopeId: agentScopedId,
57
+ limit,
58
+ memoryType: 'fact',
59
+ durability: PRESEEDED_MEMORY_DURABILITY,
60
+ minImportance: PRESEEDED_MIN_IMPORTANCE,
61
+ })
62
+ : Effect.succeed([] as MemoryRecord[]),
63
+ ])
64
+
65
+ const combined = [...agentTopMemories, ...orgTopMemories].sort((left, right) => {
66
+ if (right.importance !== left.importance) return right.importance - left.importance
67
+ if (right.accessCount !== left.accessCount) return right.accessCount - left.accessCount
68
+ const rightLastAccess = right.lastAccessedAt?.getTime() ?? 0
69
+ const leftLastAccess = left.lastAccessedAt?.getTime() ?? 0
70
+ if (rightLastAccess !== leftLastAccess) return rightLastAccess - leftLastAccess
71
+ return right.createdAt.getTime() - left.createdAt.getTime()
72
+ })
73
+
74
+ const deduped: MemoryRecord[] = []
75
+ const seen = new Set<string>()
76
+ for (const memory of combined) {
77
+ const normalizedKey = compactWhitespace(memory.content).toLowerCase()
78
+ if (!normalizedKey || seen.has(normalizedKey)) continue
79
+ seen.add(normalizedKey)
80
+ deduped.push(memory)
81
+ if (deduped.length >= limit) break
82
+ }
83
+
84
+ return formatPreSeededMemoriesSection(deduped)
85
+ })
86
+ }
@@ -0,0 +1,297 @@
1
+ import { Effect, Schema } from 'effect'
2
+ import { z } from 'zod'
3
+
4
+ import { aiLogger } from '../../config/logger'
5
+ import type { MemorySearchResult } from '../../db/memory-types'
6
+ import type { HelperModelRuntime } from '../../runtime/helper-model'
7
+ import type { MemoryRerankerStrategy, ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
8
+ import { createMemoryRerankerAgent, MEMORY_RERANKER_PROMPT } from '../../system-agents/memory-reranker.agent'
9
+ import { compactWhitespace, truncateText } from '../../utils/string'
10
+ import type { makeRerankService } from './rerank.service'
11
+
12
+ const RERANK_CANDIDATE_MAX_CHARS = 500
13
+ const RERANK_SECTION_TITLE = 'Most relevant memories'
14
+
15
+ const RERANK_PROMPT_CANDIDATE_SCHEMA = Schema.Struct({ id: Schema.String, text: Schema.String, score: Schema.Number })
16
+
17
+ const RERANK_PROMPT_MULTI_SCOPE_CANDIDATE_SCHEMA = Schema.Struct({
18
+ id: Schema.String,
19
+ text: Schema.String,
20
+ score: Schema.Number,
21
+ scope: Schema.String,
22
+ })
23
+
24
+ const RERANK_PROMPT_SCHEMA = Schema.Struct({
25
+ query: Schema.String,
26
+ maxItems: Schema.Number,
27
+ candidates: Schema.Array(RERANK_PROMPT_CANDIDATE_SCHEMA),
28
+ })
29
+
30
+ const RERANK_MULTI_SCOPE_PROMPT_SCHEMA = Schema.Struct({
31
+ query: Schema.String,
32
+ maxItems: Schema.Number,
33
+ candidates: Schema.Array(RERANK_PROMPT_MULTI_SCOPE_CANDIDATE_SCHEMA),
34
+ })
35
+
36
+ const stringifyRerankPrompt = Schema.encodeSync(Schema.fromJsonString(RERANK_PROMPT_SCHEMA))
37
+ const stringifyRerankMultiScopePrompt = Schema.encodeSync(Schema.fromJsonString(RERANK_MULTI_SCOPE_PROMPT_SCHEMA))
38
+
39
+ class MemoryRerankError extends Schema.TaggedErrorClass<MemoryRerankError>()('MemoryRerankError', {
40
+ scope: Schema.Literals(['single', 'multi-scope']),
41
+ message: Schema.String,
42
+ cause: Schema.optional(Schema.Unknown),
43
+ }) {}
44
+
45
+ const MemoryRerankOutputSchema = z.object({
46
+ sections: z.array(
47
+ z.object({
48
+ title: z.string(),
49
+ items: z.array(
50
+ z.object({
51
+ id: z.string(),
52
+ relevance: z.string().describe('Short relevance reason. Use empty string when no reason is needed.'),
53
+ }),
54
+ ),
55
+ }),
56
+ ),
57
+ })
58
+
59
+ export type MemoryRerankOutput = z.infer<typeof MemoryRerankOutputSchema>
60
+
61
+ function truncateCandidateText(value: string): string {
62
+ return truncateText(compactWhitespace(value), RERANK_CANDIDATE_MAX_CHARS)
63
+ }
64
+
65
+ function readNumericMetadata(metadata: Record<string, unknown>, key: string): number {
66
+ const value = metadata[key]
67
+ if (typeof value === 'number' && Number.isFinite(value)) return value
68
+ return 0
69
+ }
70
+
71
+ function buildRerankerCandidateText(candidate: MemorySearchResult): string {
72
+ const metadata = candidate.metadata
73
+ const relationCount = readNumericMetadata(metadata, 'relationCount')
74
+ const supportCount = readNumericMetadata(metadata, 'supportCount')
75
+ const contradictCount = readNumericMetadata(metadata, 'contradictCount')
76
+
77
+ const relatedContextRaw = metadata.relatedContext
78
+ const relatedContext = Array.isArray(relatedContextRaw)
79
+ ? relatedContextRaw
80
+ .slice(0, 3)
81
+ .map((item) => String(item).trim())
82
+ .filter((item) => item.length > 0)
83
+ : []
84
+
85
+ const contextLines: string[] = []
86
+ if (relatedContext.length > 0) {
87
+ contextLines.push(`Related context: ${relatedContext.join(' | ')}`)
88
+ }
89
+ if (relationCount > 0 || supportCount > 0 || contradictCount > 0) {
90
+ contextLines.push(
91
+ `Graph signals: relations=${relationCount}; supports=${supportCount}; contradicts=${contradictCount}`,
92
+ )
93
+ }
94
+
95
+ if (contextLines.length === 0) return candidate.content
96
+ return `${candidate.content}\n\n${contextLines.join('\n')}`
97
+ }
98
+
99
+ function getRerankerStrategy(runtimeConfig: ResolvedLotaRuntimeConfig): MemoryRerankerStrategy {
100
+ return runtimeConfig.memory.rerankerStrategy
101
+ }
102
+
103
+ function buildRerankOutput(ids: string[], title = RERANK_SECTION_TITLE): MemoryRerankOutput {
104
+ return { sections: [{ title, items: ids.map((id) => ({ id, relevance: '' })) }] }
105
+ }
106
+
107
+ function toRerankError(scope: 'single' | 'multi-scope', message: string, cause?: unknown): MemoryRerankError {
108
+ return new MemoryRerankError({ scope, message, cause })
109
+ }
110
+
111
+ function logRerankError(scope: 'single' | 'multi-scope', error: unknown): void {
112
+ if (scope === 'multi-scope') {
113
+ aiLogger.error`Multi-scope memory reranker failed: ${error}`
114
+ return
115
+ }
116
+
117
+ aiLogger.error`Memory reranker failed: ${error}`
118
+ }
119
+
120
+ function rerankCandidatesWithHelperEffect(
121
+ helperModelRuntime: HelperModelRuntime,
122
+ query: string,
123
+ candidates: MemorySearchResult[],
124
+ maxItems: number,
125
+ ): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
126
+ return Effect.tryPromise({
127
+ try: () =>
128
+ helperModelRuntime.generateHelperStructured({
129
+ tag: 'memory-reranker',
130
+ createAgent: createMemoryRerankerAgent,
131
+ defaultSystemPrompt: MEMORY_RERANKER_PROMPT,
132
+ messages: [
133
+ {
134
+ role: 'user',
135
+ content: stringifyRerankPrompt({
136
+ query,
137
+ maxItems,
138
+ candidates: candidates.map((candidate) => ({
139
+ id: candidate.id,
140
+ text: truncateCandidateText(buildRerankerCandidateText(candidate)),
141
+ score: candidate.score,
142
+ })),
143
+ }),
144
+ },
145
+ ],
146
+ schema: MemoryRerankOutputSchema,
147
+ }),
148
+ catch: (cause) => {
149
+ logRerankError('single', cause)
150
+ return toRerankError('single', 'Memory reranker failed.', cause)
151
+ },
152
+ })
153
+ }
154
+
155
+ function rerankCandidatesWithRerankServiceEffect(
156
+ query: string,
157
+ candidates: MemorySearchResult[],
158
+ maxItems: number,
159
+ reranker: ReturnType<typeof makeRerankService>,
160
+ ): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
161
+ return reranker
162
+ .rerankDocuments({
163
+ query,
164
+ topN: maxItems,
165
+ documents: candidates.map((candidate) => ({
166
+ id: candidate.id,
167
+ text: truncateCandidateText(buildRerankerCandidateText(candidate)),
168
+ })),
169
+ })
170
+ .pipe(
171
+ Effect.mapError((cause) => {
172
+ logRerankError('single', cause)
173
+ return toRerankError('single', 'Memory reranker failed.', cause)
174
+ }),
175
+ Effect.map((reranked) => buildRerankOutput(reranked.results.map((item: { id: string }) => item.id))),
176
+ )
177
+ }
178
+
179
+ export function rerankCandidates(
180
+ runtimeConfig: ResolvedLotaRuntimeConfig,
181
+ helperModelRuntime: HelperModelRuntime,
182
+ query: string,
183
+ candidates: MemorySearchResult[],
184
+ maxItems: number,
185
+ reranker?: ReturnType<typeof makeRerankService>,
186
+ ): Effect.Effect<MemoryRerankOutput | null> {
187
+ if (candidates.length === 0) return Effect.succeed(null)
188
+
189
+ return Effect.gen(function* () {
190
+ if (getRerankerStrategy(runtimeConfig) === 'rerank' && reranker) {
191
+ return yield* rerankCandidatesWithRerankServiceEffect(query, candidates, maxItems, reranker)
192
+ }
193
+
194
+ return yield* rerankCandidatesWithHelperEffect(helperModelRuntime, query, candidates, maxItems)
195
+ }).pipe(
196
+ Effect.catchCause((cause) =>
197
+ Effect.sync(() => {
198
+ logRerankError('single', cause)
199
+ return null
200
+ }),
201
+ ),
202
+ )
203
+ }
204
+
205
+ function rerankCandidatesMultiScopeWithHelperEffect(
206
+ helperModelRuntime: HelperModelRuntime,
207
+ query: string,
208
+ flattenedCandidates: Array<MemorySearchResult & { scopeTag: string }>,
209
+ maxItems: number,
210
+ ): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
211
+ return Effect.tryPromise({
212
+ try: () =>
213
+ helperModelRuntime.generateHelperStructured({
214
+ tag: 'memory-reranker-multi-scope',
215
+ createAgent: createMemoryRerankerAgent,
216
+ defaultSystemPrompt: MEMORY_RERANKER_PROMPT,
217
+ messages: [
218
+ {
219
+ role: 'user',
220
+ content: stringifyRerankMultiScopePrompt({
221
+ query,
222
+ maxItems,
223
+ candidates: flattenedCandidates.map((candidate) => ({
224
+ id: candidate.id,
225
+ text: truncateCandidateText(buildRerankerCandidateText(candidate)),
226
+ score: candidate.score,
227
+ scope: candidate.scopeTag,
228
+ })),
229
+ }),
230
+ },
231
+ ],
232
+ schema: MemoryRerankOutputSchema,
233
+ }),
234
+ catch: (cause) => {
235
+ logRerankError('multi-scope', cause)
236
+ return toRerankError('multi-scope', 'Multi-scope memory reranker failed.', cause)
237
+ },
238
+ })
239
+ }
240
+
241
+ function rerankCandidatesMultiScopeWithRerankServiceEffect(
242
+ query: string,
243
+ flattenedCandidates: Array<MemorySearchResult & { scopeTag: string }>,
244
+ maxItems: number,
245
+ reranker: ReturnType<typeof makeRerankService>,
246
+ ): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
247
+ return reranker
248
+ .rerankDocuments({
249
+ query,
250
+ topN: maxItems,
251
+ documents: flattenedCandidates.map((candidate) => ({
252
+ id: candidate.id,
253
+ text: truncateCandidateText(`${buildRerankerCandidateText(candidate)}\n\nScope: ${candidate.scopeTag}`),
254
+ })),
255
+ })
256
+ .pipe(
257
+ Effect.mapError((cause) => {
258
+ logRerankError('multi-scope', cause)
259
+ return toRerankError('multi-scope', 'Multi-scope memory reranker failed.', cause)
260
+ }),
261
+ Effect.map((reranked) =>
262
+ buildRerankOutput(
263
+ reranked.results.map((item: { id: string }) => item.id),
264
+ 'Top matches across memory scopes',
265
+ ),
266
+ ),
267
+ )
268
+ }
269
+
270
+ export function rerankCandidatesMultiScope(
271
+ runtimeConfig: ResolvedLotaRuntimeConfig,
272
+ helperModelRuntime: HelperModelRuntime,
273
+ query: string,
274
+ scopedCandidates: Array<{ scopeTag: string; candidates: MemorySearchResult[] }>,
275
+ maxItems: number,
276
+ reranker?: ReturnType<typeof makeRerankService>,
277
+ ): Effect.Effect<MemoryRerankOutput | null> {
278
+ const flattened = scopedCandidates.flatMap(({ scopeTag, candidates }) =>
279
+ candidates.map((candidate) => ({ ...candidate, scopeTag })),
280
+ )
281
+ if (flattened.length === 0 || flattened.length <= maxItems) return Effect.succeed(null)
282
+
283
+ return Effect.gen(function* () {
284
+ if (getRerankerStrategy(runtimeConfig) === 'rerank' && reranker) {
285
+ return yield* rerankCandidatesMultiScopeWithRerankServiceEffect(query, flattened, maxItems, reranker)
286
+ }
287
+
288
+ return yield* rerankCandidatesMultiScopeWithHelperEffect(helperModelRuntime, query, flattened, maxItems)
289
+ }).pipe(
290
+ Effect.catchCause((cause) =>
291
+ Effect.sync(() => {
292
+ logRerankError('multi-scope', cause)
293
+ return null
294
+ }),
295
+ ),
296
+ )
297
+ }
@@ -1,8 +1,9 @@
1
- import { MEMORY } from '../config/constants'
2
- import { VECTOR_SEARCH_OVERFETCH_MULTIPLIER } from '../config/search'
3
- import type { MemorySearchResult } from '../db/memory-types'
4
- import { compactWhitespace, truncateText } from '../utils/string'
5
- import type { MemoryRerankOutput } from './memory.service'
1
+ import { VECTOR_SEARCH_OVERFETCH_MULTIPLIER } from '@lota-sdk/shared'
2
+
3
+ import { MEMORY } from '../../config/constants'
4
+ import type { MemorySearchResult } from '../../db/memory-types'
5
+ import { compactWhitespace, truncateText } from '../../utils/string'
6
+ import type { MemoryRerankOutput } from './memory-rerank'
6
7
 
7
8
  export function getCandidateLimit(limit: number): number {
8
9
  return Math.max(limit * VECTOR_SEARCH_OVERFETCH_MULTIPLIER, MEMORY.DEFAULT_CANDIDATE_LIMIT)