@lota-sdk/core 0.4.9 → 0.4.11

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 (182) hide show
  1. package/package.json +2 -2
  2. package/src/ai/embedding-cache.ts +3 -1
  3. package/src/ai-gateway/ai-gateway.ts +164 -82
  4. package/src/ai-gateway/index.ts +16 -1
  5. package/src/config/agent-defaults.ts +4 -107
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/config/background-processing.ts +1 -1
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +22 -25
  10. package/src/config/thread-defaults.ts +1 -10
  11. package/src/create-runtime.ts +145 -670
  12. package/src/db/base.service.ts +30 -38
  13. package/src/db/memory-query-builder.ts +2 -1
  14. package/src/db/memory-store.ts +29 -20
  15. package/src/db/memory.ts +188 -195
  16. package/src/db/service-normalization.ts +97 -64
  17. package/src/db/service.ts +496 -384
  18. package/src/db/startup.ts +30 -19
  19. package/src/effect/helpers.ts +30 -5
  20. package/src/effect/index.ts +7 -7
  21. package/src/effect/layers.ts +75 -72
  22. package/src/effect/services.ts +15 -11
  23. package/src/embeddings/provider.ts +65 -71
  24. package/src/index.ts +13 -12
  25. package/src/queues/autonomous-job.queue.ts +177 -143
  26. package/src/queues/context-compaction.queue.ts +41 -39
  27. package/src/queues/delayed-node-promotion.queue.ts +61 -42
  28. package/src/queues/document-processor.queue.ts +5 -3
  29. package/src/queues/index.ts +1 -0
  30. package/src/queues/memory-consolidation.queue.ts +79 -53
  31. package/src/queues/organization-learning.queue.ts +70 -33
  32. package/src/queues/plan-agent-heartbeat.queue.ts +111 -83
  33. package/src/queues/plan-scheduler.queue.ts +101 -97
  34. package/src/queues/post-chat-memory.queue.ts +56 -46
  35. package/src/queues/queue-factory.ts +146 -69
  36. package/src/queues/queues.service.ts +61 -0
  37. package/src/queues/title-generation.queue.ts +44 -44
  38. package/src/redis/connection.ts +181 -164
  39. package/src/redis/org-memory-lock.ts +24 -9
  40. package/src/redis/redis-lease-lock.ts +8 -1
  41. package/src/redis/stream-context.ts +17 -9
  42. package/src/runtime/agent-identity-overrides.ts +7 -3
  43. package/src/runtime/agent-runtime-policy.ts +10 -5
  44. package/src/runtime/agent-stream-helpers.ts +24 -15
  45. package/src/runtime/chat-run-orchestration.ts +1 -1
  46. package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
  47. package/src/runtime/context-compaction/context-compaction.ts +131 -85
  48. package/src/runtime/domain-layer.ts +203 -0
  49. package/src/runtime/execution-plan-visibility.ts +5 -2
  50. package/src/runtime/graph-designer.ts +0 -14
  51. package/src/runtime/helper-model.ts +8 -4
  52. package/src/runtime/index.ts +1 -1
  53. package/src/runtime/indexed-repositories-policy.ts +2 -6
  54. package/src/runtime/memory/memory-block.ts +19 -9
  55. package/src/runtime/memory/memory-pipeline.ts +53 -66
  56. package/src/runtime/memory/memory-scope.ts +33 -29
  57. package/src/runtime/plugin-resolution.ts +58 -62
  58. package/src/runtime/post-turn-side-effects.ts +139 -161
  59. package/src/runtime/retrieval-adapters.ts +4 -4
  60. package/src/runtime/runtime-config.ts +3 -9
  61. package/src/runtime/runtime-extensions.ts +0 -43
  62. package/src/runtime/runtime-lifecycle.ts +124 -0
  63. package/src/runtime/runtime-services.ts +455 -0
  64. package/src/runtime/runtime-worker-registry.ts +113 -30
  65. package/src/runtime/social-chat/social-chat-agent-runner.ts +13 -8
  66. package/src/runtime/social-chat/social-chat-history.ts +24 -13
  67. package/src/runtime/social-chat/social-chat.ts +420 -369
  68. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +64 -57
  69. package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
  70. package/src/runtime/thread-chat-helpers.ts +18 -9
  71. package/src/runtime/thread-turn-context.ts +28 -74
  72. package/src/runtime/turn-lifecycle.ts +6 -14
  73. package/src/services/agent-activity.service.ts +169 -176
  74. package/src/services/agent-executor.service.ts +207 -196
  75. package/src/services/artifact.service.ts +10 -5
  76. package/src/services/attachment.service.ts +16 -48
  77. package/src/services/autonomous-job.service.ts +81 -87
  78. package/src/services/background-work.service.ts +54 -0
  79. package/src/services/chat-run-registry.service.ts +3 -1
  80. package/src/services/context-compaction.service.ts +8 -10
  81. package/src/services/document-chunk.service.ts +8 -17
  82. package/src/services/execution-plan/execution-plan-graph.ts +122 -109
  83. package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
  84. package/src/services/execution-plan/execution-plan.service.ts +68 -51
  85. package/src/services/feedback-loop.service.ts +1 -1
  86. package/src/services/global-orchestrator.service.ts +49 -15
  87. package/src/services/graph-full-routing.ts +49 -37
  88. package/src/services/index.ts +1 -0
  89. package/src/services/institutional-memory.service.ts +8 -17
  90. package/src/services/learned-skill.service.ts +38 -35
  91. package/src/services/memory/memory-conversation.ts +10 -5
  92. package/src/services/memory/memory-errors.ts +27 -0
  93. package/src/services/memory/memory-org-memory.ts +14 -3
  94. package/src/services/memory/memory-preseeded.ts +10 -4
  95. package/src/services/memory/memory-utils.ts +2 -1
  96. package/src/services/memory/memory.service.ts +37 -52
  97. package/src/services/memory/rerank.service.ts +3 -11
  98. package/src/services/monitoring-window.service.ts +1 -1
  99. package/src/services/mutating-approval.service.ts +1 -1
  100. package/src/services/node-workspace.service.ts +2 -2
  101. package/src/services/notification.service.ts +16 -4
  102. package/src/services/organization-member.service.ts +1 -1
  103. package/src/services/organization.service.ts +34 -51
  104. package/src/services/ownership-dispatcher.service.ts +148 -95
  105. package/src/services/plan/plan-agent-heartbeat.service.ts +30 -16
  106. package/src/services/plan/plan-agent-query.service.ts +13 -9
  107. package/src/services/plan/plan-approval.service.ts +52 -48
  108. package/src/services/plan/plan-artifact.service.ts +2 -2
  109. package/src/services/plan/plan-builder.service.ts +2 -2
  110. package/src/services/plan/plan-checkpoint.service.ts +1 -1
  111. package/src/services/plan/plan-compiler.service.ts +1 -1
  112. package/src/services/plan/plan-completion-side-effects.ts +99 -113
  113. package/src/services/plan/plan-coordination.service.ts +1 -1
  114. package/src/services/plan/plan-cycle.service.ts +171 -202
  115. package/src/services/plan/plan-deadline.service.ts +304 -307
  116. package/src/services/plan/plan-event-delivery.service.ts +84 -72
  117. package/src/services/plan/plan-executor-context.ts +2 -0
  118. package/src/services/plan/plan-executor-graph.ts +375 -353
  119. package/src/services/plan/plan-executor-helpers.ts +60 -75
  120. package/src/services/plan/plan-executor.service.ts +494 -489
  121. package/src/services/plan/plan-run.service.ts +12 -19
  122. package/src/services/plan/plan-scheduler.service.ts +89 -82
  123. package/src/services/plan/plan-template.service.ts +1 -1
  124. package/src/services/plan/plan-transaction-events.ts +8 -5
  125. package/src/services/plan/plan-validator.service.ts +1 -1
  126. package/src/services/plan/plan-workspace.service.ts +17 -11
  127. package/src/services/plugin-executor.service.ts +26 -21
  128. package/src/services/quality-metrics.service.ts +1 -1
  129. package/src/services/queue-job.service.ts +8 -17
  130. package/src/services/recent-activity-title.service.ts +22 -10
  131. package/src/services/recent-activity.service.ts +1 -1
  132. package/src/services/skill-resolver.service.ts +1 -1
  133. package/src/services/social-chat-history.service.ts +37 -20
  134. package/src/services/system-executor.service.ts +25 -20
  135. package/src/services/thread/thread-bootstrap.ts +37 -19
  136. package/src/services/thread/thread-listing.ts +2 -1
  137. package/src/services/thread/thread-memory-block.ts +18 -5
  138. package/src/services/thread/thread-message.service.ts +30 -13
  139. package/src/services/thread/thread-title.service.ts +1 -1
  140. package/src/services/thread/thread-turn-execution.ts +87 -83
  141. package/src/services/thread/thread-turn-preparation.service.ts +65 -40
  142. package/src/services/thread/thread-turn-streaming.ts +32 -36
  143. package/src/services/thread/thread-turn.ts +43 -29
  144. package/src/services/thread/thread.service.ts +32 -8
  145. package/src/services/user.service.ts +1 -1
  146. package/src/services/write-intent-validator.service.ts +1 -1
  147. package/src/storage/attachment-storage.service.ts +7 -4
  148. package/src/storage/generated-document-storage.service.ts +1 -1
  149. package/src/system-agents/context-compaction.agent.ts +1 -1
  150. package/src/system-agents/helper-agent-options.ts +1 -1
  151. package/src/system-agents/memory-reranker.agent.ts +1 -1
  152. package/src/system-agents/memory.agent.ts +1 -1
  153. package/src/system-agents/recent-activity-title-refiner.agent.ts +9 -6
  154. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  155. package/src/system-agents/skill-extractor.agent.ts +1 -1
  156. package/src/system-agents/skill-manager.agent.ts +1 -1
  157. package/src/system-agents/thread-router.agent.ts +23 -20
  158. package/src/system-agents/title-generator.agent.ts +1 -1
  159. package/src/tools/execution-plan.tool.ts +36 -20
  160. package/src/tools/fetch-webpage.tool.ts +30 -22
  161. package/src/tools/firecrawl-client.ts +1 -6
  162. package/src/tools/plan-approval.tool.ts +9 -1
  163. package/src/tools/remember-memory.tool.ts +3 -6
  164. package/src/tools/research-topic.tool.ts +12 -3
  165. package/src/tools/search-web.tool.ts +26 -18
  166. package/src/tools/search.tool.ts +4 -5
  167. package/src/tools/team-think.tool.ts +139 -121
  168. package/src/utils/async.ts +15 -6
  169. package/src/utils/errors.ts +27 -15
  170. package/src/workers/bootstrap.ts +34 -58
  171. package/src/workers/memory-consolidation.worker.ts +4 -1
  172. package/src/workers/organization-learning.worker.ts +16 -3
  173. package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
  174. package/src/workers/regular-chat-memory-digest.runner.ts +46 -29
  175. package/src/workers/skill-extraction.runner.ts +13 -15
  176. package/src/workers/worker-utils.ts +14 -8
  177. package/src/config/search.ts +0 -3
  178. package/src/effect/awaitable-effect.ts +0 -87
  179. package/src/effect/runtime-ref.ts +0 -25
  180. package/src/effect/runtime.ts +0 -31
  181. package/src/redis/runtime-connection.ts +0 -10
  182. package/src/runtime/agent-types.ts +0 -1
