@lota-sdk/core 0.4.8 → 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/thread-defaults.ts +33 -21
  9. package/src/create-runtime.ts +725 -387
  10. package/src/db/base.service.ts +52 -28
  11. package/src/db/cursor-pagination.ts +71 -30
  12. package/src/db/memory-store.helpers.ts +4 -7
  13. package/src/db/memory-store.ts +856 -598
  14. package/src/db/memory.ts +398 -275
  15. package/src/db/record-id.ts +32 -10
  16. package/src/db/schema-fingerprint.ts +30 -12
  17. package/src/db/service-normalization.ts +255 -0
  18. package/src/db/service.ts +726 -761
  19. package/src/db/startup.ts +140 -66
  20. package/src/db/transaction-conflict.ts +15 -0
  21. package/src/effect/awaitable-effect.ts +87 -0
  22. package/src/effect/errors.ts +121 -0
  23. package/src/effect/helpers.ts +98 -0
  24. package/src/effect/index.ts +22 -0
  25. package/src/effect/layers.ts +228 -0
  26. package/src/effect/runtime-ref.ts +25 -0
  27. package/src/effect/runtime.ts +31 -0
  28. package/src/effect/services.ts +57 -0
  29. package/src/effect/zod.ts +43 -0
  30. package/src/embeddings/provider.ts +122 -76
  31. package/src/index.ts +46 -1
  32. package/src/openrouter/direct-provider.ts +11 -35
  33. package/src/queues/autonomous-job.queue.ts +130 -74
  34. package/src/queues/context-compaction.queue.ts +60 -15
  35. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  36. package/src/queues/document-processor.queue.ts +52 -77
  37. package/src/queues/memory-consolidation.queue.ts +47 -32
  38. package/src/queues/organization-learning.queue.ts +13 -4
  39. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  40. package/src/queues/plan-scheduler.queue.ts +107 -31
  41. package/src/queues/post-chat-memory.queue.ts +66 -24
  42. package/src/queues/queue-factory.ts +142 -52
  43. package/src/queues/standalone-worker.ts +39 -0
  44. package/src/queues/title-generation.queue.ts +54 -9
  45. package/src/redis/connection.ts +84 -32
  46. package/src/redis/index.ts +6 -8
  47. package/src/redis/org-memory-lock.ts +60 -27
  48. package/src/redis/redis-lease-lock.ts +200 -121
  49. package/src/redis/runtime-connection.ts +10 -0
  50. package/src/redis/stream-context.ts +84 -46
  51. package/src/runtime/agent-identity-overrides.ts +2 -2
  52. package/src/runtime/agent-runtime-policy.ts +4 -1
  53. package/src/runtime/agent-stream-helpers.ts +20 -9
  54. package/src/runtime/chat-run-orchestration.ts +102 -19
  55. package/src/runtime/chat-run-registry.ts +36 -2
  56. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  57. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  58. package/src/runtime/execution-plan-visibility.ts +2 -2
  59. package/src/runtime/execution-plan.ts +42 -15
  60. package/src/runtime/graph-designer.ts +11 -7
  61. package/src/runtime/helper-model.ts +135 -48
  62. package/src/runtime/index.ts +7 -7
  63. package/src/runtime/indexed-repositories-policy.ts +3 -3
  64. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  65. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  66. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  67. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  68. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  69. package/src/runtime/plugin-resolution.ts +144 -24
  70. package/src/runtime/plugin-types.ts +9 -1
  71. package/src/runtime/post-turn-side-effects.ts +197 -130
  72. package/src/runtime/retrieval-adapters.ts +38 -4
  73. package/src/runtime/runtime-config.ts +150 -61
  74. package/src/runtime/runtime-extensions.ts +21 -34
  75. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  76. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  77. package/src/runtime/social-chat/social-chat.ts +594 -0
  78. package/src/runtime/specialist-runner.ts +36 -10
  79. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  80. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  81. package/src/runtime/thread-chat-helpers.ts +2 -2
  82. package/src/runtime/thread-plan-turn.ts +2 -1
  83. package/src/runtime/thread-turn-context.ts +172 -94
  84. package/src/runtime/turn-lifecycle.ts +93 -27
  85. package/src/services/agent-activity.service.ts +287 -203
  86. package/src/services/agent-executor.service.ts +329 -217
  87. package/src/services/artifact.service.ts +225 -148
  88. package/src/services/attachment.service.ts +137 -115
  89. package/src/services/autonomous-job.service.ts +888 -491
  90. package/src/services/chat-run-registry.service.ts +11 -1
  91. package/src/services/context-compaction.service.ts +136 -86
  92. package/src/services/document-chunk.service.ts +162 -90
  93. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  94. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  95. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  96. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  97. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  98. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  99. package/src/services/feedback-loop.service.ts +132 -76
  100. package/src/services/global-orchestrator.service.ts +80 -170
  101. package/src/services/graph-full-routing.ts +182 -0
  102. package/src/services/index.ts +18 -21
  103. package/src/services/institutional-memory.service.ts +220 -123
  104. package/src/services/learned-skill.service.ts +364 -259
  105. package/src/services/memory/memory-conversation.ts +95 -0
  106. package/src/services/memory/memory-org-memory.ts +39 -0
  107. package/src/services/memory/memory-preseeded.ts +80 -0
  108. package/src/services/memory/memory-rerank.ts +297 -0
  109. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  110. package/src/services/memory/memory.service.ts +692 -0
  111. package/src/services/memory/rerank.service.ts +209 -0
  112. package/src/services/monitoring-window.service.ts +92 -70
  113. package/src/services/mutating-approval.service.ts +62 -53
  114. package/src/services/node-workspace.service.ts +141 -98
  115. package/src/services/notification.service.ts +17 -16
  116. package/src/services/organization-member.service.ts +120 -66
  117. package/src/services/organization.service.ts +144 -51
  118. package/src/services/ownership-dispatcher.service.ts +415 -264
  119. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  120. package/src/services/plan/plan-agent-query.service.ts +322 -0
  121. package/src/services/plan/plan-approval.service.ts +102 -0
  122. package/src/services/plan/plan-artifact.service.ts +60 -0
  123. package/src/services/plan/plan-builder.service.ts +76 -0
  124. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  125. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  126. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  127. package/src/services/plan/plan-coordination.service.ts +181 -0
  128. package/src/services/plan/plan-cycle.service.ts +398 -0
  129. package/src/services/plan/plan-deadline.service.ts +547 -0
  130. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  131. package/src/services/plan/plan-executor-context.ts +35 -0
  132. package/src/services/plan/plan-executor-graph.ts +475 -0
  133. package/src/services/plan/plan-executor-helpers.ts +322 -0
  134. package/src/services/plan/plan-executor-persistence.ts +209 -0
  135. package/src/services/plan/plan-executor.service.ts +1654 -0
  136. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  137. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  138. package/src/services/plan/plan-run-serialization.ts +15 -0
  139. package/src/services/plan/plan-run.service.ts +644 -0
  140. package/src/services/plan/plan-scheduler.service.ts +385 -0
  141. package/src/services/plan/plan-template.service.ts +224 -0
  142. package/src/services/plan/plan-transaction-events.ts +33 -0
  143. package/src/services/plan/plan-validator.service.ts +907 -0
  144. package/src/services/plan/plan-workspace.service.ts +125 -0
  145. package/src/services/plugin-executor.service.ts +97 -68
  146. package/src/services/quality-metrics.service.ts +112 -94
  147. package/src/services/queue-job.service.ts +296 -230
  148. package/src/services/recent-activity-title.service.ts +65 -36
  149. package/src/services/recent-activity.service.ts +274 -259
  150. package/src/services/skill-resolver.service.ts +38 -12
  151. package/src/services/social-chat-history.service.ts +176 -125
  152. package/src/services/system-executor.service.ts +91 -61
  153. package/src/services/thread/thread-active-run.ts +203 -0
  154. package/src/services/thread/thread-bootstrap.ts +369 -0
  155. package/src/services/thread/thread-listing.ts +198 -0
  156. package/src/services/thread/thread-memory-block.ts +117 -0
  157. package/src/services/thread/thread-message.service.ts +363 -0
  158. package/src/services/thread/thread-record-store.ts +155 -0
  159. package/src/services/thread/thread-title.service.ts +74 -0
  160. package/src/services/thread/thread-turn-execution.ts +280 -0
  161. package/src/services/thread/thread-turn-message-context.ts +73 -0
  162. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  163. package/src/services/thread/thread-turn-streaming.ts +402 -0
  164. package/src/services/thread/thread-turn-tracing.ts +35 -0
  165. package/src/services/thread/thread-turn.ts +343 -0
  166. package/src/services/thread/thread.service.ts +335 -0
  167. package/src/services/user.service.ts +82 -32
  168. package/src/services/write-intent-validator.service.ts +63 -51
  169. package/src/storage/attachment-parser.ts +69 -27
  170. package/src/storage/attachment-storage.service.ts +331 -275
  171. package/src/storage/generated-document-storage.service.ts +66 -34
  172. package/src/system-agents/agent-result.ts +3 -1
  173. package/src/system-agents/context-compaction.agent.ts +2 -2
  174. package/src/system-agents/delegated-agent-factory.ts +159 -90
  175. package/src/system-agents/memory-reranker.agent.ts +2 -2
  176. package/src/system-agents/memory.agent.ts +2 -2
  177. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  178. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  179. package/src/system-agents/skill-extractor.agent.ts +2 -2
  180. package/src/system-agents/skill-manager.agent.ts +2 -2
  181. package/src/system-agents/thread-router.agent.ts +157 -113
  182. package/src/system-agents/title-generator.agent.ts +2 -2
  183. package/src/tools/execution-plan.tool.ts +220 -161
  184. package/src/tools/fetch-webpage.tool.ts +21 -17
  185. package/src/tools/firecrawl-client.ts +16 -6
  186. package/src/tools/index.ts +1 -0
  187. package/src/tools/memory-block.tool.ts +14 -6
  188. package/src/tools/plan-approval.tool.ts +49 -47
  189. package/src/tools/read-file-parts.tool.ts +44 -33
  190. package/src/tools/remember-memory.tool.ts +65 -45
  191. package/src/tools/search-web.tool.ts +26 -22
  192. package/src/tools/search.tool.ts +41 -29
  193. package/src/tools/team-think.tool.ts +124 -83
  194. package/src/tools/user-questions.tool.ts +4 -3
  195. package/src/tools/web-tool-shared.ts +6 -0
  196. package/src/utils/async.ts +17 -23
  197. package/src/utils/crypto.ts +21 -0
  198. package/src/utils/date-time.ts +40 -1
  199. package/src/utils/errors.ts +95 -16
  200. package/src/utils/hono-error-handler.ts +24 -39
  201. package/src/utils/index.ts +2 -1
  202. package/src/utils/null-proto-record.ts +41 -0
  203. package/src/utils/sse-keepalive.ts +124 -21
  204. package/src/workers/bootstrap.ts +186 -51
  205. package/src/workers/memory-consolidation.worker.ts +325 -237
  206. package/src/workers/organization-learning.worker.ts +50 -16
  207. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  208. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  209. package/src/workers/skill-extraction.runner.ts +176 -93
  210. package/src/workers/utils/file-section-chunker.ts +8 -10
  211. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  212. package/src/workers/utils/repomix-file-sections.ts +2 -2
  213. package/src/workers/utils/thread-message-query.ts +97 -38
  214. package/src/workers/worker-utils.ts +56 -31
  215. package/src/config/debug-logger.ts +0 -47
  216. package/src/redis/connection-accessor.ts +0 -26
  217. package/src/runtime/context-compaction-runtime.ts +0 -87
  218. package/src/runtime/social-chat-agent-runner.ts +0 -118
  219. package/src/runtime/social-chat.ts +0 -516
  220. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  221. package/src/services/adaptive-playbook.service.ts +0 -152
  222. package/src/services/artifact-provenance.service.ts +0 -172
  223. package/src/services/chat-attachments.service.ts +0 -17
  224. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  225. package/src/services/execution-plan.service.ts +0 -1118
  226. package/src/services/memory.service.ts +0 -914
  227. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  228. package/src/services/plan-agent-query.service.ts +0 -267
  229. package/src/services/plan-approval.service.ts +0 -83
  230. package/src/services/plan-artifact.service.ts +0 -50
  231. package/src/services/plan-builder.service.ts +0 -67
  232. package/src/services/plan-checkpoint.service.ts +0 -81
  233. package/src/services/plan-completion-side-effects.ts +0 -80
  234. package/src/services/plan-coordination.service.ts +0 -157
  235. package/src/services/plan-cycle.service.ts +0 -284
  236. package/src/services/plan-deadline.service.ts +0 -430
  237. package/src/services/plan-event-delivery.service.ts +0 -166
  238. package/src/services/plan-executor.service.ts +0 -1950
  239. package/src/services/plan-run.service.ts +0 -515
  240. package/src/services/plan-scheduler.service.ts +0 -240
  241. package/src/services/plan-template.service.ts +0 -177
  242. package/src/services/plan-validator.service.ts +0 -818
  243. package/src/services/plan-workspace.service.ts +0 -83
  244. package/src/services/rerank.service.ts +0 -156
  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'
