@lota-sdk/core 0.4.7 → 0.4.9

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 (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
package/src/index.ts CHANGED
@@ -6,10 +6,55 @@ export * from './db'
6
6
  export * from './document'
7
7
  export * from './queues'
8
8
  export * from './redis'
9
- export * from './runtime/index'
9
+ export * from './runtime'
10
10
  export * from './services'
11
11
  export * from './storage'
12
12
  export * from './system-agents'
13
13
  export * from './tools'
14
14
  export * from './utils'
15
15
  export * from './workers'
16
+ export { Effect } from 'effect'
17
+ export {
18
+ AgentConfigLive,
19
+ AgentFactoryLive,
20
+ AppLoggerTag,
21
+ AppLoggerLive,
22
+ EffectBadRequestError,
23
+ clearLotaSdkRuntime,
24
+ ConfigurationError,
25
+ ConflictError,
26
+ DatabaseError,
27
+ DatabaseLive,
28
+ DatabaseServiceTag as EffectDatabaseService,
29
+ EffectNotFoundError,
30
+ EffectThreadTurnError,
31
+ EffectValidationError,
32
+ getLotaSdkRuntime,
33
+ isEffectError,
34
+ LockAcquisitionError,
35
+ LockLostError,
36
+ ActiveThreadRunConflictError,
37
+ RedisError,
38
+ RedisLive,
39
+ RedisServiceTag as EffectRedisService,
40
+ RuntimeAdaptersServiceTag,
41
+ RuntimeConfigLive,
42
+ RuntimeConfigServiceTag,
43
+ RuntimeExtensionsLive,
44
+ RuntimeWorkerExtensionsServiceTag,
45
+ setLotaSdkRuntime,
46
+ ThreadConfigLive,
47
+ ThreadConfigServiceTag,
48
+ TimeoutError,
49
+ ToolProvidersServiceTag,
50
+ TurnHooksServiceTag,
51
+ toAwaitableEffect,
52
+ toAwaitableService,
53
+ summarizeZodIssues,
54
+ toValidationError,
55
+ toValidationIssues,
56
+ zodParse,
57
+ } from './effect'
58
+ export { AppError, BadRequestError, NotFoundError } from './utils'
59
+ export type { AppErrorLike, AppErrorResponse } from './utils'
60
+ export type { EffectError, ValidationIssue } from './effect'
@@ -0,0 +1,29 @@
1
+ import { createOpenAI } from '@ai-sdk/openai'
2
+
3
+ import { ConfigurationError } from '../effect/errors'
4
+
5
+ const DIRECT_OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1' as const
6
+ const OPENROUTER_MODEL_PREFIX = 'openrouter/' as const
7
+
8
+ export function resolveOpenRouterApiKey(openRouterApiKey: string | undefined): string {
9
+ const key = openRouterApiKey?.trim()
10
+ if (key) return key
11
+
12
+ throw new ConfigurationError({
13
+ message: 'Missing OpenRouter API key. Configure createLotaRuntime({ aiGateway: { openRouterApiKey } }).',
14
+ key: 'aiGateway.openRouterApiKey',
15
+ })
16
+ }
17
+
18
+ export function normalizeDirectOpenRouterModelId(modelId: string): string {
19
+ const normalized = modelId.trim()
20
+ if (!normalized) {
21
+ throw new ConfigurationError({ message: 'OpenRouter model id is required.', key: 'openRouterModelId' })
22
+ }
23
+
24
+ return normalized.startsWith(OPENROUTER_MODEL_PREFIX) ? normalized.slice(OPENROUTER_MODEL_PREFIX.length) : normalized
25
+ }
26
+
27
+ export function getDirectOpenRouterProvider(openRouterApiKey?: string) {
28
+ return createOpenAI({ baseURL: DIRECT_OPENROUTER_BASE_URL, apiKey: resolveOpenRouterApiKey(openRouterApiKey) })
29
+ }
@@ -1,14 +1,18 @@
1
1
  import type { AutonomousJobSchedule } from '@lota-sdk/shared'
2
2
  import type { Job } from 'bullmq'
3
+ import { Effect } from 'effect'
4
+ import type { Context } from 'effect'
5
+ import type IORedis from 'ioredis'
3
6
 
4
7
  import { serverLogger } from '../config/logger'
5
- import { databaseService } from '../db/service'
6
- import { autonomousJobService } from '../services/autonomous-job.service'
7
- import { queueJobService } from '../services/queue-job.service'
8
+ import { ConfigurationError } from '../effect/errors'
9
+ import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
10
+ import { AutonomousJobServiceTag } from '../services/autonomous-job.service'
8
11
  import { buildAutonomousAtJobId } from '../utils/autonomous-job-ids'
9
12
  import type { WorkerHandle } from '../workers/worker-utils'
10
- import { DEFAULT_JOB_RETENTION } from '../workers/worker-utils'
13
+ import { DEFAULT_JOB_RETENTION, getQueueJobService } from '../workers/worker-utils'
11
14
  import { createQueueFactory } from './queue-factory'
15
+ import { runStandaloneQueueWorker } from './standalone-worker'
12
16
 
13
17
  export interface AutonomousJobQueuePayload {
14
18
  autonomousJobId: string
@@ -18,17 +22,29 @@ export interface AutonomousJobQueuePayload {
18
22
 
19
23
  export const AUTONOMOUS_JOB_QUEUE = 'autonomous-job'
20
24
 
25
+ interface AutonomousJobQueueDeps {
26
+ 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
38
+ }
39
+
21
40
  const DEFAULT_AUTONOMOUS_JOB_OPTIONS = {
22
41
  ...DEFAULT_JOB_RETENTION,
23
42
  attempts: 3,
24
43
  backoff: { type: 'exponential', delay: 5_000 },
25
44
  } as const
26
45
 
27
- async function processAutonomousJob(
28
- job: Job<AutonomousJobQueuePayload>,
29
- ): Promise<{ status: string; summary?: string }> {
30
- await databaseService.connect()
31
- return autonomousJobService.executeQueuedRun(job)
46
+ function processAutonomousJob(job: Job<AutonomousJobQueuePayload>): Promise<{ status: string; summary?: string }> {
47
+ return Effect.runPromise(getDeps().autonomousJobService.executeQueuedRun(job))
32
48
  }
33
49
 
34
50
  const autonomousJobQueue = createQueueFactory<AutonomousJobQueuePayload>({
@@ -37,6 +53,7 @@ const autonomousJobQueue = createQueueFactory<AutonomousJobQueuePayload>({
37
53
  jobName: 'run-autonomous-job',
38
54
  concurrency: 2,
39
55
  defaultJobOptions: DEFAULT_AUTONOMOUS_JOB_OPTIONS,
56
+ prepare: () => getDeps().databaseService.connect(),
40
57
  processor: processAutonomousJob,
41
58
  })
42
59
 
@@ -44,91 +61,130 @@ function buildAutonomousSchedulerId(autonomousJobId: string): string {
44
61
  return `autonomous:${autonomousJobId}`
45
62
  }
46
63
 
47
- export async function enqueueAutonomousJobRun(params: {
64
+ export function enqueueAutonomousJobRun(params: {
48
65
  payload: AutonomousJobQueuePayload
49
66
  delayMs?: number
50
67
  jobId?: string
51
68
  }): Promise<{ bullmqJobId: string; queueJobId?: string }> {
52
- const queuedJob = await autonomousJobQueue
53
- .getQueue()
54
- .add('run-autonomous-job', params.payload, {
55
- ...(typeof params.delayMs === 'number' ? { delay: Math.max(0, params.delayMs) } : {}),
56
- ...(params.jobId ? { jobId: params.jobId } : {}),
57
- })
58
-
59
- const bullmqJobId = String(queuedJob.id)
60
- let queueJobId: string | undefined
61
-
62
- try {
63
- queueJobId = await queueJobService.recordEnqueued({
64
- queueName: AUTONOMOUS_JOB_QUEUE,
65
- id: queuedJob.id,
66
- name: queuedJob.name,
67
- data: queuedJob.data,
68
- opts: queuedJob.opts,
69
- attemptsMade: queuedJob.attemptsMade,
70
- timestamp: queuedJob.timestamp,
71
- })
72
- } catch (error) {
73
- serverLogger.error`Failed to persist queued job metadata (queue=${AUTONOMOUS_JOB_QUEUE}, job=${queuedJob.id}): ${error}`
74
- }
75
-
76
- return { bullmqJobId, queueJobId }
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
+ )
79
+
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
100
  }
78
101
 
79
- export async function upsertAutonomousJobScheduler(params: {
102
+ export function upsertAutonomousJobScheduler(params: {
80
103
  autonomousJobId: string
81
104
  schedule: Extract<AutonomousJobSchedule, { kind: 'cron' | 'every' }>
82
105
  }): Promise<void> {
83
106
  const repeatOpts =
84
107
  params.schedule.kind === 'cron' ? { pattern: params.schedule.cron } : { every: params.schedule.intervalMs }
85
- const queuedJob = await autonomousJobQueue
86
- .getQueue()
87
- .upsertJobScheduler(buildAutonomousSchedulerId(params.autonomousJobId), repeatOpts, {
88
- name: 'run-autonomous-job',
89
- data: { autonomousJobId: params.autonomousJobId, trigger: 'scheduled' },
90
- opts: DEFAULT_AUTONOMOUS_JOB_OPTIONS,
91
- })
92
-
93
- try {
94
- await queueJobService.recordEnqueued({
95
- queueName: AUTONOMOUS_JOB_QUEUE,
96
- id: queuedJob.id,
97
- name: queuedJob.name,
98
- data: queuedJob.data,
99
- opts: queuedJob.opts,
100
- attemptsMade: queuedJob.attemptsMade,
101
- timestamp: queuedJob.timestamp,
102
- })
103
- } catch (error) {
104
- serverLogger.error`Failed to persist queued job metadata (queue=${AUTONOMOUS_JOB_QUEUE}, job=${queuedJob.id}): ${error}`
105
- }
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
+ )
119
+
120
+ yield* Effect.catch(
121
+ getQueueJobService().recordEnqueued({
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
+ )
106
137
  }
107
138
 
108
- export async function removeAutonomousJobScheduler(autonomousJobId: string): Promise<void> {
109
- await autonomousJobQueue.getQueue().removeJobScheduler(buildAutonomousSchedulerId(autonomousJobId))
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
+ )
110
147
  }
111
148
 
112
- export async function removeAutonomousAtJob(autonomousJobId: string): Promise<void> {
113
- try {
114
- await autonomousJobQueue.getQueue().remove(buildAutonomousAtJobId(autonomousJobId))
115
- } catch {
116
- // The delayed job may have already fired or never existed.
117
- }
149
+ export function removeAutonomousAtJob(autonomousJobId: string): Promise<void> {
150
+ return Effect.runPromise(
151
+ Effect.catch(
152
+ Effect.asVoid(
153
+ Effect.tryPromise(() => autonomousJobQueue.getQueue().remove(buildAutonomousAtJobId(autonomousJobId))),
154
+ ),
155
+ () => Effect.void,
156
+ ),
157
+ )
118
158
  }
119
159
 
120
- type AutonomousJobWorkerOptions = Parameters<typeof autonomousJobQueue.startWorker>[0]
121
-
122
- export function startAutonomousJobWorker(options: AutonomousJobWorkerOptions = {}): WorkerHandle {
123
- const handle = autonomousJobQueue.startWorker(options)
160
+ interface AutonomousJobWorkerOptions {
161
+ registerSignals?: boolean
162
+ connectionProvider: () => IORedis
163
+ deps: AutonomousJobQueueDeps
164
+ }
124
165
 
125
- autonomousJobService.recoverActiveJobs().catch((error: unknown) => {
126
- serverLogger.error`Autonomous job startup recovery failed: ${error}`
166
+ export function startAutonomousJobWorker(options: AutonomousJobWorkerOptions): WorkerHandle {
167
+ _deps = options.deps
168
+ const handle = autonomousJobQueue.startWorker({
169
+ registerSignals: options.registerSignals,
170
+ connectionProvider: options.connectionProvider,
127
171
  })
128
172
 
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
+ )
180
+
129
181
  return handle
130
182
  }
131
183
 
132
- if (import.meta.main) {
133
- startAutonomousJobWorker()
134
- }
184
+ 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
+ })
190
+ })
@@ -1,11 +1,16 @@
1
1
  import type { Job } from 'bullmq'
2
+ import { Effect } from 'effect'
3
+ import type { Context } from 'effect'
4
+ import type IORedis from 'ioredis'
2
5
 
3
6
  import { ensureRecordId } from '../db/record-id'
4
- import { databaseService } from '../db/service'
5
7
  import { TABLES } from '../db/tables'
6
- import { contextCompactionService } from '../services/context-compaction.service'
7
- import { threadService } from '../services/thread.service'
8
+ import { ConfigurationError } from '../effect/errors'
9
+ import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
10
+ import { ContextCompactionServiceTag } from '../services/context-compaction.service'
11
+ import { ThreadServiceTag } from '../services/thread/thread.service'
8
12
  import { createQueueFactory } from './queue-factory'
13
+ import { runStandaloneQueueWorker } from './standalone-worker'
9
14
 
10
15
  interface ContextCompactionJob {
11
16
  domain: 'thread'
@@ -13,17 +18,38 @@ interface ContextCompactionJob {
13
18
  contextSize?: number
14
19
  }
15
20
 
16
- async function processContextCompactionJob(job: Job<ContextCompactionJob>): Promise<void> {
17
- await databaseService.connect()
21
+ interface ContextCompactionQueueDeps {
22
+ databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
23
+ threadService: Context.Service.Shape<typeof ThreadServiceTag>
24
+ contextCompactionService: Context.Service.Shape<typeof ContextCompactionServiceTag>
25
+ }
26
+
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()
18
39
 
19
40
  const { entityId, contextSize } = job.data
20
41
  const threadRef = ensureRecordId(entityId, TABLES.THREAD)
21
- await threadService.setCompacting(threadRef, true)
22
- try {
23
- await contextCompactionService.compactThreadHistory({ threadId: threadRef, contextSize })
24
- } finally {
25
- await threadService.setCompacting(threadRef, false)
26
- }
42
+ return Effect.runPromise(
43
+ Effect.ensuring(
44
+ Effect.asVoid(
45
+ Effect.gen(function* () {
46
+ yield* threadService.setCompacting(threadRef, true)
47
+ yield* contextCompactionService.compactThreadHistory({ threadId: threadRef, contextSize })
48
+ }),
49
+ ),
50
+ Effect.ignore(threadService.setCompacting(threadRef, false)),
51
+ ),
52
+ )
27
53
  }
28
54
 
29
55
  const contextCompaction = createQueueFactory<ContextCompactionJob>({
@@ -33,6 +59,7 @@ const contextCompaction = createQueueFactory<ContextCompactionJob>({
33
59
  concurrency: 2,
34
60
  lockDuration: 300_000,
35
61
  defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 3_000 } },
62
+ prepare: () => getDeps().databaseService.connect(),
36
63
  processor: processContextCompactionJob,
37
64
  })
38
65
 
@@ -40,8 +67,26 @@ export function enqueueContextCompaction(job: ContextCompactionJob) {
40
67
  return contextCompaction.enqueue(job, { deduplication: { id: `compact:${job.domain}:${job.entityId}` } })
41
68
  }
42
69
 
43
- export const startContextCompactionWorker = contextCompaction.startWorker
44
-
45
- if (import.meta.main) {
46
- startContextCompactionWorker()
70
+ export function startContextCompactionWorker(options: {
71
+ registerSignals?: boolean
72
+ 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,
79
+ })
47
80
  }
81
+
82
+ runStandaloneQueueWorker((runtime) => {
83
+ const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
84
+ startContextCompactionWorker({
85
+ connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
86
+ deps: {
87
+ databaseService: resolve(DatabaseServiceTag),
88
+ threadService: resolve(ThreadServiceTag),
89
+ contextCompactionService: resolve(ContextCompactionServiceTag),
90
+ },
91
+ })
92
+ })
@@ -1,8 +1,13 @@
1
1
  import type { Job } from 'bullmq'
2
+ import { Effect } from 'effect'
3
+ import type { Context } from 'effect'
4
+ import type IORedis from 'ioredis'
2
5
 
3
- import { databaseService } from '../db/service'
4
- import { planExecutorService } from '../services/plan-executor.service'
6
+ import { ConfigurationError } from '../effect/errors'
7
+ import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
8
+ import { PlanExecutorServiceTag } from '../services/plan/plan-executor.service'
5
9
  import { createQueueFactory } from './queue-factory'
10
+ import { runStandaloneQueueWorker } from './standalone-worker'
6
11
 
7
12
  export interface DelayedNodePromotionJob {
8
13
  runId: string
@@ -12,13 +17,30 @@ export interface DelayedNodePromotionJob {
12
17
 
13
18
  export const DELAYED_NODE_PROMOTION_QUEUE = 'delayed-node-promotion'
14
19
 
15
- async function processDelayedNodePromotionJob(job: Job<DelayedNodePromotionJob>): Promise<void> {
16
- await databaseService.connect()
17
- await planExecutorService.promoteDelayedNode({
18
- runId: job.data.runId,
19
- nodeId: job.data.nodeId,
20
- emittedBy: job.data.emittedBy,
21
- })
20
+ interface DelayedNodePromotionQueueDeps {
21
+ databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
22
+ planExecutorService: Context.Service.Shape<typeof PlanExecutorServiceTag>
23
+ }
24
+
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()
37
+ return Promise.resolve(
38
+ planExecutorService.promoteDelayedNode({
39
+ runId: job.data.runId,
40
+ nodeId: job.data.nodeId,
41
+ emittedBy: job.data.emittedBy,
42
+ }),
43
+ ).then(() => undefined)
22
44
  }
23
45
 
24
46
  const delayedNodePromotion = createQueueFactory<DelayedNodePromotionJob>({
@@ -27,15 +49,30 @@ const delayedNodePromotion = createQueueFactory<DelayedNodePromotionJob>({
27
49
  jobName: 'promote-node',
28
50
  concurrency: 1,
29
51
  defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 5000 } },
52
+ prepare: () => getDeps().databaseService.connect(),
30
53
  processor: processDelayedNodePromotionJob,
31
54
  })
32
55
 
33
- export async function enqueueDelayedNodePromotion(job: DelayedNodePromotionJob, delayMs: number) {
34
- await delayedNodePromotion.enqueue(job, { delay: delayMs, jobId: `promote:${job.runId}:${job.nodeId}` })
56
+ export function enqueueDelayedNodePromotion(job: DelayedNodePromotionJob, delayMs: number) {
57
+ return delayedNodePromotion.enqueue(job, { delay: delayMs, jobId: `promote:${job.runId}:${job.nodeId}` })
35
58
  }
36
59
 
37
- export const startDelayedNodePromotionWorker = delayedNodePromotion.startWorker
38
-
39
- if (import.meta.main) {
40
- startDelayedNodePromotionWorker()
60
+ export function startDelayedNodePromotionWorker(options: {
61
+ registerSignals?: boolean
62
+ 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,
69
+ })
41
70
  }
71
+
72
+ 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
+ })
78
+ })