@@ -1,15 +1,10 @@
1
1
  import type { ChatMessage } from '@lota-sdk/shared'
2
2
  import { Schema, Effect } from 'effect'
3
- import type { Context } from 'effect'
4
3
 
5
4
  import type { ResolvedAgentConfig } from '../config/agent-defaults'
6
5
  import type { RecordIdRef } from '../db/record-id'
7
- import { getCurrentRuntime } from '../effect/runtime-ref'
8
6
  import { AgentConfigServiceTag, RuntimeAdaptersServiceTag } from '../effect/services'
9
- import { enqueueMemoryConsolidation } from '../queues/memory-consolidation.queue'
10
- import { enqueueRegularChatMemoryDigest, enqueueSkillExtraction } from '../queues/organization-learning.queue'
11
- import { enqueuePostChatMemory } from '../queues/post-chat-memory.queue'
12
- import { enqueueRecentActivityTitleRefinement } from '../queues/title-generation.queue'
7
+ import { LotaQueuesServiceTag } from '../queues/queues.service'
13
8
  import { RecentActivityServiceTag } from '../services/recent-activity.service'
14
9
  import { ThreadServiceTag } from '../services/thread/thread.service'
15
10
  import type { NormalizedThread, ThreadRecord } from '../services/thread/thread.types'
