@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
@@ -1,11 +1,18 @@
1
1
  import type { Job } from 'bullmq'
2
+ import { Schema, Effect } from 'effect'
3
+ import type { Context } from 'effect'
4
+ import type IORedis from 'ioredis'
2
5
 
3
6
  import { serverLogger } from '../config/logger'
4
- import { databaseService } from '../db/service'
5
- import { planDeadlineService } from '../services/plan-deadline.service'
6
- import { planSchedulerService } from '../services/plan-scheduler.service'
7
+ import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
8
+ import { PlanCycleServiceTag } from '../services/plan/plan-cycle.service'
9
+ import { PlanDeadlineServiceTag } from '../services/plan/plan-deadline.service'
10
+ import { PlanExecutorServiceTag } from '../services/plan/plan-executor.service'
11
+ import { PlanSchedulerServiceTag } from '../services/plan/plan-scheduler.service'
12
+ import { nowEpochMillis } from '../utils/date-time'
7
13
  import type { WorkerHandle } from '../workers/worker-utils'
8
- import { createQueueFactory } from './queue-factory'
14
+ import { createQueueFactoryWithDeps } from './queue-factory'
15
+ import { runStandaloneQueueWorker } from './standalone-worker'
9
16
 
10
17
  export interface PlanSchedulerFireJob {
11
18
  type: 'fire-schedule'
@@ -21,50 +28,75 @@ export type PlanSchedulerJob = PlanSchedulerFireJob | PlanSchedulerDeadlineJob
21
28
 
22
29
  export const PLAN_SCHEDULER_QUEUE = 'plan-scheduler'
23
30
 
24
- async function processPlanSchedulerJob(job: Job<PlanSchedulerJob>): Promise<void> {
25
- await databaseService.connect()
31
+ interface PlanSchedulerQueueDeps {
32
+ databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
33
+ planSchedulerService: Context.Service.Shape<typeof PlanSchedulerServiceTag>
34
+ planDeadlineService: Context.Service.Shape<typeof PlanDeadlineServiceTag>
35
+ planExecutorService: Context.Service.Shape<typeof PlanExecutorServiceTag>
36
+ planCycleService: Context.Service.Shape<typeof PlanCycleServiceTag>
37
+ }
38
+
39
+ class PlanSchedulerQueueError extends Schema.TaggedErrorClass<PlanSchedulerQueueError>()('PlanSchedulerQueueError', {
40
+ stage: Schema.Literals(['remove-schedule-fire-job', 'recover-active-schedules', 'recover-deadline-checks']),
41
+ message: Schema.String,
42
+ cause: Schema.Defect,
43
+ }) {}
44
+
45
+ function toPlanSchedulerQueueError(stage: PlanSchedulerQueueError['stage'], cause: unknown): PlanSchedulerQueueError {
46
+ return new PlanSchedulerQueueError({ stage, message: cause instanceof Error ? cause.message : String(cause), cause })
47
+ }
26
48
 
49
+ function processPlanSchedulerJob(deps: PlanSchedulerQueueDeps, job: Job<PlanSchedulerJob>): Promise<void> {
50
+ const { planSchedulerService, planDeadlineService, planExecutorService, planCycleService } = deps
27
51
  switch (job.data.type) {
28
52
  case 'fire-schedule':
29
- await planSchedulerService.fireScheduleById(job.data.scheduleId)
30
- break
53
+ return Effect.runPromise(
54
+ Effect.asVoid(
55
+ planSchedulerService.fireScheduleById(job.data.scheduleId, {
56
+ promoteDelayedNode: (params) => planExecutorService.promoteDelayedNode(params),
57
+ advanceCycle: (cycleId) => planCycleService.advanceCycle(cycleId),
58
+ recoverDeadlineChecks: () => Effect.runPromise(Effect.asVoid(planDeadlineService.recoverDeadlineChecks())),
59
+ }),
60
+ ),
61
+ )
31
62
  case 'check-deadlines':
32
- await planDeadlineService.checkDeadlines()
33
- break
63
+ return Effect.runPromise(Effect.asVoid(planDeadlineService.checkDeadlines()))
34
64
  }
35
65
  }
36
66
 
37
- const planScheduler = createQueueFactory<PlanSchedulerJob>({
67
+ const planScheduler = createQueueFactoryWithDeps<PlanSchedulerJob, PlanSchedulerQueueDeps>({
38
68
  name: PLAN_SCHEDULER_QUEUE,
39
69
  displayName: 'Plan scheduler',
40
70
  jobName: 'plan-scheduler-job',
41
71
  concurrency: 1,
42
72
  defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 5000 } },
73
+ prepare: ({ databaseService }) => databaseService.connect(),
43
74
  processor: processPlanSchedulerJob,
44
75
  })
45
76
 
46
77
  /** Enqueue a delayed job that fires a specific schedule at its nextFireAt time. */
47
- export async function enqueueScheduleFire(scheduleId: string, delayMs: number): Promise<void> {
48
- await planScheduler.enqueue(
78
+ export function enqueueScheduleFire(scheduleId: string, delayMs: number): Promise<void> {
79
+ return planScheduler.enqueue(
49
80
  { type: 'fire-schedule', scheduleId },
50
81
  { delay: Math.max(0, delayMs), jobId: `schedule:${scheduleId}`, removeOnComplete: true, removeOnFail: 50 },
51
82
  )
52
83
  }
53
84
 
54
85
  /** Remove a pending fire job for a schedule (on cancel/pause/complete). */
55
- export async function removeScheduleFireJob(scheduleId: string): Promise<void> {
56
- try {
57
- await planScheduler.getQueue().remove(`schedule:${scheduleId}`)
58
- } catch {
59
- // Job may not exist (already fired or never enqueued) — safe to ignore
60
- }
86
+ export function removeScheduleFireJob(scheduleId: string): Promise<void> {
87
+ return Effect.runPromise(
88
+ Effect.tryPromise({
89
+ try: () => planScheduler.getQueue().remove(`schedule:${scheduleId}`),
90
+ catch: (cause) => toPlanSchedulerQueueError('remove-schedule-fire-job', cause),
91
+ }).pipe(Effect.asVoid),
92
+ )
61
93
  }
62
94
 
63
95
  const DEADLINE_CHECK_JOB_PREFIX = 'deadline-check'
64
96
 
65
- export async function enqueueDeadlineCheck(scheduledFor: Date): Promise<void> {
66
- const delay = Math.max(0, scheduledFor.getTime() - Date.now())
67
- await planScheduler.enqueue(
97
+ export function enqueueDeadlineCheck(scheduledFor: Date): Promise<void> {
98
+ const delay = Math.max(0, scheduledFor.getTime() - nowEpochMillis())
99
+ return planScheduler.enqueue(
68
100
  { type: 'check-deadlines', scheduledFor: scheduledFor.toISOString() },
69
101
  {
70
102
  delay,
@@ -75,23 +107,55 @@ export async function enqueueDeadlineCheck(scheduledFor: Date): Promise<void> {
75
107
  )
76
108
  }
77
109
 
78
- export function startPlanSchedulerWorker(options: { registerSignals?: boolean } = {}): WorkerHandle {
79
- const handle = planScheduler.startWorker(options)
110
+ export function startPlanSchedulerWorker(options: {
111
+ registerSignals?: boolean
112
+ connectionProvider: () => IORedis
113
+ deps: PlanSchedulerQueueDeps
114
+ }): WorkerHandle {
115
+ const handle = planScheduler.startWorker({
116
+ deps: options.deps,
117
+ registerSignals: options.registerSignals,
118
+ connectionProvider: options.connectionProvider,
119
+ })
80
120
 
81
121
  // Recover active schedules on startup
82
- planSchedulerService.recoverActiveSchedules().catch((err: unknown) => {
83
- serverLogger.error`Plan scheduler startup recovery failed: ${err}`
84
- })
122
+ void Effect.runFork(
123
+ options.deps.planSchedulerService.recoverActiveSchedules().pipe(
124
+ Effect.mapError((cause) => toPlanSchedulerQueueError('recover-active-schedules', cause)),
125
+ Effect.catchTag('PlanSchedulerQueueError', (error) =>
126
+ Effect.sync(() => {
127
+ serverLogger.error`Plan scheduler startup recovery failed: ${error.message}`
128
+ }),
129
+ ),
130
+ ),
131
+ )
85
132
 
86
133
  // Seed deadline checks from current runtime state. Subsequent checks are
87
134
  // re-seeded by the queue job after each sweep and by schedule fire events.
88
- planDeadlineService.recoverDeadlineChecks().catch((err: unknown) => {
89
- serverLogger.error`Plan deadline recovery failed: ${err}`
90
- })
135
+ void Effect.runFork(
136
+ options.deps.planDeadlineService.recoverDeadlineChecks().pipe(
137
+ Effect.mapError((cause) => toPlanSchedulerQueueError('recover-deadline-checks', cause)),
138
+ Effect.catchTag('PlanSchedulerQueueError', (error) =>
139
+ Effect.sync(() => {
140
+ serverLogger.error`Plan deadline recovery failed: ${error.message}`
141
+ }),
142
+ ),
143
+ ),
144
+ )
91
145
 
92
146
  return handle
93
147
  }
94
148
 
95
- if (import.meta.main) {
96
- startPlanSchedulerWorker()
97
- }
149
+ runStandaloneQueueWorker((runtime) => {
150
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
151
+ startPlanSchedulerWorker({
152
+ connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
153
+ deps: {
154
+ databaseService: resolve(DatabaseServiceTag),
155
+ planSchedulerService: resolve(PlanSchedulerServiceTag),
156
+ planDeadlineService: resolve(PlanDeadlineServiceTag),
157
+ planExecutorService: resolve(PlanExecutorServiceTag),
158
+ planCycleService: resolve(PlanCycleServiceTag),
159
+ },
160
+ })
161
+ })
@@ -1,9 +1,13 @@
1
- type OrganizationOnboardStatus = string
2
1
  import type { Job } from 'bullmq'
2
+ import { Effect } from 'effect'
3
+ import type { Context } from 'effect'
4
+ import type IORedis from 'ioredis'
3
5
 
4
- import { databaseService } from '../db/service'
5
- import { memoryService } from '../services/memory.service'
6
- import { createQueueFactory } from './queue-factory'
6
+ import type { MaybeAwaitableService } from '../effect/awaitable-effect'
7
+ import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
8
+ import { MemoryServiceTag } from '../services/memory/memory.service'
9
+ import { createQueueFactoryWithDeps } from './queue-factory'
10
+ import { runStandaloneQueueWorker } from './standalone-worker'
7
11
 
8
12
  interface PostChatMemoryMessage {
9
13
  role: 'user' | 'agent'
@@ -17,7 +21,7 @@ interface PostChatMemoryExtractionJob {
17
21
  sourceId: string
18
22
  source?: string
19
23
  sourceMetadata?: Record<string, unknown>
20
- onboardStatus?: OrganizationOnboardStatus
24
+ onboardStatus?: string
21
25
  userMessage: string
22
26
  historyMessages: PostChatMemoryMessage[]
23
27
  agentMessages: Array<{ content: string; agentName?: string }>
@@ -25,9 +29,13 @@ interface PostChatMemoryExtractionJob {
25
29
  attachmentContext?: string
26
30
  }
27
31
 
28
- async function processPostChatMemoryJob(job: Job<PostChatMemoryExtractionJob>): Promise<void> {
29
- await databaseService.connect()
32
+ interface PostChatMemoryQueueDeps {
33
+ databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
34
+ memoryService: MaybeAwaitableService<Context.Service.Shape<typeof MemoryServiceTag>>
35
+ }
30
36
 
37
+ function processPostChatMemoryJob(deps: PostChatMemoryQueueDeps, job: Job<PostChatMemoryExtractionJob>): Promise<void> {
38
+ const { memoryService } = deps
31
39
  const data = job.data
32
40
  const userMessage = data.userMessage.trim()
33
41
  const agentMessages = data.agentMessages
@@ -37,7 +45,9 @@ async function processPostChatMemoryJob(job: Job<PostChatMemoryExtractionJob>):
37
45
  }))
38
46
  .filter((item) => item.content.length > 0)
39
47
 
40
- if (!userMessage || agentMessages.length === 0) return
48
+ if (!userMessage || agentMessages.length === 0) {
49
+ return Promise.resolve()
50
+ }
41
51
 
42
52
  const joinedOutput = agentMessages
43
53
  .map((item) => (item.agentName ? `[${item.agentName}] ${item.content}` : item.content))
@@ -51,23 +61,27 @@ async function processPostChatMemoryJob(job: Job<PostChatMemoryExtractionJob>):
51
61
  ),
52
62
  ]
53
63
 
54
- await memoryService.addConversationMemories({
55
- orgId: data.orgId,
56
- input: userMessage,
57
- output: joinedOutput,
58
- sourceId: data.sourceId,
59
- source: data.source,
60
- sourceMetadata: data.sourceMetadata,
61
- onboardStatus: data.onboardStatus,
62
- ...(uniqueAgentNames.length > 0 ? { agentName: uniqueAgentNames[0] } : {}),
63
- historyMessages: data.historyMessages,
64
- memoryBlock: data.memoryBlock,
65
- attachmentContext: data.attachmentContext,
66
- agentNames: uniqueAgentNames,
67
- })
64
+ return Effect.runPromise(
65
+ Effect.asVoid(
66
+ memoryService.addConversationMemories({
67
+ orgId: data.orgId,
68
+ input: userMessage,
69
+ output: joinedOutput,
70
+ sourceId: data.sourceId,
71
+ source: data.source,
72
+ sourceMetadata: data.sourceMetadata,
73
+ onboardStatus: data.onboardStatus,
74
+ ...(uniqueAgentNames.length > 0 ? { agentName: uniqueAgentNames[0] } : {}),
75
+ historyMessages: data.historyMessages,
76
+ memoryBlock: data.memoryBlock,
77
+ attachmentContext: data.attachmentContext,
78
+ agentNames: uniqueAgentNames,
79
+ }),
80
+ ),
81
+ )
68
82
  }
69
83
 
70
- const postChatMemory = createQueueFactory<PostChatMemoryExtractionJob>({
84
+ const postChatMemory = createQueueFactoryWithDeps<PostChatMemoryExtractionJob, PostChatMemoryQueueDeps>({
71
85
  name: 'post-chat-memory',
72
86
  displayName: 'Post-chat memory',
73
87
  jobName: 'extract-memory',
@@ -76,14 +90,30 @@ const postChatMemory = createQueueFactory<PostChatMemoryExtractionJob>({
76
90
  maxStalledCount: 10,
77
91
  stalledInterval: 120_000,
78
92
  defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
93
+ prepare: ({ databaseService }) => databaseService.connect(),
79
94
  processor: processPostChatMemoryJob,
80
95
  })
81
96
 
82
97
  export function enqueuePostChatMemory(job: PostChatMemoryExtractionJob, options?: { dedupeKey?: string }) {
83
98
  return postChatMemory.enqueue(job, options?.dedupeKey ? { jobId: options.dedupeKey } : undefined)
84
99
  }
85
- export const startPostChatMemoryWorker = postChatMemory.startWorker
86
100
 
87
- if (import.meta.main) {
88
- startPostChatMemoryWorker()
101
+ export function startPostChatMemoryWorker(options: {
102
+ registerSignals?: boolean
103
+ connectionProvider: () => IORedis
104
+ deps: PostChatMemoryQueueDeps
105
+ }) {
106
+ return postChatMemory.startWorker({
107
+ deps: options.deps,
108
+ registerSignals: options.registerSignals,
109
+ connectionProvider: options.connectionProvider,
110
+ })
89
111
  }
112
+
113
+ runStandaloneQueueWorker((runtime) => {
114
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
115
+ startPostChatMemoryWorker({
116
+ connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
117
+ deps: { databaseService: resolve(DatabaseServiceTag), memoryService: resolve(MemoryServiceTag) },
118
+ })
119
+ })
@@ -1,24 +1,50 @@
1
1
  import { Queue, Worker } from 'bullmq'
2
2
  import type { Job, JobsOptions, QueueOptions, WorkerOptions } from 'bullmq'
3
+ import { Effect, Schema } from 'effect'
3
4
  import type IORedis from 'ioredis'
4
5
 
6
+ import type { LotaLogger } from '../config/logger'
5
7
  import { serverLogger } from '../config/logger'
6
- import { getRedisConnectionForBullMQ } from '../redis'
7
- import { queueJobService } from '../services/queue-job.service'
8
+ import { getCurrentRuntime } from '../effect/runtime-ref'
9
+ import { RedisServiceTag } from '../effect/services'
10
+ import type { TrackedBullJobLike } from '../services/queue-job.service'
8
11
  import {
9
12
  attachWorkerEvents,
10
13
  createTracedWorkerProcessor,
11
14
  createWorkerShutdown,
12
15
  DEFAULT_JOB_RETENTION,
13
16
  registerShutdownSignals,
17
+ getQueueJobService,
14
18
  } from '../workers/worker-utils'
15
19
  import type { WorkerHandle } from '../workers/worker-utils'
16
20
 
21
+ class QueueFactoryError extends Schema.TaggedErrorClass<QueueFactoryError>()('@lota-sdk/core/QueueFactoryError', {
22
+ message: Schema.String,
23
+ cause: Schema.optional(Schema.Defect),
24
+ }) {}
25
+
26
+ function getDefaultQueueConnectionProvider(): () => IORedis {
27
+ const redis = getCurrentRuntime().runSync(Effect.service(RedisServiceTag))
28
+ return () => redis.getConnectionForBullMQ()
29
+ }
30
+
31
+ type QueueShape<TJob> = Queue<TJob, unknown, string, TJob, unknown, string>
32
+ type QueueMethod = 'add' | 'close' | 'remove' | 'removeDeduplicationKey' | 'removeJobScheduler'
33
+
34
+ const queueMethodsThatWaitForClose = new Set<QueueMethod>([
35
+ 'add',
36
+ 'close',
37
+ 'remove',
38
+ 'removeDeduplicationKey',
39
+ 'removeJobScheduler',
40
+ ])
41
+
17
42
  interface QueueFactoryConfigBase {
18
43
  name: string
19
44
  displayName: string
20
45
  jobName: string
21
46
  concurrency: number
47
+ logger?: LotaLogger
22
48
  lockDuration?: number
23
49
  stalledInterval?: number
24
50
  maxStalledCount?: number
@@ -27,6 +53,7 @@ interface QueueFactoryConfigBase {
27
53
  }
28
54
 
29
55
  interface QueueFactoryConfigInline<TJob> extends QueueFactoryConfigBase {
56
+ prepare?: (job: Job<TJob>) => Promise<void>
30
57
  processor: (job: Job<TJob>) => Promise<unknown>
31
58
  processorPath?: never
32
59
  }
@@ -38,93 +65,208 @@ interface QueueFactoryConfigFile extends QueueFactoryConfigBase {
38
65
 
39
66
  export type QueueFactoryConfig<TJob> = QueueFactoryConfigInline<TJob> | QueueFactoryConfigFile
40
67
 
68
+ interface QueueFactoryWithDepsConfig<TJob, TDeps> extends QueueFactoryConfigBase {
69
+ prepare?: (deps: TDeps, job: Job<TJob>) => Promise<void>
70
+ processor: (deps: TDeps, job: Job<TJob>) => Promise<unknown>
71
+ }
72
+
73
+ interface QueueWorkerConfigInline<TJob> {
74
+ prepare?: (job: Job<TJob>) => Promise<void>
75
+ processor: (job: Job<TJob>) => Promise<unknown>
76
+ processorPath?: never
77
+ }
78
+
79
+ interface QueueWorkerConfigFile {
80
+ processor?: never
81
+ processorPath: string
82
+ }
83
+
84
+ type QueueWorkerConfig<TJob> = QueueWorkerConfigInline<TJob> | QueueWorkerConfigFile
85
+
86
+ export interface QueueFactoryWorkerOptions {
87
+ registerSignals?: boolean
88
+ connectionProvider?: () => IORedis
89
+ }
90
+
41
91
  export interface QueueFactory<TJob> {
42
- getQueue: () => Queue<TJob, unknown, string, TJob, unknown, string>
92
+ getQueue: () => QueueShape<TJob>
43
93
  enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
44
- startWorker: (options?: { registerSignals?: boolean }) => WorkerHandle
94
+ startWorker: (options?: QueueFactoryWorkerOptions) => WorkerHandle
45
95
  }
46
96
 
47
- export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): QueueFactory<TJob> {
48
- type QueueShape = Queue<TJob, unknown, string, TJob, unknown, string>
97
+ export interface QueueFactoryWithDeps<TJob, TDeps> {
98
+ getQueue: () => QueueShape<TJob>
99
+ enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
100
+ startWorker: (options: QueueFactoryWorkerOptions & { deps: TDeps }) => WorkerHandle
101
+ }
49
102
 
50
- let _queue: QueueShape | null = null
51
- let _queueConnection: IORedis | null = null
103
+ export function recordEnqueuedJobMetadata(params: {
104
+ queueName: string
105
+ job: Omit<TrackedBullJobLike, 'queueName'>
106
+ logger?: LotaLogger
107
+ }): Effect.Effect<string | undefined> {
108
+ const logger = params.logger ?? serverLogger
109
+ return getQueueJobService()
110
+ .recordEnqueued({ queueName: params.queueName, ...params.job })
111
+ .pipe(
112
+ Effect.tapError((error) =>
113
+ Effect.sync(() => {
114
+ logger.error`Failed to persist queued job metadata (queue=${params.queueName}, job=${params.job.id}): ${error}`
115
+ }),
116
+ ),
117
+ Effect.orElseSucceed(() => undefined),
118
+ )
119
+ }
52
120
 
53
- const getConnection = (): IORedis => config.connectionProvider?.() ?? getRedisConnectionForBullMQ()
121
+ function createQueueFactoryRuntime<TJob>(config: QueueFactoryConfigBase): {
122
+ getQueue: () => QueueShape<TJob>
123
+ enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
124
+ startWorker: (workerConfig: QueueWorkerConfig<TJob>, options?: QueueFactoryWorkerOptions) => WorkerHandle
125
+ } {
126
+ type State = {
127
+ queue: QueueShape<TJob> | null
128
+ rawQueue: QueueShape<TJob> | null
129
+ connection: IORedis | null
130
+ pendingClose: Promise<void> | null
131
+ }
54
132
 
55
- const getQueue = (): QueueShape => {
56
- const connection = getConnection()
57
- const shouldRecreateQueue =
58
- _queue === null ||
59
- _queueConnection === null ||
60
- _queueConnection !== connection ||
61
- _queueConnection.status === 'close' ||
62
- _queueConnection.status === 'end'
63
-
64
- if (shouldRecreateQueue) {
65
- if (_queue) {
66
- void _queue.close().catch((error: unknown) => {
67
- serverLogger.warn`Failed to close stale ${config.displayName} queue: ${error}`
68
- })
69
- }
133
+ let state: State = { queue: null, rawQueue: null, connection: null, pendingClose: null }
70
134
 
71
- _queue = new Queue<TJob, unknown, string, TJob, unknown, string>(config.name, {
72
- connection: connection as QueueOptions['connection'],
73
- defaultJobOptions: { ...DEFAULT_JOB_RETENTION, ...config.defaultJobOptions },
74
- })
75
- _queueConnection = connection
76
- }
77
- if (_queue === null) {
78
- throw new Error(`Failed to initialize queue: ${config.name}`)
135
+ const resolveConnectionProvider = (): (() => IORedis) =>
136
+ config.connectionProvider ?? getDefaultQueueConnectionProvider()
137
+
138
+ const getConnection = (): IORedis => resolveConnectionProvider()()
139
+
140
+ const waitForPendingClose = (): Promise<void> => {
141
+ const pendingClose = state.pendingClose
142
+ if (!pendingClose) {
143
+ return Promise.resolve()
79
144
  }
80
- return _queue
145
+
146
+ return pendingClose.finally(() => {
147
+ if (state.pendingClose === pendingClose) {
148
+ state.pendingClose = null
149
+ }
150
+ })
81
151
  }
82
152
 
83
- const jobName = config.jobName
84
- const toData = (job: TJob) => job
85
-
86
- const enqueue = async (job: TJob, options?: JobsOptions): Promise<void> => {
87
- const queuedJob = await getQueue().add(jobName, toData(job), options)
88
- try {
89
- await queueJobService.recordEnqueued({
90
- queueName: config.name,
91
- id: queuedJob.id,
92
- name: queuedJob.name,
93
- data: queuedJob.data,
94
- opts: queuedJob.opts,
95
- attemptsMade: queuedJob.attemptsMade,
96
- timestamp: queuedJob.timestamp,
97
- })
98
- } catch (error) {
99
- serverLogger.error`Failed to persist queued job metadata (queue=${config.name}, job=${queuedJob.id}): ${error}`
153
+ const wrapQueue = (queue: QueueShape<TJob>): QueueShape<TJob> =>
154
+ new Proxy(queue, {
155
+ get(target, property, receiver) {
156
+ if (typeof property !== 'string') {
157
+ return Reflect.get(target, property, receiver) as unknown
158
+ }
159
+
160
+ const value = (target as unknown as Record<string, unknown>)[property]
161
+ if (typeof value !== 'function' || !queueMethodsThatWaitForClose.has(property as QueueMethod)) {
162
+ return value
163
+ }
164
+
165
+ return (...args: Array<unknown>) =>
166
+ waitForPendingClose().then(() =>
167
+ Reflect.apply(value as (...methodArgs: Array<unknown>) => unknown, target, args),
168
+ )
169
+ },
170
+ })
171
+
172
+ const getQueue = (): QueueShape<TJob> => {
173
+ const connection = getConnection()
174
+ const isStale =
175
+ state.rawQueue === null ||
176
+ state.queue === null ||
177
+ state.connection === null ||
178
+ state.connection !== connection ||
179
+ state.connection.status === 'close' ||
180
+ state.connection.status === 'end'
181
+
182
+ if (!isStale && state.queue) {
183
+ return state.queue
184
+ }
185
+
186
+ if (state.rawQueue) {
187
+ const staleQueue = state.rawQueue
188
+ const previousPendingClose = state.pendingClose ?? Promise.resolve()
189
+ state.pendingClose = previousPendingClose
190
+ .catch(() => undefined)
191
+ .then(() =>
192
+ staleQueue.close().catch((error: unknown) => {
193
+ serverLogger.warn`Failed to close stale ${config.displayName} queue: ${error}`
194
+ }),
195
+ )
196
+ .then(() => undefined)
100
197
  }
198
+
199
+ const queue = new Queue<TJob, unknown, string, TJob, unknown, string>(config.name, {
200
+ connection: connection as QueueOptions['connection'],
201
+ defaultJobOptions: { ...DEFAULT_JOB_RETENTION, ...config.defaultJobOptions },
202
+ })
203
+ const wrappedQueue = wrapQueue(queue)
204
+
205
+ state = { queue: wrappedQueue, rawQueue: queue, connection, pendingClose: state.pendingClose }
206
+ return wrappedQueue
101
207
  }
102
208
 
103
- const startWorker = (options: { registerSignals?: boolean } = {}): WorkerHandle => {
104
- const { registerSignals = import.meta.main } = options
209
+ const enqueue = (job: TJob, options?: JobsOptions): Promise<void> =>
210
+ Effect.runPromise(
211
+ Effect.gen(function* () {
212
+ const queuedJob = yield* Effect.tryPromise({
213
+ try: () => getQueue().add(config.jobName, job, options),
214
+ catch: (cause) =>
215
+ new QueueFactoryError({ message: `Failed to enqueue job on queue "${config.name}".`, cause }),
216
+ })
217
+ yield* recordEnqueuedJobMetadata({ queueName: config.name, job: queuedJob, logger: config.logger })
218
+ }),
219
+ )
220
+
221
+ const startWorker = (
222
+ workerConfig: QueueWorkerConfig<TJob>,
223
+ options: QueueFactoryWorkerOptions = {},
224
+ ): WorkerHandle => {
225
+ const { registerSignals = import.meta.main, connectionProvider } = options
226
+ const logger = config.logger ?? serverLogger
105
227
 
106
228
  const workerOptions: WorkerOptions = {
107
- connection: getConnection() as QueueOptions['connection'],
229
+ connection: (connectionProvider ?? resolveConnectionProvider())() as QueueOptions['connection'],
108
230
  concurrency: config.concurrency,
109
231
  ...(config.lockDuration !== undefined ? { lockDuration: config.lockDuration } : {}),
110
232
  ...(config.stalledInterval !== undefined ? { stalledInterval: config.stalledInterval } : {}),
111
233
  ...(config.maxStalledCount !== undefined ? { maxStalledCount: config.maxStalledCount } : {}),
112
234
  }
113
235
 
114
- const worker = config.processorPath
115
- ? new Worker(config.name, config.processorPath, workerOptions)
236
+ const worker = workerConfig.processorPath
237
+ ? new Worker(config.name, workerConfig.processorPath, workerOptions)
116
238
  : new Worker(
117
239
  config.name,
118
- createTracedWorkerProcessor(config.name, (config as QueueFactoryConfigInline<TJob>).processor),
240
+ createTracedWorkerProcessor(config.name, (job) =>
241
+ Effect.runPromise(
242
+ Effect.gen(function* () {
243
+ const inlineWorkerConfig = workerConfig as QueueWorkerConfigInline<TJob>
244
+ const typedJob = job as Job<TJob>
245
+ const prepare = inlineWorkerConfig.prepare
246
+ if (prepare) {
247
+ yield* Effect.tryPromise({
248
+ try: () => prepare(typedJob),
249
+ catch: (cause) =>
250
+ new QueueFactoryError({ message: `Worker prepare failed for queue "${config.name}".`, cause }),
251
+ })
252
+ }
253
+ return yield* Effect.tryPromise({
254
+ try: () => inlineWorkerConfig.processor(typedJob),
255
+ catch: (cause) =>
256
+ new QueueFactoryError({ message: `Worker processor failed for queue "${config.name}".`, cause }),
257
+ })
258
+ }),
259
+ ),
260
+ ),
119
261
  workerOptions,
120
262
  )
121
263
 
122
- attachWorkerEvents(worker, config.displayName, serverLogger)
264
+ attachWorkerEvents(worker, config.displayName, logger)
123
265
 
124
- const shutdown = createWorkerShutdown(worker, config.displayName, serverLogger)
266
+ const shutdown = createWorkerShutdown(worker, config.displayName, logger)
125
267
 
126
268
  if (registerSignals) {
127
- registerShutdownSignals({ name: config.displayName, shutdown, logger: serverLogger })
269
+ registerShutdownSignals({ name: config.displayName, shutdown, logger })
128
270
  }
129
271
 
130
272
  return { worker, shutdown }
@@ -132,3 +274,29 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
132
274
 
133
275
  return { getQueue, enqueue, startWorker }
134
276
  }
277
+
278
+ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): QueueFactory<TJob> {
279
+ const runtime = createQueueFactoryRuntime<TJob>(config)
280
+ return {
281
+ getQueue: runtime.getQueue,
282
+ enqueue: runtime.enqueue,
283
+ startWorker: (options) => runtime.startWorker(config, options),
284
+ }
285
+ }
286
+
287
+ export function createQueueFactoryWithDeps<TJob, TDeps>(
288
+ config: QueueFactoryWithDepsConfig<TJob, TDeps>,
289
+ ): QueueFactoryWithDeps<TJob, TDeps> {
290
+ const runtime = createQueueFactoryRuntime<TJob>(config)
291
+ const prepare = config.prepare
292
+
293
+ return {
294
+ getQueue: runtime.getQueue,
295
+ enqueue: runtime.enqueue,
296
+ startWorker: ({ deps, ...options }) =>
297
+ runtime.startWorker(
298
+ { prepare: prepare ? (job) => prepare(deps, job) : undefined, processor: (job) => config.processor(deps, job) },
299
+ options,
300
+ ),
301
+ }
302
+ }