@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
@@ -3,12 +3,23 @@ import { Effect } from 'effect'
3
3
  import type { Context } from 'effect'
4
4
  import type IORedis from 'ioredis'
5
5
 
6
- import { ConfigurationError } from '../effect/errors'
7
6
  import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
8
- import { PlanExecutorServiceTag } from '../services/plan/plan-executor.service'
9
- import { createQueueFactory } from './queue-factory'
7
+ import { QueueJobServiceTag } from '../services/queue-job.service'
8
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
9
+ import { createQueueFactoryWithDeps } from './queue-factory'
10
10
  import { runStandaloneQueueWorker } from './standalone-worker'
11
11
 
12
+ // Minimal service shape used by the worker processor. Declared structurally to
13
+ // avoid importing the service tag — which would form a dependency cycle since
14
+ // PlanExecutorServiceLive depends on LotaQueuesServiceTag.
15
+ interface DelayedNodePromotionPlanExecutorShape {
16
+ promoteDelayedNode(params: {
17
+ runId: string
18
+ nodeId: string
19
+ emittedBy: string
20
+ }): Effect.Effect<unknown, unknown, unknown>
21
+ }
22
+
12
23
  export interface DelayedNodePromotionJob {
13
24
  runId: string
14
25
  nodeId: string
@@ -17,23 +28,16 @@ export interface DelayedNodePromotionJob {
17
28
 
18
29
  export const DELAYED_NODE_PROMOTION_QUEUE = 'delayed-node-promotion'
19
30
 
20
- interface DelayedNodePromotionQueueDeps {
31
+ export interface DelayedNodePromotionWorkerDeps {
21
32
  databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
22
- planExecutorService: Context.Service.Shape<typeof PlanExecutorServiceTag>
33
+ planExecutorService: DelayedNodePromotionPlanExecutorShape
23
34
  }
24
35
 
25
- let _deps: DelayedNodePromotionQueueDeps | null = null
26
- function getDeps(): DelayedNodePromotionQueueDeps {
27
- if (!_deps)
28
- throw new ConfigurationError({
29
- message: 'Delayed node promotion queue is not configured. Initialize the runtime before starting the worker.',
30
- key: 'queue-deps',
31
- })
32
- return _deps
33
- }
34
-
35
- function processDelayedNodePromotionJob(job: Job<DelayedNodePromotionJob>): Promise<void> {
36
- const { planExecutorService } = getDeps()
36
+ function processDelayedNodePromotionJob(
37
+ deps: DelayedNodePromotionWorkerDeps,
38
+ job: Job<DelayedNodePromotionJob>,
39
+ ): Promise<void> {
40
+ const { planExecutorService } = deps
37
41
  return Promise.resolve(
38
42
  planExecutorService.promoteDelayedNode({
39
43
  runId: job.data.runId,
@@ -43,36 +47,51 @@ function processDelayedNodePromotionJob(job: Job<DelayedNodePromotionJob>): Prom
43
47
  ).then(() => undefined)
44
48
  }
45
49
 
46
- const delayedNodePromotion = createQueueFactory<DelayedNodePromotionJob>({
47
- name: DELAYED_NODE_PROMOTION_QUEUE,
48
- displayName: 'Delayed node promotion',
49
- jobName: 'promote-node',
50
- concurrency: 1,
51
- defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 5000 } },
52
- prepare: () => getDeps().databaseService.connect(),
53
- processor: processDelayedNodePromotionJob,
54
- })
55
-
56
- export function enqueueDelayedNodePromotion(job: DelayedNodePromotionJob, delayMs: number) {
57
- return delayedNodePromotion.enqueue(job, { delay: delayMs, jobId: `promote:${job.runId}:${job.nodeId}` })
50
+ export interface DelayedNodePromotionQueueRuntime {
51
+ enqueueDelayedNodePromotion(job: DelayedNodePromotionJob, delayMs: number): Promise<void>
52
+ startWorker(options: { registerSignals?: boolean; deps: DelayedNodePromotionWorkerDeps }): WorkerHandle
58
53
  }
59
54
 
60
- export function startDelayedNodePromotionWorker(options: {
61
- registerSignals?: boolean
55
+ interface MakeDelayedNodePromotionQueueRuntimeParams {
62
56
  connectionProvider: () => IORedis
63
- deps: DelayedNodePromotionQueueDeps
64
- }): ReturnType<typeof delayedNodePromotion.startWorker> {
65
- _deps = options.deps
66
- return delayedNodePromotion.startWorker({
67
- registerSignals: options.registerSignals,
68
- connectionProvider: options.connectionProvider,
57
+ queueJobService: QueueJobService
58
+ }
59
+
60
+ export function makeDelayedNodePromotionQueueRuntime(
61
+ params: MakeDelayedNodePromotionQueueRuntimeParams,
62
+ ): DelayedNodePromotionQueueRuntime {
63
+ const { connectionProvider, queueJobService } = params
64
+ const queue = createQueueFactoryWithDeps<DelayedNodePromotionJob, DelayedNodePromotionWorkerDeps>({
65
+ name: DELAYED_NODE_PROMOTION_QUEUE,
66
+ displayName: 'Delayed node promotion',
67
+ jobName: 'promote-node',
68
+ concurrency: 1,
69
+ defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 5000 } },
70
+ connectionProvider,
71
+ queueJobService,
72
+ prepare: ({ databaseService }) => Effect.runPromise(databaseService.connect()),
73
+ processor: processDelayedNodePromotionJob,
69
74
  })
75
+
76
+ return {
77
+ enqueueDelayedNodePromotion: (job, delayMs) =>
78
+ queue.enqueue(job, { delay: delayMs, jobId: `promote:${job.runId}:${job.nodeId}` }),
79
+ startWorker: (options) =>
80
+ queue.startWorker({ deps: options.deps, registerSignals: options.registerSignals, connectionProvider }),
81
+ }
70
82
  }
71
83
 
72
84
  runStandaloneQueueWorker((runtime) => {
73
- const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
74
- startDelayedNodePromotionWorker({
75
- connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
76
- deps: { databaseService: resolve(DatabaseServiceTag), planExecutorService: resolve(PlanExecutorServiceTag) },
77
- })
85
+ void (async () => {
86
+ const { PlanExecutorServiceTag } = await import('../services/plan/plan-executor.service')
87
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
88
+ const redis = resolve(RedisServiceTag)
89
+ const delayedNodePromotionQueue = makeDelayedNodePromotionQueueRuntime({
90
+ connectionProvider: () => redis.getConnectionForBullMQ(),
91
+ queueJobService: resolve(QueueJobServiceTag),
92
+ })
93
+ delayedNodePromotionQueue.startWorker({
94
+ deps: { databaseService: resolve(DatabaseServiceTag), planExecutorService: resolve(PlanExecutorServiceTag) },
95
+ })
96
+ })()
78
97
  })
@@ -3,8 +3,8 @@ import type IORedis from 'ioredis'
3
3
 
4
4
  import type { chatLogger } from '../config/logger'
5
5
  import { sha256Hex } from '../utils/crypto'
6
- import { DEFAULT_JOB_RETENTION, getQueueJobService } from '../workers/worker-utils'
7
- import type { WorkerHandle } from '../workers/worker-utils'
6
+ import { DEFAULT_JOB_RETENTION } from '../workers/worker-utils'
7
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
8
8
  import { createQueueFactory } from './queue-factory'
9
9
 
10
10
  export type DocumentSourceChannel = string
@@ -56,6 +56,7 @@ export function createDocumentProcessorQueueRuntime<TJob extends DocumentProcess
56
56
  getConnectionForBullMQ: () => IORedis
57
57
  getWorkerPath: () => string
58
58
  logger: typeof chatLogger
59
+ queueJobService: QueueJobService
59
60
  queueName?: string
60
61
  workerName?: string
61
62
  concurrency?: number
@@ -75,6 +76,7 @@ export function createDocumentProcessorQueueRuntime<TJob extends DocumentProcess
75
76
  connectionProvider: params.getConnectionForBullMQ,
76
77
  defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 3, backoff: { type: 'exponential', delay: 1000 } },
77
78
  processorPath: params.getWorkerPath(),
79
+ queueJobService: params.queueJobService,
78
80
  })
79
81
 
80
82
  return {
@@ -86,7 +88,7 @@ export function createDocumentProcessorQueueRuntime<TJob extends DocumentProcess
86
88
  )
87
89
 
88
90
  yield* Effect.catch(
89
- getQueueJobService().recordEnqueued({
91
+ params.queueJobService.recordEnqueued({
90
92
  queueName,
91
93
  id: queuedJob.id,
92
94
  name: queuedJob.name,
@@ -8,4 +8,5 @@ export * from './organization-learning.queue'
8
8
  export * from './plan-agent-heartbeat.queue'
9
9
  export * from './plan-scheduler.queue'
10
10
  export * from './post-chat-memory.queue'
11
+ export * from './queues.service'
11
12
  export * from './title-generation.queue'
@@ -1,12 +1,12 @@
1
1
  import { Effect } from 'effect'
2
+ import type { Context } from 'effect'
3
+ import type IORedis from 'ioredis'
2
4
 
3
5
  import { serverLogger } from '../config/logger'
4
- import {
5
- getQueueJobService,
6
- getWorkerPath,
7
- LONG_JOB_LOCK_DURATION_MS,
8
- LOW_JOB_RETENTION,
9
- } from '../workers/worker-utils'
6
+ import { RedisServiceTag } from '../effect/services'
7
+ import { QueueJobServiceTag } from '../services/queue-job.service'
8
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
9
+ import { getWorkerPath, LONG_JOB_LOCK_DURATION_MS, LOW_JOB_RETENTION } from '../workers/worker-utils'
10
10
  import { createQueueFactory } from './queue-factory'
11
11
  import { runStandaloneQueueWorker } from './standalone-worker'
12
12
 
@@ -17,58 +17,84 @@ export interface MemoryConsolidationJob {
17
17
  const MEMORY_CONSOLIDATION_INTERVAL_MS = 24 * 60 * 60 * 1000
18
18
  const MEMORY_CONSOLIDATION_SCHEDULER_ID = 'memory-consolidation-recurring'
19
19
 
20
- const memoryConsolidation = createQueueFactory<MemoryConsolidationJob>({
21
- name: 'memory-consolidation',
22
- displayName: 'Memory consolidation',
23
- jobName: 'consolidate-turn',
24
- concurrency: 1,
25
- lockDuration: LONG_JOB_LOCK_DURATION_MS,
26
- defaultJobOptions: { ...LOW_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
27
- processorPath: getWorkerPath('memory-consolidation.worker.ts'),
28
- })
20
+ export interface MemoryConsolidationQueueRuntime {
21
+ enqueueMemoryConsolidation(job?: MemoryConsolidationJob): Promise<void>
22
+ scheduleRecurringConsolidation(): Promise<void>
23
+ startWorker(options?: { registerSignals?: boolean }): WorkerHandle
24
+ }
29
25
 
30
- export function enqueueMemoryConsolidation(job: MemoryConsolidationJob = {}) {
31
- return memoryConsolidation.enqueue(job, { jobId: job.scopeId ? `consolidate-turn:${job.scopeId}` : undefined })
26
+ interface MakeMemoryConsolidationQueueRuntimeParams {
27
+ connectionProvider: () => IORedis
28
+ queueJobService: QueueJobService
32
29
  }
33
30
 
34
- export function scheduleRecurringConsolidation() {
35
- return Effect.runPromise(
36
- Effect.gen(function* () {
37
- const queuedJob = yield* Effect.tryPromise(() =>
38
- memoryConsolidation
39
- .getQueue()
40
- .upsertJobScheduler(
41
- MEMORY_CONSOLIDATION_SCHEDULER_ID,
42
- { every: MEMORY_CONSOLIDATION_INTERVAL_MS },
43
- {
44
- name: 'consolidate',
45
- data: {},
46
- opts: { ...LOW_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
47
- },
48
- ),
49
- )
31
+ export function makeMemoryConsolidationQueueRuntime(
32
+ params: MakeMemoryConsolidationQueueRuntimeParams,
33
+ ): MemoryConsolidationQueueRuntime {
34
+ const { connectionProvider, queueJobService } = params
35
+ const queue = createQueueFactory<MemoryConsolidationJob>({
36
+ name: 'memory-consolidation',
37
+ displayName: 'Memory consolidation',
38
+ jobName: 'consolidate-turn',
39
+ concurrency: 1,
40
+ lockDuration: LONG_JOB_LOCK_DURATION_MS,
41
+ defaultJobOptions: { ...LOW_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
42
+ processorPath: getWorkerPath('memory-consolidation.worker.ts'),
43
+ connectionProvider,
44
+ queueJobService,
45
+ })
46
+
47
+ const enqueueMemoryConsolidation: MemoryConsolidationQueueRuntime['enqueueMemoryConsolidation'] = (job = {}) =>
48
+ queue.enqueue(job, { jobId: job.scopeId ? `consolidate-turn:${job.scopeId}` : undefined })
50
49
 
51
- yield* Effect.catch(
52
- getQueueJobService().recordEnqueued({
53
- queueName: 'memory-consolidation',
54
- id: queuedJob.id,
55
- name: queuedJob.name,
56
- data: queuedJob.data,
57
- opts: queuedJob.opts,
58
- attemptsMade: queuedJob.attemptsMade,
59
- timestamp: queuedJob.timestamp,
60
- }),
61
- (error) =>
62
- Effect.sync(() => {
63
- serverLogger.error`Failed to persist queued job metadata (queue=memory-consolidation, job=${queuedJob.id}): ${error}`
50
+ const scheduleRecurringConsolidation: MemoryConsolidationQueueRuntime['scheduleRecurringConsolidation'] = () =>
51
+ Effect.runPromise(
52
+ Effect.gen(function* () {
53
+ const queuedJob = yield* Effect.tryPromise(() =>
54
+ queue
55
+ .getQueue()
56
+ .upsertJobScheduler(
57
+ MEMORY_CONSOLIDATION_SCHEDULER_ID,
58
+ { every: MEMORY_CONSOLIDATION_INTERVAL_MS },
59
+ {
60
+ name: 'consolidate',
61
+ data: {},
62
+ opts: { ...LOW_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
63
+ },
64
+ ),
65
+ )
66
+
67
+ yield* Effect.catch(
68
+ queueJobService.recordEnqueued({
69
+ queueName: 'memory-consolidation',
70
+ id: queuedJob.id,
71
+ name: queuedJob.name,
72
+ data: queuedJob.data,
73
+ opts: queuedJob.opts,
74
+ attemptsMade: queuedJob.attemptsMade,
75
+ timestamp: queuedJob.timestamp,
64
76
  }),
65
- )
66
- }),
67
- )
68
- }
77
+ (error) =>
78
+ Effect.sync(() => {
79
+ serverLogger.error`Failed to persist queued job metadata (queue=memory-consolidation, job=${queuedJob.id}): ${error}`
80
+ }),
81
+ )
82
+ }),
83
+ )
69
84
 
70
- export const startMemoryConsolidationWorker = memoryConsolidation.startWorker
85
+ return {
86
+ enqueueMemoryConsolidation,
87
+ scheduleRecurringConsolidation,
88
+ startWorker: (options = {}) => queue.startWorker({ registerSignals: options.registerSignals, connectionProvider }),
89
+ }
90
+ }
71
91
 
72
- runStandaloneQueueWorker(() => {
73
- startMemoryConsolidationWorker()
92
+ runStandaloneQueueWorker((runtime) => {
93
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
94
+ const redis = resolve(RedisServiceTag)
95
+ const memoryConsolidationQueue = makeMemoryConsolidationQueueRuntime({
96
+ connectionProvider: () => redis.getConnectionForBullMQ(),
97
+ queueJobService: resolve(QueueJobServiceTag),
98
+ })
99
+ memoryConsolidationQueue.startWorker()
74
100
  })
@@ -1,9 +1,19 @@
1
- import { Effect } from 'effect'
1
+ import { Effect, Schema } from 'effect'
2
+ import type { Context } from 'effect'
3
+ import type IORedis from 'ioredis'
2
4
 
5
+ import { RedisServiceTag } from '../effect/services'
6
+ import { QueueJobServiceTag } from '../services/queue-job.service'
7
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
3
8
  import { getWorkerPath, LONG_JOB_LOCK_DURATION_MS } from '../workers/worker-utils'
4
9
  import { createQueueFactory } from './queue-factory'
5
10
  import { runStandaloneQueueWorker } from './standalone-worker'
6
11
 
12
+ class OrganizationLearningQueueError extends Schema.TaggedErrorClass<OrganizationLearningQueueError>()(
13
+ '@lota-sdk/core/OrganizationLearningQueueError',
14
+ { message: Schema.String, cause: Schema.optional(Schema.Defect) },
15
+ ) {}
16
+
7
17
  export const ORGANIZATION_LEARNING_QUEUE = 'organization-learning'
8
18
 
9
19
  const ORGANIZATION_LEARNING_DELAY_MS = 15 * 60 * 1000
@@ -46,42 +56,69 @@ export function buildSkillExtractionJobOptions(orgId: string) {
46
56
  return { delay: ORGANIZATION_LEARNING_DELAY_MS, deduplication: { id: buildSkillExtractionDeduplicationId(orgId) } }
47
57
  }
48
58
 
49
- const organizationLearningQueue = createQueueFactory<OrganizationLearningJob>({
50
- name: ORGANIZATION_LEARNING_QUEUE,
51
- displayName: 'Organization learning',
52
- jobName: 'organization-learning',
53
- concurrency: 4,
54
- lockDuration: LONG_JOB_LOCK_DURATION_MS,
55
- defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5_000 } },
56
- processorPath: getWorkerPath('organization-learning.worker.ts'),
57
- })
58
-
59
- export function enqueueRegularChatMemoryDigest(job: Omit<RegularChatMemoryDigestJob, 'kind'>) {
60
- return organizationLearningQueue.enqueue(
61
- { kind: 'regular-chat-memory-digest', ...job },
62
- buildRegularChatMemoryDigestJobOptions(job.orgId),
63
- )
59
+ export interface OrganizationLearningQueueRuntime {
60
+ enqueueRegularChatMemoryDigest(job: Omit<RegularChatMemoryDigestJob, 'kind'>): Promise<void>
61
+ enqueueSkillExtraction(job: Omit<SkillExtractionJob, 'kind'>): Promise<void>
62
+ clearRegularChatMemoryDigestDeduplicationKey(orgId: string): Promise<void>
63
+ startWorker(options?: { registerSignals?: boolean }): WorkerHandle
64
64
  }
65
65
 
66
- export function enqueueSkillExtraction(job: Omit<SkillExtractionJob, 'kind'>) {
67
- return organizationLearningQueue.enqueue(
68
- { kind: 'skill-extraction', ...job },
69
- buildSkillExtractionJobOptions(job.orgId),
70
- )
66
+ interface MakeOrganizationLearningQueueRuntimeParams {
67
+ connectionProvider: () => IORedis
68
+ queueJobService: QueueJobService
71
69
  }
72
70
 
73
- export function clearRegularChatMemoryDigestDeduplicationKey(orgId: string): Promise<void> {
74
- return Effect.runPromise(
75
- Effect.asVoid(
76
- Effect.tryPromise(() =>
77
- organizationLearningQueue.getQueue().removeDeduplicationKey(buildRegularChatMemoryDigestDeduplicationId(orgId)),
78
- ),
79
- ),
80
- )
81
- }
71
+ export function makeOrganizationLearningQueueRuntime(
72
+ params: MakeOrganizationLearningQueueRuntimeParams,
73
+ ): OrganizationLearningQueueRuntime {
74
+ const { connectionProvider, queueJobService } = params
75
+ const queue = createQueueFactory<OrganizationLearningJob>({
76
+ name: ORGANIZATION_LEARNING_QUEUE,
77
+ displayName: 'Organization learning',
78
+ jobName: 'organization-learning',
79
+ concurrency: 4,
80
+ lockDuration: LONG_JOB_LOCK_DURATION_MS,
81
+ defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5_000 } },
82
+ processorPath: getWorkerPath('organization-learning.worker.ts'),
83
+ connectionProvider,
84
+ queueJobService,
85
+ })
86
+
87
+ const enqueueRegularChatMemoryDigest: OrganizationLearningQueueRuntime['enqueueRegularChatMemoryDigest'] = (job) =>
88
+ queue.enqueue({ kind: 'regular-chat-memory-digest', ...job }, buildRegularChatMemoryDigestJobOptions(job.orgId))
89
+
90
+ const enqueueSkillExtraction: OrganizationLearningQueueRuntime['enqueueSkillExtraction'] = (job) =>
91
+ queue.enqueue({ kind: 'skill-extraction', ...job }, buildSkillExtractionJobOptions(job.orgId))
92
+
93
+ const clearRegularChatMemoryDigestDeduplicationKey: OrganizationLearningQueueRuntime['clearRegularChatMemoryDigestDeduplicationKey'] =
94
+ (orgId) =>
95
+ Effect.runPromise(
96
+ Effect.asVoid(
97
+ Effect.tryPromise({
98
+ try: () => queue.getQueue().removeDeduplicationKey(buildRegularChatMemoryDigestDeduplicationId(orgId)),
99
+ catch: (cause) =>
100
+ new OrganizationLearningQueueError({
101
+ message: `Failed to clear regular-chat memory digest deduplication key for ${orgId}.`,
102
+ cause,
103
+ }),
104
+ }),
105
+ ),
106
+ )
82
107
 
83
- export const startOrganizationLearningWorker = organizationLearningQueue.startWorker
108
+ return {
109
+ enqueueRegularChatMemoryDigest,
110
+ enqueueSkillExtraction,
111
+ clearRegularChatMemoryDigestDeduplicationKey,
112
+ startWorker: (options = {}) => queue.startWorker({ registerSignals: options.registerSignals, connectionProvider }),
113
+ }
114
+ }
84
115
 
85
- runStandaloneQueueWorker(() => {
86
- startOrganizationLearningWorker()
116
+ runStandaloneQueueWorker((runtime) => {
117
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
118
+ const redis = resolve(RedisServiceTag)
119
+ const organizationLearningQueue = makeOrganizationLearningQueueRuntime({
120
+ connectionProvider: () => redis.getConnectionForBullMQ(),
121
+ queueJobService: resolve(QueueJobServiceTag),
122
+ })
123
+ organizationLearningQueue.startWorker()
87
124
  })