@@ -1,53 +1,29 @@
1
1
  import { createOpenAI } from '@ai-sdk/openai'
2
2
 
3
- import { getRuntimeConfig } from '../runtime/runtime-config'
3
+ import { ConfigurationError } from '../effect/errors'
4
4
 
5
5
  const DIRECT_OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1' as const
6
6
  const OPENROUTER_MODEL_PREFIX = 'openrouter/' as const
7
7
 
8
- let directOpenRouterProvider: ReturnType<typeof createOpenAI> | null = null
9
- let directOpenRouterProviderKey: string | null = null
8
+ export function resolveOpenRouterApiKey(openRouterApiKey: string | undefined): string {
9
+ const key = openRouterApiKey?.trim()
10
+ if (key) return key
10
11
 
11
- function readConfiguredOpenRouterApiKey(): string | null {
12
- try {
13
- return getRuntimeConfig().aiGateway.openRouterApiKey?.trim() || null
14
- } catch {
15
- return null
16
- }
17
- }
18
-
19
- export function resolveOpenRouterApiKey(): string {
20
- const configured = readConfiguredOpenRouterApiKey()
21
- if (configured) return configured
22
-
23
- const envKey = process.env.OPENROUTER_API_KEY?.trim()
24
- if (envKey) return envKey
25
-
26
- throw new Error('Missing OpenRouter API key. Set aiGateway.openRouterApiKey or OPENROUTER_API_KEY.')
12
+ throw new ConfigurationError({
13
+ message: 'Missing OpenRouter API key. Configure createLotaRuntime({ aiGateway: { openRouterApiKey } }).',
14
+ key: 'aiGateway.openRouterApiKey',
15
+ })
27
16
  }
28
17
 
29
18
  export function normalizeDirectOpenRouterModelId(modelId: string): string {
30
19
  const normalized = modelId.trim()
31
20
  if (!normalized) {
32
- throw new Error('OpenRouter model id is required.')
21
+ throw new ConfigurationError({ message: 'OpenRouter model id is required.', key: 'openRouterModelId' })
33
22
  }
34
23
 
35
24
  return normalized.startsWith(OPENROUTER_MODEL_PREFIX) ? normalized.slice(OPENROUTER_MODEL_PREFIX.length) : normalized
36
25
  }
37
26
 
38
- export function getDirectOpenRouterProvider() {
39
- const apiKey = resolveOpenRouterApiKey()
40
- if (directOpenRouterProvider && directOpenRouterProviderKey === apiKey) {
41
- return directOpenRouterProvider
42
- }
43
-
44
- directOpenRouterProvider = createOpenAI({ baseURL: DIRECT_OPENROUTER_BASE_URL, apiKey })
45
- directOpenRouterProviderKey = apiKey
46
-
47
- return directOpenRouterProvider
48
- }
49
-
50
- export function resetDirectOpenRouterProviderForTests(): void {
51
- directOpenRouterProvider = null
52
- directOpenRouterProviderKey = null
27
+ export function getDirectOpenRouterProvider(openRouterApiKey?: string) {
28
+ return createOpenAI({ baseURL: DIRECT_OPENROUTER_BASE_URL, apiKey: resolveOpenRouterApiKey(openRouterApiKey) })
53
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
+ })