@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,26 +1,35 @@
1
1
  import { Queue, Worker } from 'bullmq'
2
2
  import type { Job, JobsOptions, QueueOptions, WorkerOptions } from 'bullmq'
3
- import { Effect } from 'effect'
3
+ import { Effect, Schema } from 'effect'
4
4
  import type IORedis from 'ioredis'
5
5
 
6
6
  import type { LotaLogger } from '../config/logger'
7
7
  import { serverLogger } from '../config/logger'
8
- import { getCurrentRuntime } from '../effect/runtime-ref'
9
- import { RedisServiceTag } from '../effect/services'
8
+ import type { TrackedBullJobLike } from '../services/queue-job.service'
10
9
  import {
11
10
  attachWorkerEvents,
12
11
  createTracedWorkerProcessor,
13
12
  createWorkerShutdown,
14
13
  DEFAULT_JOB_RETENTION,
15
14
  registerShutdownSignals,
16
- getQueueJobService,
17
15
  } from '../workers/worker-utils'
18
- import type { WorkerHandle } from '../workers/worker-utils'
16
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
19
17
 
20
- function getDefaultQueueConnectionProvider(): () => IORedis {
21
- const redis = getCurrentRuntime().runSync(Effect.service(RedisServiceTag))
22
- return () => redis.getConnectionForBullMQ()
23
- }
18
+ class QueueFactoryError extends Schema.TaggedErrorClass<QueueFactoryError>()('@lota-sdk/core/QueueFactoryError', {
19
+ message: Schema.String,
20
+ cause: Schema.optional(Schema.Defect),
21
+ }) {}
22
+
23
+ type QueueShape<TJob> = Queue<TJob, unknown, string, TJob, unknown, string>
24
+ type QueueMethod = 'add' | 'close' | 'remove' | 'removeDeduplicationKey' | 'removeJobScheduler'
25
+
26
+ const queueMethodsThatWaitForClose = new Set<QueueMethod>([
27
+ 'add',
28
+ 'close',
29
+ 'remove',
30
+ 'removeDeduplicationKey',
31
+ 'removeJobScheduler',
32
+ ])
24
33
 
