@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
@@ -34,9 +34,9 @@ export function parseRepomixFileSections(repomixOutput: string): FileSection[] {
34
34
  return sections
35
35
  }
36
36
 
37
- export async function chunkRepomixFileSections(
37
+ export function chunkRepomixFileSections(
38
38
  repomixOutput: string,
39
39
  options: FileSectionChunkOptions = {},
40
- ): Promise<FileSectionChunk[]> {
40
+ ): FileSectionChunk[] {
41
41
  return chunkFileSections(parseRepomixFileSections(repomixOutput), options)
42
42
  }
@@ -1,93 +1,152 @@
1
- import { requireTimestamp } from '@lota-sdk/shared'
1
+ import { parseRowMetadata, requireTimestamp } from '@lota-sdk/shared'
2
+ import type { ChatMessage } from '@lota-sdk/shared'
3
+ import { Schema, Effect } from 'effect'
2
4
  import { BoundQuery } from 'surrealdb'
3
5
  import { z } from 'zod'
4
6
 
5
7
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
6
8
  import type { RecordIdRef } from '../../db/record-id'
7
- import { databaseService } from '../../db/service'
9
+ import type { SurrealDBService } from '../../db/service'
8
10
  import { TABLES } from '../../db/tables'
9
11
  import { ThreadMessageRowSchema } from '../../db/thread-message-row'
10
12
  import type { ThreadMessageRow } from '../../db/thread-message-row'
11
13
  import { normalizeTextBody } from '../../document/parsing'
14
+ import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
12
15
  import type { LotaRuntimeBackgroundCursor } from '../../runtime/runtime-extensions'
16
+ import type { SocialChatHistoryMessage } from '../../services/social-chat-history.service'
17
+ import { unsafeDateFrom } from '../../utils/date-time'
13
18
 
14
- export type DigestCursor = LotaRuntimeBackgroundCursor
15
-
16
- export interface DigestMessage {
17
- source: 'thread' | 'social'
19
+ export interface ThreadDigestMessage {
20
+ source: 'thread'
18
21
  sourceId: string
19
22
  role: 'system' | 'user' | 'assistant'
20
- parts: Array<Record<string, unknown>>
21
- metadata?: Record<string, unknown>
22
- cursor: DigestCursor
23
+ parts: ChatMessage['parts']
24
+ metadata?: ChatMessage['metadata']
25
+ cursor: LotaRuntimeBackgroundCursor
23
26
  }
24
27
 
25
- function mapThreadRow(row: ThreadMessageRow): DigestMessage {
28
+ export type SocialDigestMessage = Pick<
29
+ SocialChatHistoryMessage,
30
+ 'source' | 'sourceId' | 'role' | 'parts' | 'metadata' | 'cursor'
31
+ >
32
+ export type DigestMessage = ThreadDigestMessage | SocialDigestMessage
33
+
34
+ function normalizeMessageValue(value: unknown): unknown {
35
+ if (value instanceof Date) {
36
+ return value.toISOString()
37
+ }
38
+
39
+ if (Array.isArray(value)) {
40
+ return value.map((item) => normalizeMessageValue(item))
41
+ }
42
+
43
+ if (value && typeof value === 'object') {
44
+ return Object.fromEntries(
45
+ Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, normalizeMessageValue(entry)]),
46
+ )
47
+ }
48
+
49
+ return value
50
+ }
51
+
52
+ function readPersistedMessageParts(parts: ThreadMessageRow['parts']): ChatMessage['parts'] {
53
+ return (Array.isArray(parts) ? normalizeMessageValue(parts) : []) as ChatMessage['parts']
54
+ }
55
+
56
+ class ThreadMessageQueryError extends Schema.TaggedErrorClass<ThreadMessageQueryError>()('ThreadMessageQueryError', {
57
+ message: Schema.String,
58
+ cause: Schema.Defect,
59
+ }) {}
60
+
61
+ function mapThreadRow(row: ThreadMessageRow): ThreadDigestMessage {
26
62
  return {
27
63
  source: 'thread',
28
64
  sourceId: recordIdToString(row.threadId, TABLES.THREAD),
29
65
  role: row.role,
30
- parts: row.parts as Array<Record<string, unknown>>,
31
- metadata: row.metadata ?? undefined,
66
+ parts: readPersistedMessageParts(row.parts),
67
+ metadata: parseRowMetadata(row.metadata),
32
68
  cursor: {
33
- createdAt: new Date(requireTimestamp(row.createdAt)),
69
+ createdAt: unsafeDateFrom(requireTimestamp(row.createdAt)),
34
70
  id: recordIdToString(row.id, TABLES.THREAD_MESSAGE),
35
71
  },
36
72
  }
37
73
  }
38
74
 
75
+ const effectTryPromise = makeEffectTryPromiseWithMessage(
76
+ (message, cause) => new ThreadMessageQueryError({ message, cause }),
77
+ )
78
+
39
79
  export function compareDigestMessageOrder(left: DigestMessage, right: DigestMessage): number {
40
80
  const timeDiff = left.cursor.createdAt.getTime() - right.cursor.createdAt.getTime()
41
81
  if (timeDiff !== 0) return timeDiff
42
82
  return left.cursor.id.localeCompare(right.cursor.id)
43
83
  }
44
84
 
45
- export async function listThreadIdsForOrg(orgRef: RecordIdRef): Promise<RecordIdRef[]> {
85
+ export function listThreadIdsForOrg(db: SurrealDBService, orgRef: RecordIdRef): Promise<RecordIdRef[]> {
46
86
  const EntityIdRowSchema = z.string().trim().min(1)
47
- const ids = await databaseService.query<unknown>(
48
- new BoundQuery(
49
- `SELECT VALUE type::string(id) FROM ${TABLES.THREAD}
87
+ return Effect.runPromise(
88
+ effectTryPromise(
89
+ () =>
90
+ db.query<unknown>(
91
+ new BoundQuery(
92
+ `SELECT VALUE type::string(id) FROM ${TABLES.THREAD}
50
93
  WHERE organizationId = $organizationId`,
51
- { organizationId: orgRef },
94
+ { organizationId: orgRef },
95
+ ),
96
+ ),
97
+ 'Failed to list thread ids for org digest.',
98
+ ).pipe(
99
+ Effect.map((ids) => ids.map((value: unknown) => ensureRecordId(EntityIdRowSchema.parse(value), TABLES.THREAD))),
52
100
  ),
53
101
  )
54
- return ids.map((value) => ensureRecordId(EntityIdRowSchema.parse(value), TABLES.THREAD))
55
102
  }
56
103
 
57
- export async function listEligibleThreadMessages(params: {
104
+ export function listEligibleThreadMessages(params: {
105
+ db: SurrealDBService
58
106
  threadIds: RecordIdRef[]
59
- cursor: DigestCursor | null
107
+ cursor: LotaRuntimeBackgroundCursor | null
60
108
  onboardingCutoff: Date | null
61
109
  }): Promise<DigestMessage[]> {
62
- if (params.threadIds.length === 0) return []
110
+ if (params.threadIds.length === 0) return Promise.resolve([])
63
111
 
64
- let query: BoundQuery | null = null
65
- if (params.cursor) {
66
- const cursorRowId = ensureRecordId(params.cursor.id, TABLES.THREAD_MESSAGE)
67
- query = new BoundQuery(
68
- `SELECT type::string(id) AS id, type::string(threadId) AS threadId, role, parts, metadata, createdAt FROM ${TABLES.THREAD_MESSAGE}
112
+ return Effect.runPromise(
113
+ Effect.gen(function* () {
114
+ const query = params.cursor
115
+ ? new BoundQuery(
116
+ `SELECT type::string(id) AS id, type::string(threadId) AS threadId, role, parts, metadata, createdAt FROM ${TABLES.THREAD_MESSAGE}
69
117
  WHERE threadId IN $threadIds
70
118
  AND (
71
119
  createdAt > $cursorCreatedAt
72
120
  OR (createdAt = $cursorCreatedAt AND id > $cursorRowId)
73
121
  )
74
122
  ORDER BY createdAt ASC, id ASC`,
75
- { threadIds: params.threadIds, cursorCreatedAt: params.cursor.createdAt, cursorRowId },
76
- )
77
- } else if (params.onboardingCutoff) {
78
- query = new BoundQuery(
79
- `SELECT type::string(id) AS id, type::string(threadId) AS threadId, role, parts, metadata, createdAt FROM ${TABLES.THREAD_MESSAGE}
123
+ {
124
+ threadIds: params.threadIds,
125
+ cursorCreatedAt: params.cursor.createdAt,
126
+ cursorRowId: ensureRecordId(params.cursor.id, TABLES.THREAD_MESSAGE),
127
+ },
128
+ )
129
+ : params.onboardingCutoff
130
+ ? new BoundQuery(
131
+ `SELECT type::string(id) AS id, type::string(threadId) AS threadId, role, parts, metadata, createdAt FROM ${TABLES.THREAD_MESSAGE}
80
132
  WHERE threadId IN $threadIds
81
133
  AND createdAt > $onboardingCutoff
82
134
  ORDER BY createdAt ASC, id ASC`,
83
- { threadIds: params.threadIds, onboardingCutoff: params.onboardingCutoff },
84
- )
85
- }
135
+ { threadIds: params.threadIds, onboardingCutoff: params.onboardingCutoff },
136
+ )
137
+ : null
86
138
 
87
- if (!query) return []
139
+ if (!query) {
140
+ return []
141
+ }
88
142
 
89
- const rows = await databaseService.query<unknown>(query)
90
- return rows.map((row) => mapThreadRow(ThreadMessageRowSchema.parse(row)))
143
+ const rows = yield* effectTryPromise(
144
+ () => params.db.query<unknown>(query),
145
+ 'Failed to list eligible thread messages.',
146
+ )
147
+ return rows.map((row: unknown) => mapThreadRow(ThreadMessageRowSchema.parse(row)))
148
+ }),
149
+ )
91
150
  }
92
151
 
93
152
  export function normalizeBlock(value: string): string {
@@ -1,10 +1,27 @@
1
- import path from 'node:path'
2
1
  import { fileURLToPath } from 'node:url'
3
2
 
4
3
  import type { Job, Worker } from 'bullmq'
4
+ import { Effect } from 'effect'
5
+ import type { Context } from 'effect'
5
6
 
6
7
  import { chatLogger } from '../config/logger'
7
- import { queueJobService } from '../services/queue-job.service'
8
+ import { resolveLotaService } from '../effect/runtime'
9
+ import { QueueJobServiceTag } from '../services/queue-job.service'
10
+
11
+ let currentQueueJobService: Context.Service.Shape<typeof QueueJobServiceTag> | null = null
12
+
13
+ export function configureQueueJobService(service: Context.Service.Shape<typeof QueueJobServiceTag>): void {
14
+ currentQueueJobService = service
15
+ }
16
+
17
+ export function clearQueueJobService(): void {
18
+ currentQueueJobService = null
19
+ }
20
+
21
+ export function getQueueJobService(): Context.Service.Shape<typeof QueueJobServiceTag> {
22
+ return currentQueueJobService ?? resolveLotaService(QueueJobServiceTag)
23
+ }
24
+
8
25
  export const DEFAULT_JOB_RETENTION = { removeOnComplete: true, removeOnFail: { age: 24 * 60 * 60, count: 200 } }
9
26
  export const LOW_JOB_RETENTION = { removeOnComplete: true, removeOnFail: { age: 6 * 60 * 60, count: 50 } }
10
27
  export const LONG_JOB_LOCK_DURATION_MS = 600_000
@@ -12,7 +29,7 @@ export const LONG_JOB_LOCK_DURATION_MS = 600_000
12
29
  const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000
13
30
 
14
31
  export function getWorkerPath(workerName: string): string {
15
- return fileURLToPath(new URL(path.join('.', workerName), import.meta.url))
32
+ return fileURLToPath(new URL(workerName, import.meta.url))
16
33
  }
17
34
 
18
35
  export interface WorkerHandle {
@@ -72,9 +89,9 @@ export const attachWorkerEvents = (worker: Worker, name: string, logger: typeof
72
89
  }
73
90
 
74
91
  export const createWorkerShutdown = (worker: Worker, name: string, logger: typeof chatLogger = chatLogger) => {
75
- return async () => {
92
+ return () => {
76
93
  logger.info`Shutting down ${name} worker`
77
- await worker.close()
94
+ return Effect.runPromise(Effect.asVoid(Effect.tryPromise(() => worker.close())))
78
95
  }
79
96
  }
80
97
 
@@ -82,7 +99,7 @@ export function createTracedWorkerProcessor<TJob extends TracedWorkerJobLike, TR
82
99
  queueName: string,
83
100
  processor: (job: TJob) => Promise<TResult>,
84
101
  ): (job: TJob) => Promise<TResult> {
85
- return async (job: TJob) => {
102
+ return (job: TJob) => {
86
103
  const trackedJob = {
87
104
  queueName,
88
105
  id: typeof job.id === 'string' || typeof job.id === 'number' ? job.id : undefined,
@@ -93,31 +110,52 @@ export function createTracedWorkerProcessor<TJob extends TracedWorkerJobLike, TR
93
110
  timestamp: job.timestamp,
94
111
  }
95
112
 
96
- try {
97
- await queueJobService.markAttemptStarted(trackedJob)
98
- } catch (error) {
99
- chatLogger.error`Failed to persist queue job start (queue=${queueName}, job=${job.id}): ${error}`
113
+ const logPersistenceError = (phase: string, error: unknown) => {
114
+ chatLogger.error`Failed to persist queue job ${phase} (queue=${queueName}, job=${job.id}): ${error}`
100
115
  }
101
116
 
102
- try {
103
- const result = await processor(job)
104
- try {
105
- await queueJobService.markAttemptCompleted(trackedJob, result)
106
- } catch (error) {
107
- chatLogger.error`Failed to persist queue job completion (queue=${queueName}, job=${job.id}): ${error}`
108
- }
109
- return result
110
- } catch (error) {
111
- try {
112
- await queueJobService.markAttemptFailed(trackedJob, error)
113
- } catch (persistenceError) {
114
- chatLogger.error`Failed to persist queue job failure (queue=${queueName}, job=${job.id}): ${persistenceError}`
115
- }
116
- throw error
117
- }
117
+ return Effect.runPromise(
118
+ Effect.catch(
119
+ Effect.gen(function* () {
120
+ yield* Effect.catch(getQueueJobService().markAttemptStarted(trackedJob), (error) =>
121
+ Effect.sync(() => {
122
+ logPersistenceError('start', error)
123
+ }),
124
+ )
125
+
126
+ const result = yield* Effect.tryPromise(() => processor(job))
127
+
128
+ yield* Effect.catch(getQueueJobService().markAttemptCompleted(trackedJob, result), (error) =>
129
+ Effect.sync(() => {
130
+ logPersistenceError('completion', error)
131
+ }),
132
+ )
133
+
134
+ return result
135
+ }),
136
+ (error) =>
137
+ Effect.gen(function* () {
138
+ yield* Effect.catch(getQueueJobService().markAttemptFailed(trackedJob, error), (persistenceError) =>
139
+ Effect.sync(() => {
140
+ logPersistenceError('failure', persistenceError)
141
+ }),
142
+ )
143
+ return yield* error
144
+ }),
145
+ ).pipe(
146
+ Effect.withSpan(`worker.${queueName}.${job.name}`),
147
+ Effect.annotateSpans({
148
+ 'job.id': trackedJob.id !== undefined ? String(trackedJob.id) : 'unknown',
149
+ 'job.name': job.name,
150
+ 'job.attemptsMade': typeof job.attemptsMade === 'number' ? job.attemptsMade : -1,
151
+ 'queue.name': queueName,
152
+ }),
153
+ ),
154
+ )
118
155
  }
119
156
  }
120
157
 
158
+ /** Registers process signal handlers for sandboxed workers that run outside an Effect scope. */
121
159
  export const registerShutdownSignals = ({
122
160
  name,
123
161
  shutdown,
@@ -130,21 +168,26 @@ export const registerShutdownSignals = ({
130
168
  timeoutMs?: number
131
169
  }) => {
132
170
  let shuttingDown = false
171
+ let forcedExitArmed = true
133
172
 
134
173
  const handleSignal = () => {
135
174
  if (shuttingDown) return
136
175
  shuttingDown = true
137
- const timeout = setTimeout(() => {
138
- logger.warn`Forced shutdown after ${timeoutMs}ms`
139
- process.exit(0)
140
- }, timeoutMs)
176
+ void Effect.runFork(
177
+ Effect.gen(function* () {
178
+ yield* Effect.tryPromise(() => Bun.sleep(timeoutMs))
179
+ if (!forcedExitArmed) return
180
+ logger.warn`Forced shutdown after ${timeoutMs}ms`
181
+ process.exit(0)
182
+ }),
183
+ )
141
184
 
142
185
  shutdown()
143
186
  .catch((err: unknown) => {
144
187
  logger.error`Failed to shutdown ${name} worker: ${err}`
145
188
  })
146
189
  .finally(() => {
147
- clearTimeout(timeout)
190
+ forcedExitArmed = false
148
191
  process.exit(0)
149
192
  })
150
193
  }
@@ -1,47 +0,0 @@
1
- import { chatLogger } from './logger'
2
-
3
- const isDebug = (): boolean => {
4
- const level = process.env.LOG_LEVEL
5
- const flag = process.env.LOTA_DEBUG
6
- return level === 'debug' || flag === '1'
7
- }
8
-
9
- interface DebugTimer {
10
- step(name: string): void
11
- elapsed(): number
12
- }
13
-
14
- const NOOP_TIMER: DebugTimer = { step() {}, elapsed: () => 0 }
15
-
16
- function createTimer(label: string): DebugTimer {
17
- const start = performance.now()
18
- let lastStep = start
19
-
20
- return {
21
- step(name: string) {
22
- const now = performance.now()
23
- const stepMs = now - lastStep
24
- const totalMs = now - start
25
- chatLogger.debug`[ttft:${label}] ${name}: ${stepMs.toFixed(1)}ms (elapsed: ${totalMs.toFixed(1)}ms)`
26
- lastStep = now
27
- },
28
- elapsed() {
29
- return performance.now() - start
30
- },
31
- }
32
- }
33
-
34
- export const lotaDebugLogger = {
35
- get enabled() {
36
- return isDebug()
37
- },
38
-
39
- timer(label: string): DebugTimer {
40
- return isDebug() ? createTimer(label) : NOOP_TIMER
41
- },
42
-
43
- step(name: string) {
44
- if (!isDebug()) return
45
- chatLogger.debug`[ttft] ${name}`
46
- },
47
- }
@@ -1,3 +0,0 @@
1
- export { VECTOR_SEARCH_OVERFETCH_MULTIPLIER } from '@lota-sdk/shared'
2
-
3
- export const DEFAULT_MEMORY_SEARCH_LIMIT = 10
@@ -1,26 +0,0 @@
1
- import type IORedis from 'ioredis'
2
-
3
- export interface RedisConnectionAccessor {
4
- getConnection(): IORedis
5
- getConnectionForBullMQ(): IORedis
6
- }
7
-
8
- let redisManager: RedisConnectionAccessor | undefined
9
-
10
- export function setRedisConnectionManager(manager: RedisConnectionAccessor): void {
11
- redisManager = manager
12
- }
13
-
14
- export function getRedisConnection(): IORedis {
15
- if (!redisManager) {
16
- throw new Error('Redis connection manager not configured. Call setRedisConnectionManager() first.')
17
- }
18
- return redisManager.getConnection()
19
- }
20
-
21
- export function getRedisConnectionForBullMQ(): IORedis {
22
- if (!redisManager) {
23
- throw new Error('Redis connection manager not configured. Call setRedisConnectionManager() first.')
24
- }
25
- return redisManager.getConnectionForBullMQ()
26
- }
@@ -1 +0,0 @@
1
- export type { ChatMode, CreateHelperToolLoopAgentOptions, CreateRoutedAgentOptions } from '@lota-sdk/shared'
@@ -1,87 +0,0 @@
1
- import { createContextCompactionAgent } from '../system-agents/context-compaction.agent'
2
- import {
3
- buildContextCompactionPrompt,
4
- buildMemoryBlockCompactionPrompt,
5
- ContextCompactionOutputSchema,
6
- createContextCompactionRuntime,
7
- parseCompactionOutput,
8
- } from './context-compaction'
9
- import type { ContextCompactionRunnerParams } from './context-compaction'
10
- import {
11
- COMPACTION_CHUNK_MAX_CHARS,
12
- CONTEXT_COMPACTION_INCLUDED_TOOL_NAMES,
13
- CONTEXT_COMPACTION_INCLUDED_TOOL_PREFIXES,
14
- CONTEXT_COMPACTION_THRESHOLD_RATIO,
15
- CONTEXT_OUTPUT_RESERVE_TOKENS,
16
- CONTEXT_SAFETY_MARGIN_TOKENS,
17
- SUMMARY_ROLLUP_MAX_TOKENS,
18
- } from './context-compaction-constants'
19
- import type { GenerateHelperStructuredParams, GenerateHelperTextParams } from './helper-model'
20
-
21
- const CONTEXT_COMPACTION_MAX_OUTPUT_TOKENS = 512
22
-
23
- interface HelperModelRuntime {
24
- generateHelperStructured<T>(params: GenerateHelperStructuredParams<T>): Promise<T>
25
- generateHelperText(params: GenerateHelperTextParams): Promise<string>
26
- }
27
-
28
- interface CreateContextCompactionRuntimeDeps {
29
- helperModelRuntime: HelperModelRuntime
30
- now?: () => number
31
- randomId?: () => string
32
- }
33
-
34
- async function runContextCompacter(helperModelRuntime: HelperModelRuntime, params: ContextCompactionRunnerParams) {
35
- const output = await helperModelRuntime.generateHelperStructured({
36
- tag: 'context-compaction',
37
- createAgent: createContextCompactionAgent,
38
- messages: [
39
- {
40
- role: 'user',
41
- content: buildContextCompactionPrompt({
42
- previousSummary: params.previousSummary,
43
- transcript: params.transcript,
44
- }),
45
- },
46
- ],
47
- schema: ContextCompactionOutputSchema,
48
- maxOutputTokens: 8_000,
49
- })
50
-
51
- return parseCompactionOutput(output)
52
- }
53
-
54
- export function createWiredContextCompactionRuntime(deps: CreateContextCompactionRuntimeDeps) {
55
- const { helperModelRuntime } = deps
56
-
57
- const runtime = createContextCompactionRuntime({
58
- runCompacter: (params) => runContextCompacter(helperModelRuntime, params),
59
- now: deps.now,
60
- randomId: deps.randomId,
61
- thresholdRatio: CONTEXT_COMPACTION_THRESHOLD_RATIO,
62
- outputReserveTokens: CONTEXT_OUTPUT_RESERVE_TOKENS,
63
- safetyMarginTokens: CONTEXT_SAFETY_MARGIN_TOKENS,
64
- compactionChunkMaxChars: COMPACTION_CHUNK_MAX_CHARS,
65
- summaryRollupMaxTokens: SUMMARY_ROLLUP_MAX_TOKENS,
66
- includedToolNames: CONTEXT_COMPACTION_INCLUDED_TOOL_NAMES,
67
- includedToolPrefixes: CONTEXT_COMPACTION_INCLUDED_TOOL_PREFIXES,
68
- })
69
-
70
- async function compactMemoryBlockSummary(params: {
71
- previousSummary: string
72
- newEntriesText: string
73
- }): Promise<string> {
74
- const previousSummary = params.previousSummary.trim()
75
- const newEntriesText = params.newEntriesText.trim()
76
- if (!previousSummary && !newEntriesText) return ''
77
-
78
- return helperModelRuntime.generateHelperText({
79
- tag: 'memory-block-compaction',
80
- createAgent: createContextCompactionAgent,
81
- messages: [{ role: 'user', content: buildMemoryBlockCompactionPrompt({ previousSummary, newEntriesText }) }],
82
- maxOutputTokens: CONTEXT_COMPACTION_MAX_OUTPUT_TOKENS,
83
- })
84
- }
85
-
86
- return { ...runtime, compactMemoryBlockSummary }
87
- }
@@ -1,43 +0,0 @@
1
- export const ORG_SCOPE_PREFIX = 'org'
2
-
3
- const SCOPE_ID_MAX_LENGTH = 255
4
- const SCOPE_PREFIX_REGEX = /^[a-z][a-z0-9_]*$/
5
-
6
- function stripRecordPrefix(id: string): string {
7
- if (typeof id !== 'string' || id.length === 0) {
8
- throw new TypeError('id must be a non-empty string')
9
- }
10
- const [, ...rest] = id.split(':')
11
- return rest.length > 0 ? rest.join(':') : id
12
- }
13
-
14
- export function scopeId(prefix: string, id: string): string {
15
- if (typeof prefix !== 'string' || prefix.length === 0) {
16
- throw new TypeError('prefix must be a non-empty string')
17
- }
18
- if (!SCOPE_PREFIX_REGEX.test(prefix)) {
19
- throw new Error(`Invalid scope prefix: ${prefix}`)
20
- }
21
-
22
- const stripped = stripRecordPrefix(id)
23
- const scoped = `${prefix}:${stripped}`
24
- if (scoped.length > SCOPE_ID_MAX_LENGTH) {
25
- throw new Error('scopeId exceeds maximum length')
26
- }
27
-
28
- return scoped
29
- }
30
-
31
- export function agentScopeId(orgId: string, agentName: string): string {
32
- if (typeof agentName !== 'string' || agentName.trim().length === 0) {
33
- throw new TypeError('agentName must be a non-empty string')
34
- }
35
- const strippedOrgId = stripRecordPrefix(orgId)
36
- const scoped = `agent:${strippedOrgId}:${agentName.trim()}`
37
-
38
- if (scoped.length > SCOPE_ID_MAX_LENGTH) {
39
- throw new Error('scopeId exceeds maximum length')
40
- }
41
-
42
- return scoped
43
- }