@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
package/src/index.ts CHANGED
@@ -15,41 +15,42 @@ export * from './utils'
15
15
  export * from './workers'
16
16
  export { Effect } from 'effect'
17
17
  export {
18
+ ActiveThreadRunConflictError,
18
19
  AgentConfigLive,
20
+ AgentConfigServiceTag,
19
21
  AgentFactoryLive,
20
- AppLoggerTag,
22
+ AgentFactoryServiceTag,
23
+ AiGenerationError,
21
24
  AppLoggerLive,
22
- EffectBadRequestError,
23
- clearLotaSdkRuntime,
25
+ AppLoggerTag,
26
+ BaseServicePersistenceError,
24
27
  ConfigurationError,
25
28
  ConflictError,
26
29
  DatabaseError,
27
30
  DatabaseLive,
31
+ DatabaseServiceTag,
28
32
  DatabaseServiceTag as EffectDatabaseService,
29
- EffectNotFoundError,
30
- EffectThreadTurnError,
31
- EffectValidationError,
32
- getLotaSdkRuntime,
33
- isEffectError,
33
+ ForbiddenError,
34
34
  LockAcquisitionError,
35
35
  LockLostError,
36
- ActiveThreadRunConflictError,
37
36
  RedisError,
38
37
  RedisLive,
38
+ RedisServiceTag,
39
39
  RedisServiceTag as EffectRedisService,
40
40
  RuntimeAdaptersServiceTag,
41
41
  RuntimeConfigLive,
42
42
  RuntimeConfigServiceTag,
43
43
  RuntimeExtensionsLive,
44
44
  RuntimeWorkerExtensionsServiceTag,
45
- setLotaSdkRuntime,
45
+ ServiceError,
46
46
  ThreadConfigLive,
47
47
  ThreadConfigServiceTag,
48
+ ThreadTurnError,
48
49
  TimeoutError,
49
50
  ToolProvidersServiceTag,
50
51
  TurnHooksServiceTag,
51
- toAwaitableEffect,
52
- toAwaitableService,
52
+ ValidationError,
53
+ isEffectError,
53
54
  summarizeZodIssues,
54
55
  toValidationError,
55
56
  toValidationIssues,
@@ -1,19 +1,35 @@
1
1
  import type { AutonomousJobSchedule } from '@lota-sdk/shared'
2
2
  import type { Job } from 'bullmq'
3
- import { Effect } from 'effect'
3
+ import { Effect, Schema } from 'effect'
4
4
  import type { Context } from 'effect'
5
5
  import type IORedis from 'ioredis'
6
6
 
7
7
  import { serverLogger } from '../config/logger'
8
- import { ConfigurationError } from '../effect/errors'
9
8
  import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
10
- import { AutonomousJobServiceTag } from '../services/autonomous-job.service'
9
+ import { QueueJobServiceTag } from '../services/queue-job.service'
11
10
  import { buildAutonomousAtJobId } from '../utils/autonomous-job-ids'
12
- import type { WorkerHandle } from '../workers/worker-utils'
13
- import { DEFAULT_JOB_RETENTION, getQueueJobService } from '../workers/worker-utils'
14
- import { createQueueFactory } from './queue-factory'
11
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
12
+ import { DEFAULT_JOB_RETENTION } from '../workers/worker-utils'
13
+ import { createQueueFactoryWithDeps, recordEnqueuedJobMetadata } from './queue-factory'
15
14
  import { runStandaloneQueueWorker } from './standalone-worker'
16
15
 
16
+ // Minimal service shape used by the queue's worker processor. We declare it
17
+ // structurally to avoid importing `AutonomousJobServiceTag` — that would form a
18
+ // dependency cycle since `AutonomousJobServiceLive` now depends on
19
+ // `LotaQueuesServiceTag` (defined in `queues.service.ts` which imports this
20
+ // module).
21
+ interface AutonomousJobWorkerServiceShape {
22
+ executeQueuedRun(
23
+ job: Job<AutonomousJobQueuePayload>,
24
+ ): Effect.Effect<{ status: string; summary?: string }, unknown, unknown>
25
+ recoverActiveJobs(): Effect.Effect<unknown, unknown, unknown>
26
+ }
27
+
28
+ class AutonomousJobQueueError extends Schema.TaggedErrorClass<AutonomousJobQueueError>()(
29
+ '@lota-sdk/core/AutonomousJobQueueError',
30
+ { message: Schema.String, cause: Schema.optional(Schema.Defect) },
31
+ ) {}
32
+
17
33
  export interface AutonomousJobQueuePayload {
18
34
  autonomousJobId: string
19
35
  autonomousJobRunId?: string
@@ -22,19 +38,9 @@ export interface AutonomousJobQueuePayload {
22
38
 
23
39
  export const AUTONOMOUS_JOB_QUEUE = 'autonomous-job'
24
40
 
25
- interface AutonomousJobQueueDeps {
41
+ export interface AutonomousJobWorkerDeps {
26
42
  databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
27
- autonomousJobService: Context.Service.Shape<typeof AutonomousJobServiceTag>
28
- }
29
-
30
- let _deps: AutonomousJobQueueDeps | null = null
31
- function getDeps(): AutonomousJobQueueDeps {
32
- if (!_deps)
33
- throw new ConfigurationError({
34
- message: 'Autonomous job queue is not configured. Initialize the runtime before starting the worker.',
35
- key: 'queue-deps',
36
- })
37
- return _deps
43
+ autonomousJobService: AutonomousJobWorkerServiceShape
38
44
  }
39
45
 
40
46
  const DEFAULT_AUTONOMOUS_JOB_OPTIONS = {
@@ -43,148 +49,176 @@ const DEFAULT_AUTONOMOUS_JOB_OPTIONS = {
43
49
  backoff: { type: 'exponential', delay: 5_000 },
44
50
  } as const
45
51
 
46
- function processAutonomousJob(job: Job<AutonomousJobQueuePayload>): Promise<{ status: string; summary?: string }> {
47
- return Effect.runPromise(getDeps().autonomousJobService.executeQueuedRun(job))
52
+ function processAutonomousJob(
53
+ deps: AutonomousJobWorkerDeps,
54
+ job: Job<AutonomousJobQueuePayload>,
55
+ ): Promise<{ status: string; summary?: string }> {
56
+ // `executeQueuedRun` is typed with a wide residual context to account for
57
+ // the dynamic thread-turn import inside the service; the actual Effect
58
+ // resolves every tag via the managed runtime that produced this service.
59
+ return Effect.runPromise(
60
+ deps.autonomousJobService.executeQueuedRun(job) as Effect.Effect<
61
+ { status: string; summary?: string },
62
+ never,
63
+ never
64
+ >,
65
+ )
48
66
  }
49
67
 
50
- const autonomousJobQueue = createQueueFactory<AutonomousJobQueuePayload>({
51
- name: AUTONOMOUS_JOB_QUEUE,
52
- displayName: 'Autonomous job',
53
- jobName: 'run-autonomous-job',
54
- concurrency: 2,
55
- defaultJobOptions: DEFAULT_AUTONOMOUS_JOB_OPTIONS,
56
- prepare: () => getDeps().databaseService.connect(),
57
- processor: processAutonomousJob,
58
- })
59
-
60
68
  function buildAutonomousSchedulerId(autonomousJobId: string): string {
61
69
  return `autonomous:${autonomousJobId}`
62
70
  }
63
71
 
64
- export function enqueueAutonomousJobRun(params: {
65
- payload: AutonomousJobQueuePayload
66
- delayMs?: number
67
- jobId?: string
68
- }): Promise<{ bullmqJobId: string; queueJobId?: string }> {
69
- return Effect.runPromise(
70
- Effect.gen(function* () {
71
- const queuedJob = yield* Effect.tryPromise(() =>
72
- autonomousJobQueue
73
- .getQueue()
74
- .add('run-autonomous-job', params.payload, {
75
- ...(typeof params.delayMs === 'number' ? { delay: Math.max(0, params.delayMs) } : {}),
76
- ...(params.jobId ? { jobId: params.jobId } : {}),
77
- }),
78
- )
72
+ interface MakeAutonomousJobQueueRuntimeParams {
73
+ connectionProvider: () => IORedis
74
+ queueJobService: QueueJobService
75
+ }
79
76
 
80
- const bullmqJobId = String(queuedJob.id)
81
- const queueJobId = yield* Effect.catch(
82
- getQueueJobService().recordEnqueued({
83
- queueName: AUTONOMOUS_JOB_QUEUE,
84
- id: queuedJob.id,
85
- name: queuedJob.name,
86
- data: queuedJob.data,
87
- opts: queuedJob.opts,
88
- attemptsMade: queuedJob.attemptsMade,
89
- timestamp: queuedJob.timestamp,
90
- }),
91
- (error) =>
92
- Effect.sync(() => {
93
- serverLogger.error`Failed to persist queued job metadata (queue=${AUTONOMOUS_JOB_QUEUE}, job=${queuedJob.id}): ${error}`
94
- return undefined
95
- }),
96
- )
97
- return { bullmqJobId, queueJobId }
98
- }),
99
- )
77
+ export interface AutonomousJobQueueRuntime {
78
+ enqueueAutonomousJobRun(params: {
79
+ payload: AutonomousJobQueuePayload
80
+ delayMs?: number
81
+ jobId?: string
82
+ }): Promise<{ bullmqJobId: string; queueJobId?: string }>
83
+ upsertAutonomousJobScheduler(params: {
84
+ autonomousJobId: string
85
+ schedule: Extract<AutonomousJobSchedule, { kind: 'cron' | 'every' }>
86
+ }): Promise<void>
87
+ removeAutonomousJobScheduler(autonomousJobId: string): Promise<void>
88
+ removeAutonomousAtJob(autonomousJobId: string): Promise<void>
89
+ startWorker(options: { registerSignals?: boolean; deps: AutonomousJobWorkerDeps }): WorkerHandle
100
90
  }
101
91
 
102
- export function upsertAutonomousJobScheduler(params: {
103
- autonomousJobId: string
104
- schedule: Extract<AutonomousJobSchedule, { kind: 'cron' | 'every' }>
105
- }): Promise<void> {
106
- const repeatOpts =
107
- params.schedule.kind === 'cron' ? { pattern: params.schedule.cron } : { every: params.schedule.intervalMs }
108
- return Effect.runPromise(
109
- Effect.gen(function* () {
110
- const queuedJob = yield* Effect.tryPromise(() =>
111
- autonomousJobQueue
112
- .getQueue()
113
- .upsertJobScheduler(buildAutonomousSchedulerId(params.autonomousJobId), repeatOpts, {
114
- name: 'run-autonomous-job',
115
- data: { autonomousJobId: params.autonomousJobId, trigger: 'scheduled' },
116
- opts: DEFAULT_AUTONOMOUS_JOB_OPTIONS,
117
- }),
118
- )
92
+ export function makeAutonomousJobQueueRuntime(params: MakeAutonomousJobQueueRuntimeParams): AutonomousJobQueueRuntime {
93
+ const { connectionProvider, queueJobService } = params
94
+ const queue = createQueueFactoryWithDeps<AutonomousJobQueuePayload, AutonomousJobWorkerDeps>({
95
+ name: AUTONOMOUS_JOB_QUEUE,
96
+ displayName: 'Autonomous job',
97
+ jobName: 'run-autonomous-job',
98
+ concurrency: 2,
99
+ defaultJobOptions: DEFAULT_AUTONOMOUS_JOB_OPTIONS,
100
+ connectionProvider,
101
+ queueJobService,
102
+ prepare: ({ databaseService }) => Effect.runPromise(databaseService.connect()),
103
+ processor: processAutonomousJob,
104
+ })
119
105
 
120
- yield* Effect.catch(
121
- getQueueJobService().recordEnqueued({
106
+ const enqueueAutonomousJobRun: AutonomousJobQueueRuntime['enqueueAutonomousJobRun'] = (request) =>
107
+ Effect.runPromise(
108
+ Effect.gen(function* () {
109
+ const queuedJob = yield* Effect.tryPromise({
110
+ try: () =>
111
+ queue
112
+ .getQueue()
113
+ .add('run-autonomous-job', request.payload, {
114
+ ...(typeof request.delayMs === 'number' ? { delay: Math.max(0, request.delayMs) } : {}),
115
+ ...(request.jobId ? { jobId: request.jobId } : {}),
116
+ }),
117
+ catch: (cause) => new AutonomousJobQueueError({ message: 'Failed to enqueue autonomous job run.', cause }),
118
+ })
119
+
120
+ const bullmqJobId = String(queuedJob.id)
121
+ const queueJobId = yield* recordEnqueuedJobMetadata({
122
122
  queueName: AUTONOMOUS_JOB_QUEUE,
123
- id: queuedJob.id,
124
- name: queuedJob.name,
125
- data: queuedJob.data,
126
- opts: queuedJob.opts,
127
- attemptsMade: queuedJob.attemptsMade,
128
- timestamp: queuedJob.timestamp,
129
- }),
130
- (error) =>
131
- Effect.sync(() => {
132
- serverLogger.error`Failed to persist queued job metadata (queue=${AUTONOMOUS_JOB_QUEUE}, job=${queuedJob.id}): ${error}`
133
- }),
134
- )
135
- }),
136
- )
137
- }
138
-
139
- export function removeAutonomousJobScheduler(autonomousJobId: string): Promise<void> {
140
- return Effect.runPromise(
141
- Effect.asVoid(
142
- Effect.tryPromise(() =>
143
- autonomousJobQueue.getQueue().removeJobScheduler(buildAutonomousSchedulerId(autonomousJobId)),
144
- ),
145
- ),
146
- )
147
- }
123
+ job: queuedJob,
124
+ queueJobService,
125
+ })
126
+ return { bullmqJobId, queueJobId }
127
+ }),
128
+ )
129
+
130
+ const upsertAutonomousJobScheduler: AutonomousJobQueueRuntime['upsertAutonomousJobScheduler'] = (request) => {
131
+ const repeatOpts =
132
+ request.schedule.kind === 'cron' ? { pattern: request.schedule.cron } : { every: request.schedule.intervalMs }
133
+ return Effect.runPromise(
134
+ Effect.gen(function* () {
135
+ const queuedJob = yield* Effect.tryPromise({
136
+ try: () =>
137
+ queue
138
+ .getQueue()
139
+ .upsertJobScheduler(buildAutonomousSchedulerId(request.autonomousJobId), repeatOpts, {
140
+ name: 'run-autonomous-job',
141
+ data: { autonomousJobId: request.autonomousJobId, trigger: 'scheduled' },
142
+ opts: DEFAULT_AUTONOMOUS_JOB_OPTIONS,
143
+ }),
144
+ catch: (cause) =>
145
+ new AutonomousJobQueueError({
146
+ message: `Failed to upsert autonomous job scheduler for ${request.autonomousJobId}.`,
147
+ cause,
148
+ }),
149
+ })
150
+
151
+ yield* recordEnqueuedJobMetadata({ queueName: AUTONOMOUS_JOB_QUEUE, job: queuedJob, queueJobService })
152
+ }),
153
+ )
154
+ }
148
155
 
149
- export function removeAutonomousAtJob(autonomousJobId: string): Promise<void> {
150
- return Effect.runPromise(
151
- Effect.catch(
156
+ const removeAutonomousJobScheduler: AutonomousJobQueueRuntime['removeAutonomousJobScheduler'] = (autonomousJobId) =>
157
+ Effect.runPromise(
152
158
  Effect.asVoid(
153
- Effect.tryPromise(() => autonomousJobQueue.getQueue().remove(buildAutonomousAtJobId(autonomousJobId))),
159
+ Effect.tryPromise({
160
+ try: () => queue.getQueue().removeJobScheduler(buildAutonomousSchedulerId(autonomousJobId)),
161
+ catch: (cause) =>
162
+ new AutonomousJobQueueError({
163
+ message: `Failed to remove autonomous job scheduler for ${autonomousJobId}.`,
164
+ cause,
165
+ }),
166
+ }),
154
167
  ),
155
- () => Effect.void,
156
- ),
157
- )
158
- }
159
-
160
- interface AutonomousJobWorkerOptions {
161
- registerSignals?: boolean
162
- connectionProvider: () => IORedis
163
- deps: AutonomousJobQueueDeps
164
- }
165
-
166
- export function startAutonomousJobWorker(options: AutonomousJobWorkerOptions): WorkerHandle {
167
- _deps = options.deps
168
- const handle = autonomousJobQueue.startWorker({
169
- registerSignals: options.registerSignals,
170
- connectionProvider: options.connectionProvider,
171
- })
168
+ )
169
+
170
+ const removeAutonomousAtJob: AutonomousJobQueueRuntime['removeAutonomousAtJob'] = (autonomousJobId) =>
171
+ Effect.runPromise(
172
+ Effect.catch(
173
+ Effect.asVoid(
174
+ Effect.tryPromise({
175
+ try: () => queue.getQueue().remove(buildAutonomousAtJobId(autonomousJobId)),
176
+ catch: (cause) =>
177
+ new AutonomousJobQueueError({
178
+ message: `Failed to remove autonomous-at job for ${autonomousJobId}.`,
179
+ cause,
180
+ }),
181
+ }),
182
+ ),
183
+ () => Effect.void,
184
+ ),
185
+ )
172
186
 
173
- void Effect.runPromise(
174
- Effect.catch(options.deps.autonomousJobService.recoverActiveJobs(), (error) =>
175
- Effect.sync(() => {
176
- serverLogger.error`Autonomous job startup recovery failed: ${error}`
177
- }),
178
- ),
179
- )
187
+ const startWorker: AutonomousJobQueueRuntime['startWorker'] = (options) => {
188
+ const { deps } = options
189
+ const handle = queue.startWorker({ deps, registerSignals: options.registerSignals, connectionProvider })
180
190
 
181
- return handle
191
+ void Effect.runPromise(
192
+ Effect.catch(deps.autonomousJobService.recoverActiveJobs(), (error) =>
193
+ Effect.sync(() => {
194
+ serverLogger.error`Autonomous job startup recovery failed: ${error}`
195
+ }),
196
+ ) as Effect.Effect<unknown, never, never>,
197
+ )
198
+
199
+ return handle
200
+ }
201
+
202
+ return {
203
+ enqueueAutonomousJobRun,
204
+ upsertAutonomousJobScheduler,
205
+ removeAutonomousJobScheduler,
206
+ removeAutonomousAtJob,
207
+ startWorker,
208
+ }
182
209
  }
183
210
 
184
211
  runStandaloneQueueWorker((runtime) => {
185
- const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
186
- startAutonomousJobWorker({
187
- connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
188
- deps: { databaseService: resolve(DatabaseServiceTag), autonomousJobService: resolve(AutonomousJobServiceTag) },
189
- })
212
+ void (async () => {
213
+ const { AutonomousJobServiceTag } = await import('../services/autonomous-job.service')
214
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
215
+ const redis = resolve(RedisServiceTag)
216
+ const autonomousJobQueueRuntime = makeAutonomousJobQueueRuntime({
217
+ connectionProvider: () => redis.getConnectionForBullMQ(),
218
+ queueJobService: resolve(QueueJobServiceTag),
219
+ })
220
+ autonomousJobQueueRuntime.startWorker({
221
+ deps: { databaseService: resolve(DatabaseServiceTag), autonomousJobService: resolve(AutonomousJobServiceTag) },
222
+ })
223
+ })()
190
224
  })
@@ -5,11 +5,12 @@ import type IORedis from 'ioredis'
5
5
 
6
6
  import { ensureRecordId } from '../db/record-id'
7
7
  import { TABLES } from '../db/tables'
8
- import { ConfigurationError } from '../effect/errors'
9
8
  import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
10
9
  import { ContextCompactionServiceTag } from '../services/context-compaction.service'
10
+ import { QueueJobServiceTag } from '../services/queue-job.service'
11
11
  import { ThreadServiceTag } from '../services/thread/thread.service'
12
- import { createQueueFactory } from './queue-factory'
12
+ import type { QueueJobService, WorkerHandle } from '../workers/worker-utils'
13
+ import { createQueueFactoryWithDeps } from './queue-factory'
13
14
  import { runStandaloneQueueWorker } from './standalone-worker'
14
15
 
15
16
  interface ContextCompactionJob {
@@ -18,25 +19,14 @@ interface ContextCompactionJob {
18
19
  contextSize?: number
19
20
  }
20
21
 
21
- interface ContextCompactionQueueDeps {
22
+ export interface ContextCompactionWorkerDeps {
22
23
  databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
23
24
  threadService: Context.Service.Shape<typeof ThreadServiceTag>
24
25
  contextCompactionService: Context.Service.Shape<typeof ContextCompactionServiceTag>
25
26
  }
26
27
 
27
- let _deps: ContextCompactionQueueDeps | null = null
28
- function getDeps(): ContextCompactionQueueDeps {
29
- if (!_deps)
30
- throw new ConfigurationError({
31
- message: 'Context compaction queue is not configured. Initialize the runtime before starting the worker.',
32
- key: 'queue-deps',
33
- })
34
- return _deps
35
- }
36
-
37
- function processContextCompactionJob(job: Job<ContextCompactionJob>): Promise<void> {
38
- const { threadService, contextCompactionService } = getDeps()
39
-
28
+ function processContextCompactionJob(deps: ContextCompactionWorkerDeps, job: Job<ContextCompactionJob>): Promise<void> {
29
+ const { threadService, contextCompactionService } = deps
40
30
  const { entityId, contextSize } = job.data
41
31
  const threadRef = ensureRecordId(entityId, TABLES.THREAD)
42
32
  return Effect.runPromise(
@@ -52,37 +42,49 @@ function processContextCompactionJob(job: Job<ContextCompactionJob>): Promise<vo
52
42
  )
53
43
  }
54
44
 
55
- const contextCompaction = createQueueFactory<ContextCompactionJob>({
56
- name: 'context-compaction',
57
- displayName: 'Context compaction',
58
- jobName: 'compact',
59
- concurrency: 2,
60
- lockDuration: 300_000,
61
- defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 3_000 } },
62
- prepare: () => getDeps().databaseService.connect(),
63
- processor: processContextCompactionJob,
64
- })
65
-
66
- export function enqueueContextCompaction(job: ContextCompactionJob) {
67
- return contextCompaction.enqueue(job, { deduplication: { id: `compact:${job.domain}:${job.entityId}` } })
45
+ export interface ContextCompactionQueueRuntime {
46
+ enqueueContextCompaction(job: ContextCompactionJob): Promise<void>
47
+ startWorker(options: { registerSignals?: boolean; deps: ContextCompactionWorkerDeps }): WorkerHandle
68
48
  }
69
49
 
70
- export function startContextCompactionWorker(options: {
71
- registerSignals?: boolean
50
+ interface MakeContextCompactionQueueRuntimeParams {
72
51
  connectionProvider: () => IORedis
73
- deps: ContextCompactionQueueDeps
74
- }): ReturnType<typeof contextCompaction.startWorker> {
75
- _deps = options.deps
76
- return contextCompaction.startWorker({
77
- registerSignals: options.registerSignals,
78
- connectionProvider: options.connectionProvider,
52
+ queueJobService: QueueJobService
53
+ }
54
+
55
+ export function makeContextCompactionQueueRuntime(
56
+ params: MakeContextCompactionQueueRuntimeParams,
57
+ ): ContextCompactionQueueRuntime {
58
+ const { connectionProvider, queueJobService } = params
59
+ const queue = createQueueFactoryWithDeps<ContextCompactionJob, ContextCompactionWorkerDeps>({
60
+ name: 'context-compaction',
61
+ displayName: 'Context compaction',
62
+ jobName: 'compact',
63
+ concurrency: 2,
64
+ lockDuration: 300_000,
65
+ defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 3_000 } },
66
+ connectionProvider,
67
+ queueJobService,
68
+ prepare: ({ databaseService }) => Effect.runPromise(databaseService.connect()),
69
+ processor: processContextCompactionJob,
79
70
  })
71
+
72
+ return {
73
+ enqueueContextCompaction: (job) =>
74
+ queue.enqueue(job, { deduplication: { id: `compact:${job.domain}:${job.entityId}` } }),
75
+ startWorker: (options) =>
76
+ queue.startWorker({ deps: options.deps, registerSignals: options.registerSignals, connectionProvider }),
77
+ }
80
78
  }
81
79
 
82
80
  runStandaloneQueueWorker((runtime) => {
83
81
  const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
84
- startContextCompactionWorker({
85
- connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
82
+ const redis = resolve(RedisServiceTag)
83
+ const contextCompactionQueue = makeContextCompactionQueueRuntime({
84
+ connectionProvider: () => redis.getConnectionForBullMQ(),
85
+ queueJobService: resolve(QueueJobServiceTag),
86
+ })
87
+ contextCompactionQueue.startWorker({
86
88
  deps: {
87
89
  databaseService: resolve(DatabaseServiceTag),
88
90
  threadService: resolve(ThreadServiceTag),