25
34
  interface QueueFactoryConfigBase {
26
35
  name: string
@@ -32,7 +41,8 @@ interface QueueFactoryConfigBase {
32
41
  stalledInterval?: number
33
42
  maxStalledCount?: number
34
43
  defaultJobOptions?: JobsOptions
35
- connectionProvider?: () => IORedis
44
+ connectionProvider: () => IORedis
45
+ queueJobService: QueueJobService
36
46
  }
37
47
 
38
48
  interface QueueFactoryConfigInline<TJob> extends QueueFactoryConfigBase {
@@ -48,32 +58,73 @@ interface QueueFactoryConfigFile extends QueueFactoryConfigBase {
48
58
 
49
59
  export type QueueFactoryConfig<TJob> = QueueFactoryConfigInline<TJob> | QueueFactoryConfigFile
50
60
 
61
+ interface QueueFactoryWithDepsConfig<TJob, TDeps> extends QueueFactoryConfigBase {
62
+ prepare?: (deps: TDeps, job: Job<TJob>) => Promise<void>
63
+ processor: (deps: TDeps, job: Job<TJob>) => Promise<unknown>
64
+ }
65
+
66
+ interface QueueWorkerConfigInline<TJob> {
67
+ prepare?: (job: Job<TJob>) => Promise<void>
68
+ processor: (job: Job<TJob>) => Promise<unknown>
69
+ processorPath?: never
70
+ }
71
+
72
+ interface QueueWorkerConfigFile {
73
+ processor?: never
74
+ processorPath: string
75
+ }
76
+
77
+ type QueueWorkerConfig<TJob> = QueueWorkerConfigInline<TJob> | QueueWorkerConfigFile
78
+
79
+ export interface QueueFactoryWorkerOptions {
80
+ registerSignals?: boolean
81
+ connectionProvider?: () => IORedis
82
+ }
83
+
51
84
  export interface QueueFactory<TJob> {
52
- getQueue: () => Queue<TJob, unknown, string, TJob, unknown, string>
85
+ getQueue: () => QueueShape<TJob>
53
86
  enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
54
- startWorker: (options?: { registerSignals?: boolean; connectionProvider?: () => IORedis }) => WorkerHandle
87
+ startWorker: (options?: QueueFactoryWorkerOptions) => WorkerHandle
55
88
  }
56
89
 
57
- export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): QueueFactory<TJob> {
58
- type QueueShape = Queue<TJob, unknown, string, TJob, unknown, string>
59
- type QueueMethod = 'add' | 'close' | 'remove' | 'removeDeduplicationKey' | 'removeJobScheduler'
60
- const queueMethodsThatWaitForClose = new Set<QueueMethod>([
61
- 'add',
62
- 'close',
63
- 'remove',
64
- 'removeDeduplicationKey',
65
- 'removeJobScheduler',
66
- ])
67
-
68
- let state: {
69
- queue: QueueShape | null
70
- rawQueue: QueueShape | null
90
+ export interface QueueFactoryWithDeps<TJob, TDeps> {
91
+ getQueue: () => QueueShape<TJob>
92
+ enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
93
+ startWorker: (options: QueueFactoryWorkerOptions & { deps: TDeps }) => WorkerHandle
94
+ }
95
+
96
+ export function recordEnqueuedJobMetadata(params: {
97
+ queueName: string
98
+ job: Omit<TrackedBullJobLike, 'queueName'>
99
+ queueJobService: QueueJobService
100
+ logger?: LotaLogger
101
+ }): Effect.Effect<string | undefined> {
102
+ const logger = params.logger ?? serverLogger
103
+ return params.queueJobService.recordEnqueued({ queueName: params.queueName, ...params.job }).pipe(
104
+ Effect.tapError((error) =>
105
+ Effect.sync(() => {
106
+ logger.error`Failed to persist queued job metadata (queue=${params.queueName}, job=${params.job.id}): ${error}`
107
+ }),
108
+ ),
109
+ Effect.orElseSucceed(() => undefined),
110
+ )
111
+ }
112
+
113
+ function createQueueFactoryRuntime<TJob>(config: QueueFactoryConfigBase): {
114
+ getQueue: () => QueueShape<TJob>
115
+ enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
116
+ startWorker: (workerConfig: QueueWorkerConfig<TJob>, options?: QueueFactoryWorkerOptions) => WorkerHandle
117
+ } {
118
+ type State = {
119
+ queue: QueueShape<TJob> | null
120
+ rawQueue: QueueShape<TJob> | null
71
121
  connection: IORedis | null
72
122
  pendingClose: Promise<void> | null
73
- } = { queue: null, rawQueue: null, connection: null, pendingClose: null }
123
+ }
124
+
125
+ let state: State = { queue: null, rawQueue: null, connection: null, pendingClose: null }
74
126
 
75
- const resolveConnectionProvider = (): (() => IORedis) =>
76
- config.connectionProvider ?? getDefaultQueueConnectionProvider()
127
+ const resolveConnectionProvider = (): (() => IORedis) => config.connectionProvider
77
128
 
78
129
  const getConnection = (): IORedis => resolveConnectionProvider()()
79
130
 
@@ -90,7 +141,7 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
90
141
  })
91
142
  }
92
143
 
93
- const wrapQueue = (queue: QueueShape): QueueShape =>
144
+ const wrapQueue = (queue: QueueShape<TJob>): QueueShape<TJob> =>
94
145
  new Proxy(queue, {
95
146
  get(target, property, receiver) {
96
147
  if (typeof property !== 'string') {
@@ -109,7 +160,7 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
109
160
  },
110
161
  })
111
162
 
112
- const getQueue = (): QueueShape => {
163
+ const getQueue = (): QueueShape<TJob> => {
113
164
  const connection = getConnection()
114
165
  const isStale =
115
166
  state.rawQueue === null ||
@@ -146,36 +197,26 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
146
197
  return wrappedQueue
147
198
  }
148
199
 
149
- const jobName = config.jobName
150
- const toData = (job: TJob) => job
151
-
152
200
  const enqueue = (job: TJob, options?: JobsOptions): Promise<void> =>
153
201
  Effect.runPromise(
154
202
  Effect.gen(function* () {
155
- const queuedJob = yield* Effect.tryPromise(() => getQueue().add(jobName, toData(job), options))
156
- yield* getQueueJobService()
157
- .recordEnqueued({
158
- queueName: config.name,
159
- id: queuedJob.id,
160
- name: queuedJob.name,
161
- data: queuedJob.data,
162
- opts: queuedJob.opts,
163
- attemptsMade: queuedJob.attemptsMade,
164
- timestamp: queuedJob.timestamp,
165
- })
166
- .pipe(
167
- Effect.tapError((error) =>
168
- Effect.sync(() => {
169
- serverLogger.error`Failed to persist queued job metadata (queue=${config.name}, job=${queuedJob.id}): ${error}`
170
- }),
171
- ),
172
- Effect.orElseSucceed(() => undefined),
173
- )
203
+ const queuedJob = yield* Effect.tryPromise({
204
+ try: () => getQueue().add(config.jobName, job, options),
205
+ catch: (cause) =>
206
+ new QueueFactoryError({ message: `Failed to enqueue job on queue "${config.name}".`, cause }),
207
+ })
208
+ yield* recordEnqueuedJobMetadata({
209
+ queueName: config.name,
210
+ job: queuedJob,
211
+ queueJobService: config.queueJobService,
212
+ logger: config.logger,
213
+ })
174
214
  }),
175
215
  )
176
216
 
177
217
  const startWorker = (
178
- options: { registerSignals?: boolean; connectionProvider?: () => IORedis } = {},
218
+ workerConfig: QueueWorkerConfig<TJob>,
219
+ options: QueueFactoryWorkerOptions = {},
179
220
  ): WorkerHandle => {
180
221
  const { registerSignals = import.meta.main, connectionProvider } = options
181
222
  const logger = config.logger ?? serverLogger
@@ -188,23 +229,33 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
188
229
  ...(config.maxStalledCount !== undefined ? { maxStalledCount: config.maxStalledCount } : {}),
189
230
  }
190
231
 
191
- const worker = config.processorPath
192
- ? new Worker(config.name, config.processorPath, workerOptions)
232
+ const worker = workerConfig.processorPath
233
+ ? new Worker(config.name, workerConfig.processorPath, workerOptions)
193
234
  : new Worker(
194
235
  config.name,
195
- createTracedWorkerProcessor(config.name, (job) =>
196
- Effect.runPromise(
197
- Effect.gen(function* () {
198
- const inlineConfig = config as QueueFactoryConfigInline<TJob>
199
- const typedJob = job as Job<TJob>
200
- const prepare = inlineConfig.prepare
201
- if (prepare) {
202
- yield* Effect.tryPromise(() => prepare(typedJob))
203
- }
204
- const processor = inlineConfig.processor
205
- return yield* Effect.tryPromise(() => processor(typedJob))
206
- }),
207
- ),
236
+ createTracedWorkerProcessor(
237
+ config.name,
238
+ (job) =>
239
+ Effect.runPromise(
240
+ Effect.gen(function* () {
241
+ const inlineWorkerConfig = workerConfig as QueueWorkerConfigInline<TJob>
242
+ const typedJob = job as Job<TJob>
243
+ const prepare = inlineWorkerConfig.prepare
244
+ if (prepare) {
245
+ yield* Effect.tryPromise({
246
+ try: () => prepare(typedJob),
247
+ catch: (cause) =>
248
+ new QueueFactoryError({ message: `Worker prepare failed for queue "${config.name}".`, cause }),
249
+ })
250
+ }
251
+ return yield* Effect.tryPromise({
252
+ try: () => inlineWorkerConfig.processor(typedJob),
253
+ catch: (cause) =>
254
+ new QueueFactoryError({ message: `Worker processor failed for queue "${config.name}".`, cause }),
255
+ })
256
+ }),
257
+ ),
258
+ config.queueJobService,
208
259
  ),
209
260
  workerOptions,
210
261
  )
@@ -222,3 +273,29 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
222
273
 
223
274
  return { getQueue, enqueue, startWorker }
224
275
  }
276
+
277
+ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): QueueFactory<TJob> {
278
+ const runtime = createQueueFactoryRuntime<TJob>(config)
279
+ return {
280
+ getQueue: runtime.getQueue,
281
+ enqueue: runtime.enqueue,
282
+ startWorker: (options) => runtime.startWorker(config, options),
283
+ }
284
+ }
285
+
286
+ export function createQueueFactoryWithDeps<TJob, TDeps>(
287
+ config: QueueFactoryWithDepsConfig<TJob, TDeps>,
288
+ ): QueueFactoryWithDeps<TJob, TDeps> {
289
+ const runtime = createQueueFactoryRuntime<TJob>(config)
290
+ const prepare = config.prepare
291
+
292
+ return {
293
+ getQueue: runtime.getQueue,
294
+ enqueue: runtime.enqueue,
295
+ startWorker: ({ deps, ...options }) =>
296
+ runtime.startWorker(
297
+ { prepare: prepare ? (job) => prepare(deps, job) : undefined, processor: (job) => config.processor(deps, job) },
298
+ options,
299
+ ),
300
+ }
301
+ }
@@ -0,0 +1,61 @@
1
+ import { Context, Effect, Layer } from 'effect'
2
+ import type IORedis from 'ioredis'
3
+
4
+ import { RedisServiceTag } from '../effect/services'
5
+ import { QueueJobServiceTag } from '../services/queue-job.service'
6
+ import { makeAutonomousJobQueueRuntime } from './autonomous-job.queue'
7
+ import type { AutonomousJobQueueRuntime } from './autonomous-job.queue'
8
+ import { makeContextCompactionQueueRuntime } from './context-compaction.queue'
9
+ import type { ContextCompactionQueueRuntime } from './context-compaction.queue'
10
+ import { makeDelayedNodePromotionQueueRuntime } from './delayed-node-promotion.queue'
11
+ import type { DelayedNodePromotionQueueRuntime } from './delayed-node-promotion.queue'
12
+ import { makeMemoryConsolidationQueueRuntime } from './memory-consolidation.queue'
13
+ import type { MemoryConsolidationQueueRuntime } from './memory-consolidation.queue'
14
+ import { makeOrganizationLearningQueueRuntime } from './organization-learning.queue'
15
+ import type { OrganizationLearningQueueRuntime } from './organization-learning.queue'
16
+ import { makePlanAgentHeartbeatQueueRuntime } from './plan-agent-heartbeat.queue'
17
+ import type { PlanAgentHeartbeatQueueRuntime } from './plan-agent-heartbeat.queue'
18
+ import { makePlanSchedulerQueueRuntime } from './plan-scheduler.queue'
19
+ import type { PlanSchedulerQueueRuntime } from './plan-scheduler.queue'
20
+ import { makePostChatMemoryQueueRuntime } from './post-chat-memory.queue'
21
+ import type { PostChatMemoryQueueRuntime } from './post-chat-memory.queue'
22
+ import { makeTitleGenerationQueueRuntime } from './title-generation.queue'
23
+ import type { TitleGenerationQueueRuntime } from './title-generation.queue'
24
+
25
+ export interface LotaQueuesRuntime {
26
+ autonomousJob: AutonomousJobQueueRuntime
27
+ contextCompaction: ContextCompactionQueueRuntime
28
+ delayedNodePromotion: DelayedNodePromotionQueueRuntime
29
+ memoryConsolidation: MemoryConsolidationQueueRuntime
30
+ organizationLearning: OrganizationLearningQueueRuntime
31
+ planAgentHeartbeat: PlanAgentHeartbeatQueueRuntime
32
+ planScheduler: PlanSchedulerQueueRuntime
33
+ postChatMemory: PostChatMemoryQueueRuntime
34
+ titleGeneration: TitleGenerationQueueRuntime
35
+ }
36
+
37
+ export class LotaQueuesServiceTag extends Context.Service<LotaQueuesServiceTag, LotaQueuesRuntime>()(
38
+ '@lota-sdk/core/LotaQueuesService',
39
+ ) {}
40
+
41
+ export const LotaQueuesLive = Layer.effect(
42
+ LotaQueuesServiceTag,
43
+ Effect.gen(function* () {
44
+ const redis = yield* RedisServiceTag
45
+ const queueJobService = yield* QueueJobServiceTag
46
+ const connectionProvider: () => IORedis = () => redis.getConnectionForBullMQ()
47
+ const baseDeps = { connectionProvider, queueJobService }
48
+
49
+ return LotaQueuesServiceTag.of({
50
+ autonomousJob: makeAutonomousJobQueueRuntime(baseDeps),
51
+ contextCompaction: makeContextCompactionQueueRuntime(baseDeps),
52
+ delayedNodePromotion: makeDelayedNodePromotionQueueRuntime(baseDeps),
53
+ memoryConsolidation: makeMemoryConsolidationQueueRuntime(baseDeps),
54
+ organizationLearning: makeOrganizationLearningQueueRuntime(baseDeps),
55
+ planAgentHeartbeat: makePlanAgentHeartbeatQueueRuntime(baseDeps),
56
+ planScheduler: makePlanSchedulerQueueRuntime(baseDeps),
57
+ postChatMemory: makePostChatMemoryQueueRuntime(baseDeps),
58
+ titleGeneration: makeTitleGenerationQueueRuntime(baseDeps),
59
+ })
60
+ }),
61
+ )
@@ -4,11 +4,12 @@ import type { Context } from 'effect'
4
4
  import type IORedis from 'ioredis'
5
5
 
6
6
  import { ensureRecordId } from '../db/record-id'
7
- import { ConfigurationError } from '../effect/errors'
8
7
  import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
8
+ import { QueueJobServiceTag } from '../services/queue-job.service'
9
9
  import { RecentActivityTitleServiceTag } from '../services/recent-activity-title.service'
10
10
  import { ThreadTitleServiceTag } from '../services/thread/thread-title.service'
11
- import { createQueueFactory } from './queue-factory'
11
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
12
+ import { createQueueFactoryWithDeps } from './queue-factory'
12
13
  import { runStandaloneQueueWorker } from './standalone-worker'
13
14
 
14
15
  export const TITLE_GENERATION_QUEUE = 'title-generation'
@@ -30,24 +31,14 @@ interface RecentActivityTitleRefinementJob {
30
31
 
31
32
  type TitleGenerationJob = ThreadTitleGenerationJob | RecentActivityTitleRefinementJob
32
33
 
33
- interface TitleGenerationQueueDeps {
34
+ export interface TitleGenerationWorkerDeps {
34
35
  databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
35
36
  threadTitleService: Context.Service.Shape<typeof ThreadTitleServiceTag>
36
37
  recentActivityTitleService: Context.Service.Shape<typeof RecentActivityTitleServiceTag>
37
38
  }
38
39
 
39
- let _deps: TitleGenerationQueueDeps | null = null
40
- function getDeps(): TitleGenerationQueueDeps {
41
- if (!_deps)
42
- throw new ConfigurationError({
43
- message: 'Title generation queue is not configured. Initialize the runtime before starting the worker.',
44
- key: 'queue-deps',
45
- })
46
- return _deps
47
- }
48
-
49
- function processTitleGenerationJob(job: Job<TitleGenerationJob>): Promise<void> {
50
- const { threadTitleService, recentActivityTitleService } = getDeps()
40
+ function processTitleGenerationJob(deps: TitleGenerationWorkerDeps, job: Job<TitleGenerationJob>): Promise<void> {
41
+ const { threadTitleService, recentActivityTitleService } = deps
51
42
  if (job.data.kind === 'thread-title') {
52
43
  return Effect.runPromise(
53
44
  Effect.asVoid(threadTitleService.generateAndPersistTitle(ensureRecordId(job.data.threadId), job.data.sourceText)),
@@ -57,44 +48,53 @@ function processTitleGenerationJob(job: Job<TitleGenerationJob>): Promise<void>
57
48
  return Effect.runPromise(Effect.asVoid(recentActivityTitleService.refineRecentActivityTitle(job.data.activityId)))
58
49
  }
59
50
 
60
- const titleGeneration = createQueueFactory<TitleGenerationJob>({
61
- name: TITLE_GENERATION_QUEUE,
62
- displayName: 'Title generation',
63
- jobName: 'title-generation',
64
- concurrency: 10,
65
- lockDuration: 300_000,
66
- defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
67
- prepare: () => getDeps().databaseService.connect(),
68
- processor: processTitleGenerationJob,
69
- })
70
-
71
- export function enqueueThreadTitleGeneration(job: Omit<ThreadTitleGenerationJob, 'kind'>) {
72
- return titleGeneration.enqueue({ kind: 'thread-title', ...job }, { jobId: `thread-title:${job.threadId}` })
51
+ export interface TitleGenerationQueueRuntime {
52
+ enqueueThreadTitleGeneration(job: Omit<ThreadTitleGenerationJob, 'kind'>): Promise<void>
53
+ enqueueRecentActivityTitleRefinement(job: Omit<RecentActivityTitleRefinementJob, 'kind'>): Promise<void>
54
+ startWorker(options: { registerSignals?: boolean; deps: TitleGenerationWorkerDeps }): WorkerHandle
73
55
  }
74
56
 
75
- export function enqueueRecentActivityTitleRefinement(job: Omit<RecentActivityTitleRefinementJob, 'kind'>) {
76
- return titleGeneration.enqueue(
77
- { kind: 'recent-activity-title', ...job },
78
- { jobId: `recent-activity-title:${job.activityId}` },
79
- )
57
+ interface MakeTitleGenerationQueueRuntimeParams {
58
+ connectionProvider: () => IORedis
59
+ queueJobService: QueueJobService
80
60
  }
81
61
 
82
- export function startTitleGenerationWorker(options: {
83
- registerSignals?: boolean
84
- connectionProvider: () => IORedis
85
- deps: TitleGenerationQueueDeps
86
- }): ReturnType<typeof titleGeneration.startWorker> {
87
- _deps = options.deps
88
- return titleGeneration.startWorker({
89
- registerSignals: options.registerSignals,
90
- connectionProvider: options.connectionProvider,
62
+ export function makeTitleGenerationQueueRuntime(
63
+ params: MakeTitleGenerationQueueRuntimeParams,
64
+ ): TitleGenerationQueueRuntime {
65
+ const { connectionProvider, queueJobService } = params
66
+
67
+ const queue = createQueueFactoryWithDeps<TitleGenerationJob, TitleGenerationWorkerDeps>({
68
+ name: TITLE_GENERATION_QUEUE,
69
+ displayName: 'Title generation',
70
+ jobName: 'title-generation',
71
+ concurrency: 10,
72
+ lockDuration: 300_000,
73
+ defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
74
+ connectionProvider,
75
+ queueJobService,
76
+ prepare: ({ databaseService }) => Effect.runPromise(databaseService.connect()),
77
+ processor: processTitleGenerationJob,
91
78
  })
79
+
80
+ return {
81
+ enqueueThreadTitleGeneration: (job) =>
82
+ queue.enqueue({ kind: 'thread-title', ...job }, { jobId: `thread-title:${job.threadId}` }),
83
+ enqueueRecentActivityTitleRefinement: (job) =>
84
+ queue.enqueue({ kind: 'recent-activity-title', ...job }, { jobId: `recent-activity-title:${job.activityId}` }),
85
+ startWorker: (options) =>
86
+ queue.startWorker({ deps: options.deps, registerSignals: options.registerSignals, connectionProvider }),
87
+ }
92
88
  }
93
89
 
94
90
  runStandaloneQueueWorker((runtime) => {
95
91
  const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
96
- startTitleGenerationWorker({
97
- connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
92
+ const redis = resolve(RedisServiceTag)
93
+ const titleGenerationQueue = makeTitleGenerationQueueRuntime({
94
+ connectionProvider: () => redis.getConnectionForBullMQ(),
95
+ queueJobService: resolve(QueueJobServiceTag),
96
+ })
97
+ titleGenerationQueue.startWorker({
98
98
  deps: {
99
99
  databaseService: resolve(DatabaseServiceTag),
100
100
  threadTitleService: resolve(ThreadTitleServiceTag),