@@ -21,7 +16,6 @@ import {
21
16
  shouldEnqueueOnboardingPostChatMemory,
22
17
  shouldEnqueueRegularDigestForThread,
23
18
  } from './memory/memory-digest-policy'
24
- import type { LotaRuntimeAdapters } from './runtime-extensions'
25
19
  import { shouldEnqueueSkillExtraction } from './skill-extraction-policy'
26
20
  import {
27
21
  appendPersistedThreadContextToHistoryMessages,
@@ -33,13 +27,6 @@ import {
33
27
  toHistoryMessages,
34
28
  } from './thread-chat-helpers'
35
29
 
36
- interface PostTurnSideEffectsServices {
37
- recentActivityService: Context.Service.Shape<typeof RecentActivityServiceTag>
38
- threadService: Context.Service.Shape<typeof ThreadServiceTag>
39
- agentConfig: ResolvedAgentConfig
40
- runtimeAdapters: LotaRuntimeAdapters
41
- }
42
-
43
30
  class PostTurnSideEffectsError extends Schema.TaggedErrorClass<PostTurnSideEffectsError>()('PostTurnSideEffectsError', {
44
31
  message: Schema.String,
45
32
  cause: Schema.Defect,
@@ -55,20 +42,6 @@ function tryPostTurnSideEffect<A>(
55
42
  })
56
43
  }
57
44
 
58
- function getPostTurnSideEffectsServices(): PostTurnSideEffectsServices {
59
- const runtime = getCurrentRuntime()
60
- return runtime.runSync(
61
- Effect.gen(function* () {
62
- return {
63
- recentActivityService: yield* RecentActivityServiceTag,
64
- threadService: yield* ThreadServiceTag,
65
- agentConfig: yield* AgentConfigServiceTag,
66
- runtimeAdapters: yield* RuntimeAdaptersServiceTag,
67
- }
68
- }),
69
- )
70
- }
71
-
72
45
  function resolveDisplayName(
73
46
  agentConfig: ResolvedAgentConfig,
74
47
  agentId: string,
@@ -137,154 +110,159 @@ interface PostTurnSideEffectsParams {
137
110
  agentDisplayNamesById?: Partial<Record<string, string>>
138
111
  }
139
112
 
140
- const runPostTurnSideEffectsEffect = (params: PostTurnSideEffectsParams) =>
141
- Effect.gen(function* () {
142
- const { recentActivityService, threadService, agentConfig, runtimeAdapters } = getPostTurnSideEffectsServices()
143
- const recentHistory = yield* tryPostTurnSideEffect('Failed to load recent thread history.', () =>
144
- params.loadRecentHistory(),
113
+ export const runPostTurnSideEffects = Effect.fn('PostTurnSideEffects.run')(function* (
114
+ params: PostTurnSideEffectsParams,
115
+ ) {
116
+ const recentActivityService = yield* RecentActivityServiceTag
117
+ const threadService = yield* ThreadServiceTag
118
+ const agentConfig: ResolvedAgentConfig = yield* AgentConfigServiceTag
119
+ const runtimeAdapters = yield* RuntimeAdaptersServiceTag
120
+ const queues = yield* LotaQueuesServiceTag
121
+ const recentHistory = yield* tryPostTurnSideEffect('Failed to load recent thread history.', () =>
122
+ params.loadRecentHistory(),
123
+ )
124
+ const turnCount = yield* threadService.incrementTurnCount(params.threadRef)
125
+ const agentMessages = buildAgentHistoryMessages(agentConfig, params.allAssistantMessages)
126
+ const historyMessagesForMemory = appendPersistedThreadContextToHistoryMessages(
127
+ toHistoryMessages(agentConfig, recentHistory),
128
+ { compactionSummary: params.latestThreadRecord.compactionSummary },
129
+ )
130
+
131
+ const userMessageText = params.referenceUserMessage ? extractMessageText(params.referenceUserMessage).trim() : ''
132
+ const readableUploads = params.listReadableUploads()
133
+ const attachmentMetadataContext = buildReadableUploadMetadataContext(readableUploads)
134
+ const hasAttachmentContext = Boolean(attachmentMetadataContext)
135
+ const shouldExtractMemory = params.onboardingActive
136
+ ? shouldEnqueueOnboardingPostChatMemory({
137
+ onboardingActive: params.onboardingActive,
138
+ userMessageText,
139
+ hasAttachmentContext,
140
+ agentMessageCount: agentMessages.length,
141
+ })
142
+ : shouldEnqueueMemoryExtraction({ onboardingActive: params.onboardingActive, turnCount }) &&
143
+ userMessageText.length > 0
144
+
145
+ if (shouldExtractMemory) {
146
+ const memoryUserMessage = userMessageText || 'User uploaded attachment(s).'
147
+ yield* tryPostTurnSideEffect('Failed to enqueue post-chat memory extraction.', () =>
148
+ safeEnqueue(
149
+ () =>
150
+ queues.postChatMemory.enqueuePostChatMemory({
151
+ orgId: params.orgIdString,
152
+ threadId: params.threadIdString,
153
+ sourceId: params.referenceUserMessageId,
154
+ onboardStatus: readOptionalString((params.workspace as { onboardStatus?: unknown }).onboardStatus),
155
+ userMessage: memoryUserMessage,
156
+ historyMessages: historyMessagesForMemory,
157
+ agentMessages,
158
+ memoryBlock: params.memoryBlock.trim() ? params.memoryBlock : undefined,
159
+ attachmentContext: attachmentMetadataContext,
160
+ }),
161
+ { operationName: 'post-chat memory extraction enqueue' },
162
+ ),
145
163
  )
146
- const turnCount = yield* threadService.incrementTurnCount(params.threadRef)
147
- const agentMessages = buildAgentHistoryMessages(params.allAssistantMessages)
148
- const historyMessagesForMemory = appendPersistedThreadContextToHistoryMessages(toHistoryMessages(recentHistory), {
149
- compactionSummary: params.latestThreadRecord.compactionSummary,
150
- })
164
+ }
151
165
 
152
- const userMessageText = params.referenceUserMessage ? extractMessageText(params.referenceUserMessage).trim() : ''
153
- const readableUploads = params.listReadableUploads()
154
- const attachmentMetadataContext = buildReadableUploadMetadataContext(readableUploads)
155
- const hasAttachmentContext = Boolean(attachmentMetadataContext)
156
- const shouldExtractMemory = params.onboardingActive
157
- ? shouldEnqueueOnboardingPostChatMemory({
158
- onboardingActive: params.onboardingActive,
159
- userMessageText,
160
- hasAttachmentContext,
161
- agentMessageCount: agentMessages.length,
162
- })
163
- : shouldEnqueueMemoryExtraction({ onboardingActive: params.onboardingActive, turnCount }) &&
164
- userMessageText.length > 0
166
+ const referenceUserMessage = params.referenceUserMessage
167
+ if (params.isUserTurn && referenceUserMessage) {
168
+ const conversationSummary = buildConversationSummary({
169
+ agentConfig,
170
+ userMessageText,
171
+ assistantMessages: params.allAssistantMessages,
172
+ })
173
+ if (conversationSummary) {
174
+ const effectiveAgentId = params.visibleThreadAgentId ?? params.defaultLeadAgentId
175
+ const recentActivityResult = yield* recentActivityService.recordEvent({
176
+ orgId: params.orgRef,
177
+ userId: params.userRef,
178
+ source: 'system',
179
+ event: {
180
+ sourceEventId: `chat-turn:${params.referenceUserMessageId}`,
181
+ kind: 'chat.turn.completed',
182
+ targetKind: 'thread',
183
+ targetId: params.threadIdString,
184
+ mergeKey: `thread:${params.threadIdString}`,
185
+ title: buildRecentActivityChatSystemTitle({
186
+ agentConfig,
187
+ thread: params.thread,
188
+ visibleAgentId: effectiveAgentId,
189
+ agentDisplayNamesById: params.agentDisplayNamesById,
190
+ }),
191
+ sourceLabel: resolveDisplayName(agentConfig, effectiveAgentId, params.agentDisplayNamesById),
192
+ deepLink: buildRecentActivityChatDeepLink({
193
+ thread: params.thread,
194
+ threadId: params.threadIdString,
195
+ visibleAgentId: effectiveAgentId,
196
+ }),
197
+ metadata: {
198
+ agentId: effectiveAgentId,
199
+ agentName: resolveDisplayName(agentConfig, effectiveAgentId, params.agentDisplayNamesById),
200
+ threadId: params.threadIdString,
201
+ threadTitle: params.latestThreadRecord.title ?? params.thread.title,
202
+ threadType: params.thread.type,
203
+ ...(params.thread.threadType ? { coreType: params.thread.threadType } : {}),
204
+ userMessageText,
205
+ assistantSummary: conversationSummary,
206
+ messageId: params.referenceUserMessageId,
207
+ },
208
+ occurredAt: toIsoDateTimeString(referenceUserMessage.metadata?.createdAt ?? nowEpochMillis()),
209
+ },
210
+ })
165
211
 
166
- if (shouldExtractMemory) {
167
- const memoryUserMessage = userMessageText || 'User uploaded attachment(s).'
168
- yield* tryPostTurnSideEffect('Failed to enqueue post-chat memory extraction.', () =>
212
+ yield* tryPostTurnSideEffect('Failed to enqueue post-chat org action.', () =>
169
213
  safeEnqueue(
170
- () =>
171
- enqueuePostChatMemory({
214
+ () => {
215
+ const enqueuePostChatOrgAction = runtimeAdapters.enqueuePostChatOrgAction
216
+ if (!enqueuePostChatOrgAction) {
217
+ return
218
+ }
219
+
220
+ const sourceCreatedAt = referenceUserMessage.metadata?.createdAt ?? nowEpochMillis()
221
+ return enqueuePostChatOrgAction({
172
222
  orgId: params.orgIdString,
173
223
  threadId: params.threadIdString,
174
224
  sourceId: params.referenceUserMessageId,
175
- onboardStatus: readOptionalString((params.workspace as { onboardStatus?: unknown }).onboardStatus),
176
- userMessage: memoryUserMessage,
177
- historyMessages: historyMessagesForMemory,
178
- agentMessages,
179
- memoryBlock: params.memoryBlock.trim() ? params.memoryBlock : undefined,
180
- attachmentContext: attachmentMetadataContext,
181
- }),
182
- { operationName: 'post-chat memory extraction enqueue' },
225
+ sourceCreatedAt,
226
+ conversationSummary,
227
+ })
228
+ },
229
+ { operationName: 'post-chat org action enqueue' },
183
230
  ),
184
231
  )
185
- }
186
232
 
187
- const referenceUserMessage = params.referenceUserMessage
188
- if (params.isUserTurn && referenceUserMessage) {
189
- const conversationSummary = buildConversationSummary({
190
- userMessageText,
191
- assistantMessages: params.allAssistantMessages,
192
- })
193
- if (conversationSummary) {
194
- const effectiveAgentId = params.visibleThreadAgentId ?? params.defaultLeadAgentId
195
- const recentActivityResult = yield* recentActivityService.recordEvent({
196
- orgId: params.orgRef,
197
- userId: params.userRef,
198
- source: 'system',
199
- event: {
200
- sourceEventId: `chat-turn:${params.referenceUserMessageId}`,
201
- kind: 'chat.turn.completed',
202
- targetKind: 'thread',
203
- targetId: params.threadIdString,
204
- mergeKey: `thread:${params.threadIdString}`,
205
- title: buildRecentActivityChatSystemTitle({
206
- agentConfig,
207
- thread: params.thread,
208
- visibleAgentId: effectiveAgentId,
209
- agentDisplayNamesById: params.agentDisplayNamesById,
210
- }),
211
- sourceLabel: resolveDisplayName(agentConfig, effectiveAgentId, params.agentDisplayNamesById),
212
- deepLink: buildRecentActivityChatDeepLink({
213
- thread: params.thread,
214
- threadId: params.threadIdString,
215
- visibleAgentId: effectiveAgentId,
216
- }),
217
- metadata: {
218
- agentId: effectiveAgentId,
219
- agentName: resolveDisplayName(agentConfig, effectiveAgentId, params.agentDisplayNamesById),
220
- threadId: params.threadIdString,
221
- threadTitle: params.latestThreadRecord.title ?? params.thread.title,
222
- threadType: params.thread.type,
223
- ...(params.thread.threadType ? { coreType: params.thread.threadType } : {}),
224
- userMessageText,
225
- assistantSummary: conversationSummary,
226
- messageId: params.referenceUserMessageId,
227
- },
228
- occurredAt: toIsoDateTimeString(referenceUserMessage.metadata?.createdAt ?? nowEpochMillis()),
229
- },
230
- })
231
-
232
- yield* tryPostTurnSideEffect('Failed to enqueue post-chat org action.', () =>
233
+ if (recentActivityService.isMeaningfulRefinementCandidate(recentActivityResult.item)) {
234
+ yield* tryPostTurnSideEffect('Failed to enqueue recent activity title refinement.', () =>
233
235
  safeEnqueue(
234
- () => {
235
- const enqueuePostChatOrgAction = runtimeAdapters.enqueuePostChatOrgAction
236
- if (!enqueuePostChatOrgAction) {
237
- return
238
- }
239
-
240
- const sourceCreatedAt = referenceUserMessage.metadata?.createdAt ?? nowEpochMillis()
241
- return enqueuePostChatOrgAction({
242
- orgId: params.orgIdString,
243
- threadId: params.threadIdString,
244
- sourceId: params.referenceUserMessageId,
245
- sourceCreatedAt,
246
- conversationSummary,
247
- })
248
- },
249
- { operationName: 'post-chat org action enqueue' },
236
+ () =>
237
+ queues.titleGeneration.enqueueRecentActivityTitleRefinement({ activityId: recentActivityResult.item.id }),
238
+ { operationName: 'recent activity title refinement enqueue' },
250
239
  ),
251
240
  )
252
-
253
- if (recentActivityService.isMeaningfulRefinementCandidate(recentActivityResult.item)) {
254
- yield* tryPostTurnSideEffect('Failed to enqueue recent activity title refinement.', () =>
255
- safeEnqueue(() => enqueueRecentActivityTitleRefinement({ activityId: recentActivityResult.item.id }), {
256
- operationName: 'recent activity title refinement enqueue',
257
- }),
258
- )
259
- }
260
241
  }
261
242
  }
243
+ }
262
244
 
263
- if (shouldEnqueueRegularDigestForThread({ onboardingActive: params.onboardingActive, turnCount })) {
264
- yield* tryPostTurnSideEffect('Failed to enqueue regular chat memory digest.', () =>
265
- safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: params.orgIdString }), {
266
- operationName: 'regular chat memory digest enqueue',
267
- }),
268
- )
269
- }
270
-
271
- if (shouldEnqueueSkillExtraction({ onboardingActive: params.onboardingActive, turnCount })) {
272
- yield* tryPostTurnSideEffect('Failed to enqueue skill extraction.', () =>
273
- safeEnqueue(() => enqueueSkillExtraction({ orgId: params.orgIdString }), {
274
- operationName: 'skill extraction enqueue',
275
- }),
276
- )
277
- }
245
+ if (shouldEnqueueRegularDigestForThread({ onboardingActive: params.onboardingActive, turnCount })) {
246
+ yield* tryPostTurnSideEffect('Failed to enqueue regular chat memory digest.', () =>
247
+ safeEnqueue(() => queues.organizationLearning.enqueueRegularChatMemoryDigest({ orgId: params.orgIdString }), {
248
+ operationName: 'regular chat memory digest enqueue',
249
+ }),
250
+ )
251
+ }
278
252
 
279
- if (shouldEnqueueMemoryConsolidation({ onboardingActive: params.onboardingActive, turnCount })) {
280
- yield* tryPostTurnSideEffect('Failed to enqueue memory consolidation.', () =>
281
- safeEnqueue(() => enqueueMemoryConsolidation({ scopeId: params.orgIdString }), {
282
- operationName: 'memory consolidation enqueue',
283
- }),
284
- )
285
- }
286
- })
253
+ if (shouldEnqueueSkillExtraction({ onboardingActive: params.onboardingActive, turnCount })) {
254
+ yield* tryPostTurnSideEffect('Failed to enqueue skill extraction.', () =>
255
+ safeEnqueue(() => queues.organizationLearning.enqueueSkillExtraction({ orgId: params.orgIdString }), {
256
+ operationName: 'skill extraction enqueue',
257
+ }),
258
+ )
259
+ }
287
260
 
288
- export function runPostTurnSideEffects(params: PostTurnSideEffectsParams): Promise<void> {
289
- return Effect.runPromise(runPostTurnSideEffectsEffect(params))
290
- }
261
+ if (shouldEnqueueMemoryConsolidation({ onboardingActive: params.onboardingActive, turnCount })) {
262
+ yield* tryPostTurnSideEffect('Failed to enqueue memory consolidation.', () =>
263
+ safeEnqueue(() => queues.memoryConsolidation.enqueueMemoryConsolidation({ scopeId: params.orgIdString }), {
264
+ operationName: 'memory consolidation enqueue',
265
+ }),
266
+ )
267
+ }
268
+ })
@@ -1,8 +1,8 @@
1
1
  import { Schema, Effect } from 'effect'
2
2
 
3
- interface ScopedRetrievalTask<TCandidate> {
3
+ interface ScopedRetrievalTask<TCandidate, E = never> {
4
4
  scopeTag: string
5
- retrieve: () => PromiseLike<TCandidate[]> | Effect.Effect<TCandidate[], unknown>
5
+ retrieve: () => PromiseLike<TCandidate[]> | Effect.Effect<TCandidate[], E>
6
6
  }
7
7
 
8
8
  interface ScopedRetrievalResult<TCandidate> {
@@ -20,8 +20,8 @@ function toScopedRetrievalError(scopeTag: string, cause: unknown): ScopedRetriev
20
20
  return new ScopedRetrievalError({ scopeTag, message: cause instanceof Error ? cause.message : String(cause), cause })
21
21
  }
22
22
 
23
- export function executeScopedRetrieval<TCandidate>(
24
- tasks: ScopedRetrievalTask<TCandidate>[],
23
+ export function executeScopedRetrieval<TCandidate, E>(
24
+ tasks: ScopedRetrievalTask<TCandidate, E>[],
25
25
  ): Effect.Effect<ScopedRetrievalResult<TCandidate>[], ScopedRetrievalError> {
26
26
  return Effect.forEach(
27
27
  tasks,
@@ -8,8 +8,6 @@ import { DEFAULT_AI_GATEWAY_URL } from '../config/constants'
8
8
  import { OPENROUTER_FAST_RERANK_MODEL_ID } from '../config/model-constants'
9
9
  import type { LotaThreadConfig, ThreadBootstrapWelcomeConfig } from '../config/thread-defaults'
10
10
  import type { RecordIdRef } from '../db/record-id'
11
- import { getCurrentRuntime } from '../effect/runtime-ref'
12
- import { RuntimeConfigServiceTag } from '../effect/services'
13
11
  import type { NotificationService } from '../services/notification.service'
14
12
  import { isRecord } from '../utils/string'
15
13
  import type { GraphDesigner } from './graph-designer'
@@ -329,7 +327,7 @@ export function parseWorkerBootstrapEnv(env: Record<string, string | undefined>)
329
327
  return WORKER_BOOTSTRAP_ENV_SCHEMA.parse(env)
330
328
  }
331
329
 
332
- const runtimeEnvironmentConfig = Config.all({
330
+ export const lotaRuntimeEnvConfig = Config.all({
333
331
  surrealdbUrl: Config.string('SURREALDB_URL'),
334
332
  surrealdbNamespace: Config.string('SURREALDB_NAMESPACE'),
335
333
  surrealdbUser: Config.string('SURREALDB_USER'),
@@ -369,7 +367,7 @@ export function loadLotaRuntimeConfigFromEnv(
369
367
  overrides: LotaRuntimeEnvironmentOverrides,
370
368
  options: { configProvider?: ConfigProvider.ConfigProvider } = {},
371
369
  ) {
372
- return runtimeEnvironmentConfig
370
+ return lotaRuntimeEnvConfig
373
371
  .parse(options.configProvider ?? ConfigProvider.fromEnv())
374
372
  .pipe(
375
373
  Effect.map((env) =>
@@ -403,7 +401,7 @@ export function loadLotaRuntimeConfigFromEnv(
403
401
  apiKey: Redacted.value(env.firecrawlApiKey),
404
402
  ...(Option.isSome(env.firecrawlApiBaseUrl) ? { apiBaseUrl: env.firecrawlApiBaseUrl.value } : {}),
405
403
  },
406
- logging: overrides.logging ?? { level: env.logLevel as (typeof logLevelValues)[number] },
404
+ logging: overrides.logging ?? { level: z.enum(logLevelValues).parse(env.logLevel) },
407
405
  memory: {
408
406
  searchK: env.memorySearchK,
409
407
  rerankerStrategy: env.memoryRerankerStrategy as MemoryRerankerStrategy,
@@ -422,8 +420,4 @@ export function loadLotaRuntimeConfigFromEnv(
422
420
  )
423
421
  }
424
422
 
425
- export function getRuntimeConfig(): ResolvedLotaRuntimeConfig {
426
- return getCurrentRuntime().runSync(Effect.service(RuntimeConfigServiceTag))
427
- }
428
-
429
423
  export type { LotaThreadConfig }
@@ -6,19 +6,9 @@ import type {
6
6
  PlanSpecRecord,
7
7
  } from '@lota-sdk/shared'
8
8
  import type { ToolSet } from 'ai'
9
- import { Effect } from 'effect'
10
- import type { Context } from 'effect'
11
9
 
12
10
  import type { RecordIdRef } from '../db/record-id'
13
- import { getCurrentRuntime } from '../effect/runtime-ref'
14
- import {
15
- RuntimeAdaptersServiceTag,
16
- RuntimeWorkerExtensionsServiceTag,
17
- ToolProvidersServiceTag,
18
- TurnHooksServiceTag,
19
- } from '../effect/services'
20
11
  import type { ReadableUploadMetadata } from '../storage/attachment-types'
21
- import type { LotaRuntimeWorkerExtensions } from './runtime-worker-registry'
22
12
 
23
13
  export interface LotaRuntimeBackgroundCursor {
24
14
  createdAt: Date
@@ -201,36 +191,3 @@ export interface LotaRuntimeAdapters {
201
191
  connectPluginDatabases?: () => Promise<void>
202
192
  withWorkspaceMemoryLock?: <T>(workspaceId: string, fn: () => Promise<T>) => Promise<T>
203
193
  }
204
-
205
- function resolveFromRuntime<I, T>(tag: Context.Key<I, T>): T {
206
- return getCurrentRuntime().runSync(Effect.service(tag))
207
- }
208
-
209
- export function getRuntimeAdapters(): LotaRuntimeAdapters {
210
- return resolveFromRuntime(RuntimeAdaptersServiceTag)
211
- }
212
-
213
- export function getTurnHooks(): LotaRuntimeTurnHooks {
214
- return resolveFromRuntime(TurnHooksServiceTag)
215
- }
216
-
217
- export function getToolProviders(): ToolSet {
218
- return resolveFromRuntime(ToolProvidersServiceTag)
219
- }
220
-
221
- export function getConfiguredPluginDatabaseConnector(): (() => Promise<void>) | undefined {
222
- return getRuntimeAdapters().connectPluginDatabases
223
- }
224
-
225
- export function getExtraWorkers(): LotaRuntimeWorkerExtensions {
226
- return resolveFromRuntime(RuntimeWorkerExtensionsServiceTag)
227
- }
228
-
229
- export function withConfiguredWorkspaceMemoryLock<T>(workspaceId: string, fn: () => Promise<T>): Promise<T> {
230
- const adapter = getRuntimeAdapters().withWorkspaceMemoryLock
231
- if (!adapter) {
232
- return fn()
233
- }
234
-
235
- return adapter(workspaceId, fn)
236
- }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Plugin database connect/disconnect helpers and runtime disconnect assembly.
3
+ *
4
+ * The plugin connect/disconnect helpers run inside the supplied
5
+ * `ManagedRuntime` so spans, logging, and layer context stay consistent with
6
+ * the rest of the SDK runtime. `createRuntimeDisconnect` instead drives the
7
+ * shutdown sequence through the caller-supplied `runPromiseWithCurrentContext`
8
+ * (currently `Effect.runPromiseWith(Context.empty())`), which intentionally
9
+ * does not preserve the SDK runtime context — by then the runtime is being
10
+ * torn down.
11
+ */
12
+
13
+ import type { ManagedRuntime } from 'effect'
14
+ import { Effect } from 'effect'
15
+
16
+ import { clearAiGatewayRuntime } from '../ai-gateway/ai-gateway'
17
+ import { effectTryPromise } from '../effect/helpers'
18
+ import type { LotaPlugin } from './plugin-types'
19
+
20
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- ManagedRuntime is contravariant in R; `any` is the only valid wildcard
21
+ type SdkManagedRuntime = ManagedRuntime.ManagedRuntime<any, any>
22
+
23
+ function getPluginLifecycleServices(plugin: LotaPlugin): Record<string, unknown> {
24
+ return plugin.services
25
+ }
26
+
27
+ /**
28
+ * Build a plugin database connector that iterates the configured plugins and
29
+ * calls each `services.connectDatabase()` once, tracking completion in
30
+ * `connectedPluginDatabases` so repeated calls are idempotent.
31
+ */
32
+ export function createPluginDatabaseConnector(
33
+ managedRuntime: SdkManagedRuntime,
34
+ pluginRuntime: Record<string, LotaPlugin>,
35
+ connectedPluginDatabases: Set<string>,
36
+ ): () => Promise<void> {
37
+ return () =>
38
+ managedRuntime.runPromise(
39
+ Effect.gen(function* () {
40
+ for (const [pluginName, plugin] of Object.entries(pluginRuntime)) {
41
+ if (connectedPluginDatabases.has(pluginName)) {
42
+ continue
43
+ }
44
+
45
+ const services = getPluginLifecycleServices(plugin)
46
+ const connectDatabase = Reflect.get(services, 'connectDatabase')
47
+ if (typeof connectDatabase !== 'function') {
48
+ continue
49
+ }
50
+
51
+ const connectDatabaseFn = connectDatabase as (this: typeof services) => Promise<void>
52
+ yield* effectTryPromise(() => connectDatabaseFn.call(services))
53
+ connectedPluginDatabases.add(pluginName)
54
+ }
55
+ }),
56
+ )
57
+ }
58
+
59
+ /**
60
+ * Build a plugin database disconnector. Clears `connectedPluginDatabases`
61
+ * entries regardless of whether the plugin provides a `disconnectDatabase`
62
+ * hook, so the set stays in sync with actual connection state.
63
+ */
64
+ export function createPluginDatabaseDisconnector(
65
+ managedRuntime: SdkManagedRuntime,
66
+ pluginRuntime: Record<string, LotaPlugin>,
67
+ connectedPluginDatabases: Set<string>,
68
+ ): () => Promise<void> {
69
+ return () =>
70
+ managedRuntime.runPromise(
71
+ Effect.gen(function* () {
72
+ for (const [pluginName, plugin] of Object.entries(pluginRuntime)) {
73
+ if (!connectedPluginDatabases.has(pluginName)) {
74
+ continue
75
+ }
76
+
77
+ const services = getPluginLifecycleServices(plugin)
78
+ const disconnectDatabase = Reflect.get(services, 'disconnectDatabase')
79
+ if (typeof disconnectDatabase !== 'function') {
80
+ connectedPluginDatabases.delete(pluginName)
81
+ continue
82
+ }
83
+
84
+ const disconnectDatabaseFn = disconnectDatabase as (this: typeof services) => Promise<void>
85
+ yield* effectTryPromise(() => disconnectDatabaseFn.call(services))
86
+ connectedPluginDatabases.delete(pluginName)
87
+ }
88
+ }),
89
+ )
90
+ }
91
+
92
+ interface CreateDisconnectInput {
93
+ managedRuntime: SdkManagedRuntime
94
+ runPromiseWithCurrentContext: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>
95
+ socialChatShutdown: () => Promise<void>
96
+ disconnectPluginDatabases: () => Promise<void>
97
+ }
98
+
99
+ /**
100
+ * Compose the runtime `disconnect()` function. The returned function is
101
+ * idempotent: the first call starts the shutdown sequence, subsequent calls
102
+ * return the same in-flight promise.
103
+ */
104
+ export function createRuntimeDisconnect(input: CreateDisconnectInput): () => Promise<void> {
105
+ const { managedRuntime, runPromiseWithCurrentContext, socialChatShutdown, disconnectPluginDatabases } = input
106
+
107
+ let disconnectPromise: Promise<void> | null = null
108
+
109
+ return () => {
110
+ if (disconnectPromise) {
111
+ return disconnectPromise
112
+ }
113
+
114
+ disconnectPromise = runPromiseWithCurrentContext(
115
+ Effect.gen(function* () {
116
+ yield* Effect.ignore(effectTryPromise(() => socialChatShutdown()))
117
+ yield* Effect.ignore(effectTryPromise(() => disconnectPluginDatabases()))
118
+ yield* effectTryPromise(() => managedRuntime.dispose())
119
+ yield* Effect.sync(() => clearAiGatewayRuntime())
120
+ }),
121
+ )
122
+ return disconnectPromise
123
+ }
124
+ }