@lota-sdk/core 0.4.8 → 0.4.10

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 (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /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,173 @@
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 { ConfigProvider, Option, 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 { configureLotaLogger, resolveLotaLogLevel, serverLogger } from '../config/logger'
4
6
  import { connectWithStartupRetry, waitForDatabaseBootstrap } from '../db/startup'
5
- import { parseWorkerBootstrapEnv } from '../runtime/runtime-config'
7
+ import { buildWorkerInfrastructureLayer, setLotaSdkRuntime } from '../effect'
8
+ import { DatabaseServiceTag } from '../effect/services'
9
+ import { lotaRuntimeEnvConfig, parseLotaRuntimeConfig, parseWorkerBootstrapEnv } from '../runtime/runtime-config'
6
10
  import { getConfiguredPluginDatabaseConnector } from '../runtime/runtime-extensions'
11
+ import { FirecrawlLive } from '../tools/firecrawl-client'
12
+ import { getErrorMessage } from '../utils/errors'
7
13
 
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)
14
+ class SandboxedWorkerBootstrapError extends Schema.TaggedErrorClass<SandboxedWorkerBootstrapError>()(
15
+ 'SandboxedWorkerBootstrapError',
16
+ {
17
+ stage: Schema.Literals(['setup', 'initialize', 'connect-db', 'connect-plugin-db', 'bootstrap-wait']),
18
+ message: Schema.String,
19
+ cause: Schema.Defect,
20
+ },
21
+ ) {}
22
+
23
+ function toSandboxedWorkerBootstrapError(
24
+ stage: SandboxedWorkerBootstrapError['stage'],
25
+ cause: unknown,
26
+ ): SandboxedWorkerBootstrapError {
27
+ return new SandboxedWorkerBootstrapError({ stage, message: getErrorMessage(cause), cause })
18
28
  }
19
29
 
20
- let sandboxedWorkerRuntimePromise: Promise<void> | null = null
30
+ function buildSandboxedWorkerRuntimeConfigEffect() {
31
+ return lotaRuntimeEnvConfig
32
+ .parse(ConfigProvider.fromEnv())
33
+ .pipe(
34
+ Effect.map((env) =>
35
+ parseLotaRuntimeConfig({
36
+ database: {
37
+ url: env.surrealdbUrl,
38
+ namespace: env.surrealdbNamespace,
39
+ username: env.surrealdbUser,
40
+ password: Redacted.value(env.surrealdbPassword),
41
+ },
42
+ redis: { url: env.redisUrl },
43
+ aiGateway: {
44
+ url: env.aiGatewayUrl,
45
+ key: Redacted.value(env.aiGatewayKey),
46
+ ...(Option.isSome(env.openRouterApiKey)
47
+ ? { openRouterApiKey: Redacted.value(env.openRouterApiKey.value) }
48
+ : {}),
49
+ },
50
+ s3: {
51
+ endpoint: env.s3Endpoint,
52
+ bucket: env.s3Bucket,
53
+ region: env.s3Region,
54
+ accessKeyId: Redacted.value(env.s3AccessKeyId),
55
+ secretAccessKey: Redacted.value(env.s3SecretAccessKey),
56
+ attachmentUrlExpiresIn: env.attachmentUrlExpiresIn,
57
+ },
58
+ firecrawl: {
59
+ apiKey: Redacted.value(env.firecrawlApiKey),
60
+ ...(Option.isSome(env.firecrawlApiBaseUrl) ? { apiBaseUrl: env.firecrawlApiBaseUrl.value } : {}),
61
+ },
62
+ agents: {
63
+ roster: ['worker'],
64
+ leadAgentId: 'worker',
65
+ displayNames: { worker: 'Worker' },
66
+ teamConsultParticipants: [],
67
+ },
68
+ }),
69
+ ),
70
+ )
71
+ }
72
+
73
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- wildcard for host-provided ManagedRuntime
74
+ type WorkerManagedRuntime = ManagedRuntime.ManagedRuntime<any, any>
75
+
76
+ let sandboxedWorkerSetupPromise: Promise<WorkerManagedRuntime> | null = null
77
+ let sandboxedWorkerInitPromise: Promise<WorkerManagedRuntime> | null = null
78
+
79
+ function ensureSandboxedWorkerRuntimeConfigured(): Promise<WorkerManagedRuntime> {
80
+ if (sandboxedWorkerSetupPromise) {
81
+ return sandboxedWorkerSetupPromise
82
+ }
83
+
84
+ // Assign before the async kicks off so concurrent callers observe the in-flight promise.
85
+ sandboxedWorkerSetupPromise = Effect.runPromise(
86
+ Effect.gen(function* () {
87
+ const runtimeConfig = yield* buildSandboxedWorkerRuntimeConfigEffect()
88
+ yield* Effect.sync(() => configureLotaLogger(resolveLotaLogLevel(Bun.env.LOG_LEVEL) ?? 'info'))
89
+
90
+ const infrastructureLayer = buildWorkerInfrastructureLayer(runtimeConfig)
91
+
92
+ const layer = Layer.mergeAll(infrastructureLayer, Layer.provide(AiGatewayLive, infrastructureLayer))
93
+
94
+ const fullLayer = Layer.mergeAll(layer, Layer.provide(Layer.mergeAll(EmbeddingCacheLive, FirecrawlLive), layer))
21
95
 
22
- export async function initializeSandboxedWorkerRuntime(): Promise<void> {
23
- if (sandboxedWorkerRuntimePromise) {
24
- await sandboxedWorkerRuntimePromise
25
- return
96
+ const managedRuntime = ManagedRuntime.make(fullLayer)
97
+
98
+ yield* Effect.sync(() => {
99
+ setLotaSdkRuntime(managedRuntime)
100
+ })
101
+
102
+ return managedRuntime
103
+ }),
104
+ ).catch((error) => {
105
+ sandboxedWorkerSetupPromise = null
106
+ throw error
107
+ })
108
+
109
+ return sandboxedWorkerSetupPromise
110
+ }
111
+
112
+ export function initializeSandboxedWorkerRuntime(): Promise<WorkerManagedRuntime> {
113
+ if (sandboxedWorkerInitPromise) {
114
+ return sandboxedWorkerInitPromise
26
115
  }
27
116
 
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
117
+ // Assign before the async kicks off so concurrent callers observe the in-flight promise.
118
+ sandboxedWorkerInitPromise = Effect.runPromise(
119
+ Effect.gen(function* () {
120
+ const env = parseWorkerBootstrapEnv(Bun.env)
121
+ const runtime = yield* Effect.tryPromise({
122
+ try: () => ensureSandboxedWorkerRuntimeConfigured(),
123
+ catch: (error) => toSandboxedWorkerBootstrapError('setup', error),
124
+ })
125
+ const db = yield* Effect.tryPromise({
126
+ try: () => runtime.runPromise(Effect.service(DatabaseServiceTag)),
127
+ catch: (error) => toSandboxedWorkerBootstrapError('initialize', error),
128
+ })
129
+
130
+ yield* Effect.tryPromise({
131
+ try: () =>
132
+ connectWithStartupRetry({
133
+ connect: () => db.connect(),
134
+ label: 'sandboxed worker AI database runtime',
135
+ logger: serverLogger,
136
+ }),
137
+ catch: (error) => toSandboxedWorkerBootstrapError('connect-db', error),
138
+ })
139
+
140
+ yield* Effect.tryPromise({
141
+ try: () =>
142
+ connectWithStartupRetry({
143
+ connect: () => {
144
+ const connectPluginRuntimeDatabases = getConfiguredPluginDatabaseConnector()
145
+ return connectPluginRuntimeDatabases ? connectPluginRuntimeDatabases() : Promise.resolve()
146
+ },
147
+ label: 'sandboxed worker plugin database runtime',
148
+ logger: serverLogger,
149
+ }),
150
+ catch: (error) => toSandboxedWorkerBootstrapError('connect-plugin-db', error),
151
+ })
152
+
153
+ yield* Effect.tryPromise({
154
+ try: () =>
155
+ waitForDatabaseBootstrap({
156
+ databaseService: db,
157
+ expectedFingerprint: env.DB_SCHEMA_FINGERPRINT,
158
+ label: 'sandboxed worker runtime',
159
+ logger: serverLogger,
160
+ connect: () => db.connect(),
161
+ }),
162
+ catch: (error) => toSandboxedWorkerBootstrapError('bootstrap-wait', error),
163
+ })
164
+
165
+ return runtime
166
+ }),
167
+ ).catch((error) => {
168
+ sandboxedWorkerInitPromise = null
169
+ throw error
170
+ })
171
+
172
+ return sandboxedWorkerInitPromise
61
173
  }