@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
@@ -0,0 +1,41 @@
1
+ import { isSafePath } from '@lota-sdk/shared'
2
+
3
+ import { BadRequestError } from '../effect/errors'
4
+ import { isRecord } from './string'
5
+
6
+ export function createNullProtoRecord(): Record<string, unknown> {
7
+ return Object.create(null) as Record<string, unknown>
8
+ }
9
+
10
+ export function cloneToNullProtoRecord(value: Record<string, unknown>): Record<string, unknown> {
11
+ const clone = createNullProtoRecord()
12
+ for (const [key, entry] of Object.entries(value)) {
13
+ clone[key] = entry
14
+ }
15
+ return clone
16
+ }
17
+
18
+ export function setPathValue(target: Record<string, unknown>, path: string, value: unknown): void {
19
+ if (!isSafePath(path)) {
20
+ throw new BadRequestError({ message: `Unsafe path "${path}"` })
21
+ }
22
+
23
+ const segments = path.split('.').map((part) => part.trim())
24
+ const lastSegment = segments[segments.length - 1]
25
+ if (!lastSegment) {
26
+ throw new BadRequestError({ message: `Unsafe path "${path}"` })
27
+ }
28
+
29
+ let current: Record<string, unknown> = target
30
+ for (const segment of segments.slice(0, -1)) {
31
+ const next = current[segment]
32
+ if (!isRecord(next)) {
33
+ current[segment] = createNullProtoRecord()
34
+ } else if (Object.getPrototypeOf(next) !== null) {
35
+ current[segment] = cloneToNullProtoRecord(next)
36
+ }
37
+ current = current[segment] as Record<string, unknown>
38
+ }
39
+
40
+ current[lastSegment] = value
41
+ }
@@ -1,3 +1,5 @@
1
+ import { Duration, Effect, Fiber } from 'effect'
2
+
1
3
  const KEEPALIVE_COMMENT = new TextEncoder().encode(': keepalive\n\n')
2
4
  const DEFAULT_KEEPALIVE_INTERVAL_MS = 20_000
3
5
 
@@ -10,27 +12,128 @@ export function wrapResponseWithKeepalive(response: Response, intervalMs = DEFAU
10
12
  const body = response.body
11
13
  if (!body) return response
12
14
 
13
- let intervalHandle: ReturnType<typeof setInterval> | null = null
14
-
15
- const transformed = body.pipeThrough(
16
- new TransformStream<Uint8Array, Uint8Array>({
17
- start(controller) {
18
- intervalHandle = setInterval(() => {
19
- try {
20
- controller.enqueue(KEEPALIVE_COMMENT)
21
- } catch {
22
- if (intervalHandle) clearInterval(intervalHandle)
23
- }
24
- }, intervalMs)
25
- },
26
- transform(chunk, controller) {
27
- controller.enqueue(chunk)
28
- },
29
- flush() {
30
- if (intervalHandle) clearInterval(intervalHandle)
31
- },
32
- }),
33
- )
15
+ let closed = false
16
+ let reader: ReadableStreamDefaultReader<Uint8Array> | null = null
17
+ let keepaliveFiber: ReturnType<typeof Effect.runFork> | null = null
18
+ let bodyPumpFiber: ReturnType<typeof Effect.runFork> | null = null
19
+
20
+ const interruptFiber = (fiber: ReturnType<typeof Effect.runFork> | null) => {
21
+ if (!fiber) return
22
+ void Effect.runFork(Fiber.interrupt(fiber))
23
+ }
24
+
25
+ const stopKeepalive = () => {
26
+ const fiber = keepaliveFiber
27
+ keepaliveFiber = null
28
+ interruptFiber(fiber)
29
+ }
30
+
31
+ const stopBodyPump = () => {
32
+ const fiber = bodyPumpFiber
33
+ bodyPumpFiber = null
34
+ interruptFiber(fiber)
35
+ }
36
+
37
+ const releaseReader = (bodyReader: ReadableStreamDefaultReader<Uint8Array>) => {
38
+ try {
39
+ bodyReader.releaseLock()
40
+ } catch {
41
+ // Best-effort cleanup.
42
+ }
43
+ }
44
+
45
+ const enqueueChunk = (controller: ReadableStreamDefaultController<Uint8Array>, chunk: Uint8Array): boolean => {
46
+ if (closed) return false
47
+
48
+ try {
49
+ controller.enqueue(chunk)
50
+ return true
51
+ } catch {
52
+ closed = true
53
+ return false
54
+ }
55
+ }
56
+
57
+ const closeStream = (controller: ReadableStreamDefaultController<Uint8Array>) => {
58
+ if (closed) return
59
+ closed = true
60
+
61
+ try {
62
+ controller.close()
63
+ } catch {
64
+ // Best-effort cleanup.
65
+ }
66
+ }
67
+
68
+ const errorStream = (controller: ReadableStreamDefaultController<Uint8Array>, error: unknown) => {
69
+ if (closed) return
70
+ closed = true
71
+
72
+ try {
73
+ controller.error(error)
74
+ } catch {
75
+ // Best-effort cleanup.
76
+ }
77
+ }
78
+
79
+ const keepaliveEffect = (controller: ReadableStreamDefaultController<Uint8Array>): Effect.Effect<void> =>
80
+ Effect.sleep(Duration.millis(intervalMs)).pipe(
81
+ Effect.flatMap(() => Effect.sync(() => enqueueChunk(controller, KEEPALIVE_COMMENT))),
82
+ Effect.flatMap((shouldContinue) => (shouldContinue ? keepaliveEffect(controller) : Effect.void)),
83
+ )
84
+
85
+ const pumpBodyEffect = (
86
+ bodyReader: ReadableStreamDefaultReader<Uint8Array>,
87
+ controller: ReadableStreamDefaultController<Uint8Array>,
88
+ ): Effect.Effect<void> =>
89
+ Effect.gen(function* () {
90
+ for (;;) {
91
+ if (closed) return
92
+
93
+ const { done, value } = yield* Effect.tryPromise(() => bodyReader.read())
94
+ if (done) {
95
+ yield* Effect.sync(() => closeStream(controller))
96
+ return
97
+ }
98
+
99
+ if (!enqueueChunk(controller, value)) {
100
+ return
101
+ }
102
+ }
103
+ }).pipe(
104
+ Effect.catch((error: unknown) => Effect.sync(() => errorStream(controller, error))),
105
+ Effect.ensuring(
106
+ Effect.sync(() => {
107
+ closed = true
108
+ stopKeepalive()
109
+ bodyPumpFiber = null
110
+ reader = null
111
+ releaseReader(bodyReader)
112
+ }),
113
+ ),
114
+ )
115
+
116
+ const transformed = new ReadableStream<Uint8Array>({
117
+ start(controller) {
118
+ const bodyReader = body.getReader()
119
+ reader = bodyReader
120
+ keepaliveFiber = Effect.runFork(keepaliveEffect(controller))
121
+ bodyPumpFiber = Effect.runFork(pumpBodyEffect(bodyReader, controller))
122
+ },
123
+ cancel(reason) {
124
+ closed = true
125
+ stopKeepalive()
126
+ stopBodyPump()
127
+
128
+ const bodyReader = reader
129
+ reader = null
130
+ if (!bodyReader) {
131
+ return
132
+ }
133
+
134
+ void bodyReader.cancel(reason).catch(() => {})
135
+ },
136
+ })
34
137
 
35
138
  return new Response(transformed, {
36
139
  headers: response.headers,
@@ -1,61 +1,196 @@
1
- import { configureLotaLogger, serverLogger } from '../config/logger'
2
- import { LOTA_SDK_DATABASE_NAME } from '../db/sdk-database'
3
- import { SurrealDBService, databaseService, setDatabaseService } from '../db/service'
1
+ import { Config, ConfigProvider, Schema, Effect, Layer, ManagedRuntime, Redacted } from 'effect'
2
+
3
+ import { AiGatewayLive } from '../ai-gateway/ai-gateway'
4
+ import { EmbeddingCacheLive } from '../ai/embedding-cache'
5
+ import { DEFAULT_AI_GATEWAY_URL } from '../config/constants'
6
+ import { configureLotaLogger, resolveLotaLogLevel, serverLogger } from '../config/logger'
4
7
  import { connectWithStartupRetry, waitForDatabaseBootstrap } from '../db/startup'
5
- import { parseWorkerBootstrapEnv } from '../runtime/runtime-config'
8
+ import { buildWorkerInfrastructureLayer, setLotaSdkRuntime } from '../effect'
9
+ import { DatabaseServiceTag } from '../effect/services'
10
+ import { parseLotaRuntimeConfig, parseWorkerBootstrapEnv } from '../runtime/runtime-config'
6
11
  import { getConfiguredPluginDatabaseConnector } from '../runtime/runtime-extensions'
12
+ import { FirecrawlLive } from '../tools/firecrawl-client'
13
+ import { getErrorMessage } from '../utils/errors'
7
14
 
8
- function ensureDatabaseServiceConfigured(): void {
9
- const env = parseWorkerBootstrapEnv(process.env)
10
- const db = new SurrealDBService({
11
- url: env.SURREALDB_URL,
12
- namespace: env.SURREALDB_NAMESPACE,
13
- database: LOTA_SDK_DATABASE_NAME,
14
- username: env.SURREALDB_USER,
15
- password: env.SURREALDB_PASSWORD,
16
- })
17
- setDatabaseService(db)
15
+ class SandboxedWorkerBootstrapError extends Schema.TaggedErrorClass<SandboxedWorkerBootstrapError>()(
16
+ 'SandboxedWorkerBootstrapError',
17
+ {
18
+ stage: Schema.Literals(['setup', 'initialize', 'connect-db', 'connect-plugin-db', 'bootstrap-wait']),
19
+ message: Schema.String,
20
+ cause: Schema.Defect,
21
+ },
22
+ ) {}
23
+
24
+ function toSandboxedWorkerBootstrapError(
25
+ stage: SandboxedWorkerBootstrapError['stage'],
26
+ cause: unknown,
27
+ ): SandboxedWorkerBootstrapError {
28
+ return new SandboxedWorkerBootstrapError({ stage, message: getErrorMessage(cause), cause })
18
29
  }
19
30
 
20
- let sandboxedWorkerRuntimePromise: Promise<void> | null = null
31
+ const workerConfig = Config.all({
32
+ surrealdbUrl: Config.string('SURREALDB_URL'),
33
+ surrealdbNamespace: Config.string('SURREALDB_NAMESPACE'),
34
+ surrealdbUser: Config.string('SURREALDB_USER'),
35
+ surrealdbPassword: Config.redacted('SURREALDB_PASSWORD'),
36
+ redisUrl: Config.string('REDIS_URL'),
37
+ aiGatewayUrl: Config.string('AI_GATEWAY_URL').pipe(Config.withDefault(DEFAULT_AI_GATEWAY_URL)),
38
+ aiGatewayKey: Config.redacted('AI_GATEWAY_KEY'),
39
+ openRouterApiKey: Config.redacted('OPENROUTER_API_KEY'),
40
+ s3Endpoint: Config.string('S3_ENDPOINT'),
41
+ s3Bucket: Config.string('S3_BUCKET'),
42
+ s3Region: Config.string('S3_REGION').pipe(Config.withDefault('garage')),
43
+ s3AccessKeyId: Config.redacted('S3_ACCESS_KEY_ID'),
44
+ s3SecretAccessKey: Config.redacted('S3_SECRET_ACCESS_KEY'),
45
+ attachmentUrlExpiresIn: Config.string('ATTACHMENT_URL_EXPIRES_IN').pipe(Config.withDefault('1800')),
46
+ firecrawlApiKey: Config.redacted('FIRECRAWL_API_KEY'),
47
+ firecrawlApiBaseUrl: Config.string('FIRECRAWL_API_BASE_URL').pipe(Config.option),
48
+ })
49
+
50
+ function buildSandboxedWorkerRuntimeConfigEffect() {
51
+ return workerConfig
52
+ .parse(ConfigProvider.fromEnv())
53
+ .pipe(
54
+ Effect.map((env) =>
55
+ parseLotaRuntimeConfig({
56
+ database: {
57
+ url: env.surrealdbUrl,
58
+ namespace: env.surrealdbNamespace,
59
+ username: env.surrealdbUser,
60
+ password: Redacted.value(env.surrealdbPassword),
61
+ },
62
+ redis: { url: env.redisUrl },
63
+ aiGateway: {
64
+ url: env.aiGatewayUrl,
65
+ key: Redacted.value(env.aiGatewayKey),
66
+ openRouterApiKey: Redacted.value(env.openRouterApiKey),
67
+ },
68
+ s3: {
69
+ endpoint: env.s3Endpoint,
70
+ bucket: env.s3Bucket,
71
+ region: env.s3Region,
72
+ accessKeyId: Redacted.value(env.s3AccessKeyId),
73
+ secretAccessKey: Redacted.value(env.s3SecretAccessKey),
74
+ attachmentUrlExpiresIn: env.attachmentUrlExpiresIn,
75
+ },
76
+ firecrawl: {
77
+ apiKey: Redacted.value(env.firecrawlApiKey),
78
+ ...(env.firecrawlApiBaseUrl._tag === 'Some' ? { apiBaseUrl: env.firecrawlApiBaseUrl.value } : {}),
79
+ },
80
+ agents: {
81
+ roster: ['worker'],
82
+ leadAgentId: 'worker',
83
+ displayNames: { worker: 'Worker' },
84
+ teamConsultParticipants: [],
85
+ },
86
+ }),
87
+ ),
88
+ )
89
+ }
90
+
91
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- wildcard for host-provided ManagedRuntime
92
+ type WorkerManagedRuntime = ManagedRuntime.ManagedRuntime<any, any>
93
+
94
+ let sandboxedWorkerRuntimePromise: Promise<WorkerManagedRuntime> | null = null
95
+ let sandboxedWorkerRuntime: WorkerManagedRuntime | null = null
96
+ let sandboxedWorkerRuntimeSetupPromise: Promise<WorkerManagedRuntime> | null = null
97
+
98
+ function ensureSandboxedWorkerRuntimeConfigured(): Promise<WorkerManagedRuntime> {
99
+ if (sandboxedWorkerRuntime) {
100
+ return Promise.resolve(sandboxedWorkerRuntime)
101
+ }
102
+
103
+ if (sandboxedWorkerRuntimeSetupPromise) {
104
+ return sandboxedWorkerRuntimeSetupPromise
105
+ }
106
+
107
+ const setupPromise = Effect.runPromise(
108
+ Effect.gen(function* () {
109
+ const runtimeConfig = yield* buildSandboxedWorkerRuntimeConfigEffect()
110
+ yield* Effect.sync(() => configureLotaLogger(resolveLotaLogLevel(Bun.env.LOG_LEVEL) ?? 'info'))
111
+
112
+ const infrastructureLayer = buildWorkerInfrastructureLayer(runtimeConfig)
113
+
114
+ const layer = Layer.mergeAll(infrastructureLayer, Layer.provide(AiGatewayLive, infrastructureLayer))
21
115
 
22
- export async function initializeSandboxedWorkerRuntime(): Promise<void> {
116
+ const fullLayer = Layer.mergeAll(layer, Layer.provide(Layer.mergeAll(EmbeddingCacheLive, FirecrawlLive), layer))
117
+
118
+ const managedRuntime = ManagedRuntime.make(fullLayer)
119
+
120
+ yield* Effect.sync(() => {
121
+ setLotaSdkRuntime(managedRuntime)
122
+ sandboxedWorkerRuntime = managedRuntime
123
+ })
124
+
125
+ return managedRuntime
126
+ }),
127
+ ).finally(() => {
128
+ sandboxedWorkerRuntimeSetupPromise = null
129
+ })
130
+
131
+ sandboxedWorkerRuntimeSetupPromise = setupPromise
132
+ return setupPromise
133
+ }
134
+
135
+ export function initializeSandboxedWorkerRuntime(): Promise<WorkerManagedRuntime> {
23
136
  if (sandboxedWorkerRuntimePromise) {
24
- await sandboxedWorkerRuntimePromise
25
- return
137
+ return sandboxedWorkerRuntimePromise
26
138
  }
27
139
 
28
- sandboxedWorkerRuntimePromise = (async () => {
29
- const env = parseWorkerBootstrapEnv(process.env)
30
- await configureLotaLogger()
31
-
32
- ensureDatabaseServiceConfigured()
33
-
34
- await connectWithStartupRetry({
35
- connect: () => databaseService.connect(),
36
- label: 'sandboxed worker AI database runtime',
37
- logger: serverLogger,
38
- })
39
-
40
- await connectWithStartupRetry({
41
- connect: async () => {
42
- const connectPluginRuntimeDatabases = getConfiguredPluginDatabaseConnector()
43
- if (connectPluginRuntimeDatabases) {
44
- await connectPluginRuntimeDatabases()
45
- }
46
- },
47
- label: 'sandboxed worker plugin database runtime',
48
- logger: serverLogger,
49
- })
50
-
51
- await waitForDatabaseBootstrap({
52
- databaseService,
53
- expectedFingerprint: env.DB_SCHEMA_FINGERPRINT,
54
- label: 'sandboxed worker runtime',
55
- logger: serverLogger,
56
- connect: () => databaseService.connect(),
57
- })
58
- })()
59
-
60
- await sandboxedWorkerRuntimePromise
140
+ const initPromise = Effect.runPromise(
141
+ Effect.gen(function* () {
142
+ const env = parseWorkerBootstrapEnv(Bun.env)
143
+ const runtime = yield* Effect.tryPromise({
144
+ try: () => ensureSandboxedWorkerRuntimeConfigured(),
145
+ catch: (error) => toSandboxedWorkerBootstrapError('setup', error),
146
+ })
147
+ const db = yield* Effect.tryPromise({
148
+ try: () => runtime.runPromise(Effect.service(DatabaseServiceTag)),
149
+ catch: (error) => toSandboxedWorkerBootstrapError('initialize', error),
150
+ })
151
+
152
+ yield* Effect.tryPromise({
153
+ try: () =>
154
+ connectWithStartupRetry({
155
+ connect: () => db.connect(),
156
+ label: 'sandboxed worker AI database runtime',
157
+ logger: serverLogger,
158
+ }),
159
+ catch: (error) => toSandboxedWorkerBootstrapError('connect-db', error),
160
+ })
161
+
162
+ yield* Effect.tryPromise({
163
+ try: () =>
164
+ connectWithStartupRetry({
165
+ connect: () => {
166
+ const connectPluginRuntimeDatabases = getConfiguredPluginDatabaseConnector()
167
+ return connectPluginRuntimeDatabases ? connectPluginRuntimeDatabases() : Promise.resolve()
168
+ },
169
+ label: 'sandboxed worker plugin database runtime',
170
+ logger: serverLogger,
171
+ }),
172
+ catch: (error) => toSandboxedWorkerBootstrapError('connect-plugin-db', error),
173
+ })
174
+
175
+ yield* Effect.tryPromise({
176
+ try: () =>
177
+ waitForDatabaseBootstrap({
178
+ databaseService: db,
179
+ expectedFingerprint: env.DB_SCHEMA_FINGERPRINT,
180
+ label: 'sandboxed worker runtime',
181
+ logger: serverLogger,
182
+ connect: () => db.connect(),
183
+ }),
184
+ catch: (error) => toSandboxedWorkerBootstrapError('bootstrap-wait', error),
185
+ })
186
+
187
+ return runtime
188
+ }),
189
+ )
190
+
191
+ sandboxedWorkerRuntimePromise = initPromise
192
+ return initPromise.catch((error) => {
193
+ sandboxedWorkerRuntimePromise = null
194
+ throw error
195
+ })
61
196
  }