@lota-sdk/core 0.1.9 → 0.1.12

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 (105) hide show
  1. package/infrastructure/schema/00_workstream.surql +1 -0
  2. package/infrastructure/schema/02_execution_plan.surql +202 -52
  3. package/package.json +4 -87
  4. package/src/ai/index.ts +3 -0
  5. package/src/bifrost/bifrost.ts +94 -25
  6. package/src/bifrost/index.ts +1 -0
  7. package/src/config/agent-defaults.ts +30 -7
  8. package/src/config/constants.ts +0 -9
  9. package/src/config/debug-logger.ts +43 -0
  10. package/src/config/index.ts +5 -0
  11. package/src/config/model-constants.ts +8 -9
  12. package/src/config/workstream-defaults.ts +4 -0
  13. package/src/db/cursor-pagination.ts +2 -2
  14. package/src/db/index.ts +10 -0
  15. package/src/db/memory-store.ts +3 -71
  16. package/src/db/memory.ts +9 -15
  17. package/src/db/service.ts +42 -2
  18. package/src/db/tables.ts +9 -2
  19. package/src/document/index.ts +2 -0
  20. package/src/document/parsing.ts +0 -25
  21. package/src/embeddings/provider.ts +102 -22
  22. package/src/index.ts +15 -499
  23. package/src/queues/index.ts +10 -0
  24. package/src/redis/connection-accessor.ts +26 -0
  25. package/src/redis/connection.ts +1 -1
  26. package/src/redis/index.ts +9 -25
  27. package/src/redis/org-memory-lock.ts +1 -1
  28. package/src/redis/redis-lease-lock.ts +1 -1
  29. package/src/redis/stream-context.ts +54 -0
  30. package/src/runtime/agent-runtime-policy.ts +9 -5
  31. package/src/runtime/agent-stream-helpers.ts +6 -3
  32. package/src/runtime/agent-types.ts +1 -5
  33. package/src/runtime/approval-continuation.ts +68 -1
  34. package/src/runtime/chat-attachments.ts +1 -1
  35. package/src/runtime/chat-request-routing.ts +6 -2
  36. package/src/runtime/context-compaction-runtime.ts +2 -2
  37. package/src/runtime/context-compaction.ts +1 -1
  38. package/src/runtime/execution-plan.ts +22 -15
  39. package/src/runtime/index.ts +26 -0
  40. package/src/runtime/indexed-repositories-policy.ts +10 -10
  41. package/src/runtime/memory-pipeline.ts +0 -2
  42. package/src/runtime/runtime-config.ts +238 -0
  43. package/src/runtime/runtime-extensions.ts +3 -2
  44. package/src/runtime/runtime-worker-registry.ts +47 -0
  45. package/src/runtime/team-consultation-orchestrator.ts +9 -6
  46. package/src/runtime/team-consultation-prompts.ts +3 -2
  47. package/src/runtime/turn-lifecycle.ts +13 -5
  48. package/src/runtime/workstream-chat-helpers.ts +0 -54
  49. package/src/runtime/workstream-routing-policy.ts +3 -7
  50. package/src/runtime.ts +387 -0
  51. package/src/services/chat-attachments.service.ts +1 -1
  52. package/src/services/context-compaction.service.ts +1 -1
  53. package/src/services/document-chunk.service.ts +2 -2
  54. package/src/services/execution-plan.service.ts +584 -793
  55. package/src/services/index.ts +14 -0
  56. package/src/services/learned-skill.service.ts +82 -39
  57. package/src/services/memory.service.ts +5 -4
  58. package/src/services/mutating-approval.service.ts +1 -1
  59. package/src/services/organization-member.service.ts +1 -1
  60. package/src/services/organization.service.ts +1 -1
  61. package/src/services/plan-approval.service.ts +83 -0
  62. package/src/services/plan-artifact.service.ts +44 -0
  63. package/src/services/plan-builder.service.ts +61 -0
  64. package/src/services/plan-checkpoint.service.ts +53 -0
  65. package/src/services/plan-compiler.service.ts +81 -0
  66. package/src/services/plan-executor.service.ts +1624 -0
  67. package/src/services/plan-run.service.ts +422 -0
  68. package/src/services/plan-validator.service.ts +760 -0
  69. package/src/services/recent-activity-title.service.ts +1 -1
  70. package/src/services/recent-activity.service.ts +14 -16
  71. package/src/services/user.service.ts +2 -2
  72. package/src/services/workstream-message.service.ts +2 -3
  73. package/src/services/workstream-title.service.ts +1 -1
  74. package/src/services/workstream-turn-preparation.ts +156 -59
  75. package/src/services/workstream-turn.ts +26 -1
  76. package/src/services/workstream.service.ts +35 -9
  77. package/src/services/workstream.types.ts +1 -0
  78. package/src/storage/attachment-parser.ts +1 -1
  79. package/src/storage/attachment-storage.service.ts +11 -10
  80. package/src/storage/generated-document-storage.service.ts +7 -6
  81. package/src/storage/index.ts +10 -0
  82. package/src/system-agents/delegated-agent-factory.ts +78 -29
  83. package/src/system-agents/index.ts +4 -0
  84. package/src/system-agents/recent-activity-title-refiner.agent.ts +38 -3
  85. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  86. package/src/system-agents/skill-extractor.agent.ts +1 -1
  87. package/src/system-agents/skill-manager.agent.ts +2 -4
  88. package/src/system-agents/title-generator.agent.ts +2 -2
  89. package/src/tools/execution-plan.tool.ts +22 -48
  90. package/src/tools/firecrawl-client.ts +2 -2
  91. package/src/tools/index.ts +12 -0
  92. package/src/tools/log-hello-world.tool.ts +17 -0
  93. package/src/tools/research-topic.tool.ts +1 -1
  94. package/src/tools/team-think.tool.ts +1 -1
  95. package/src/tools/user-questions.tool.ts +2 -2
  96. package/src/utils/index.ts +6 -0
  97. package/src/workers/bootstrap.ts +8 -16
  98. package/src/workers/index.ts +7 -0
  99. package/src/workers/regular-chat-memory-digest.runner.ts +1 -1
  100. package/src/workers/skill-extraction.runner.ts +3 -3
  101. package/src/workers/utils/{repo-indexer-chunker.ts → file-section-chunker.ts} +23 -52
  102. package/src/workers/utils/repo-structure-extractor.ts +2 -5
  103. package/src/workers/utils/repomix-file-sections.ts +42 -0
  104. package/src/config/env-shapes.ts +0 -121
  105. package/src/runtime/agent-contract.ts +0 -1
package/src/index.ts CHANGED
@@ -1,499 +1,15 @@
1
- import type { CoreWorkstreamProfile } from './config/agent-defaults'
2
- import type { LotaWorkstreamConfig } from './config/workstream-defaults'
3
- import { ensureRecordId } from './db/record-id'
4
- import type { SurrealDBService } from './db/service'
5
- import { TABLES } from './db/tables'
6
- import type { startContextCompactionWorker } from './queues/context-compaction.queue'
7
- import type {
8
- scheduleRecurringConsolidation,
9
- startMemoryConsolidationWorker,
10
- } from './queues/memory-consolidation.queue'
11
- import type { startPostChatMemoryWorker } from './queues/post-chat-memory.queue'
12
- import type { startRecentActivityTitleRefinementWorker } from './queues/recent-activity-title-refinement.queue'
13
- import type { startRegularChatMemoryDigestWorker } from './queues/regular-chat-memory-digest.queue'
14
- import type { startSkillExtractionWorker } from './queues/skill-extraction.queue'
15
- import type { startWorkstreamTitleGenerationWorker } from './queues/workstream-title-generation.queue'
16
- import type { RedisConnectionManager } from './redis/connection'
17
- import type { isApprovalContinuationRequest } from './runtime/approval-continuation'
18
- import type { routeWorkstreamChatMessages } from './runtime/chat-request-routing'
19
- import type { LotaPlugin } from './runtime/plugin-types'
20
- import type { LotaRuntimeAdapters, LotaRuntimeTurnHooks } from './runtime/runtime-extensions'
21
- import type { attachmentService } from './services/attachment.service'
22
- import type { documentChunkService } from './services/document-chunk.service'
23
- import type { executionPlanService } from './services/execution-plan.service'
24
- import type { memoryService } from './services/memory.service'
25
- import type { verifyMutatingApproval } from './services/mutating-approval.service'
26
- import type { organizationMemberService } from './services/organization-member.service'
27
- import type { organizationService } from './services/organization.service'
28
- import type { recentActivityTitleService } from './services/recent-activity-title.service'
29
- import type { recentActivityService } from './services/recent-activity.service'
30
- import type { userService } from './services/user.service'
31
- import type { workstreamMessageService } from './services/workstream-message.service'
32
- import type { workstreamTitleService } from './services/workstream-title.service'
33
- import type {
34
- createWorkstreamApprovalContinuationStream,
35
- createWorkstreamTurnStream,
36
- runWorkstreamTurnInBackground,
37
- } from './services/workstream-turn'
38
- import type { workstreamService } from './services/workstream.service'
39
- import type { generatedDocumentStorageService } from './storage/generated-document-storage.service'
40
-
41
- export type LotaAgentFactoryRegistry = Record<string, (...args: unknown[]) => unknown>
42
-
43
- interface LotaRuntimeBuiltInWorkers {
44
- startContextCompactionWorker: typeof startContextCompactionWorker
45
- startMemoryConsolidationWorker: typeof startMemoryConsolidationWorker
46
- startPostChatMemoryWorker: typeof startPostChatMemoryWorker
47
- startRecentActivityTitleRefinementWorker: typeof startRecentActivityTitleRefinementWorker
48
- startRegularChatMemoryDigestWorker: typeof startRegularChatMemoryDigestWorker
49
- startSkillExtractionWorker: typeof startSkillExtractionWorker
50
- startWorkstreamTitleGenerationWorker: typeof startWorkstreamTitleGenerationWorker
51
- scheduleRecurringConsolidation: typeof scheduleRecurringConsolidation
52
- }
53
-
54
- type ArchiveSdkWorkstream = (
55
- workstreamId: Parameters<typeof workstreamService.updateStatus>[0],
56
- status?: 'archived',
57
- ) => ReturnType<typeof workstreamService.updateStatus>
58
-
59
- type UnarchiveSdkWorkstream = (
60
- workstreamId: Parameters<typeof workstreamService.updateStatus>[0],
61
- status?: 'regular',
62
- ) => ReturnType<typeof workstreamService.updateStatus>
63
-
64
- export interface LotaRuntimeConfig {
65
- database: { url: string; namespace: string; username: string; password: string }
66
- redis: { url: string }
67
- aiGateway: { url: string; key: string; admin?: string; pass?: string; embeddingModel?: string }
68
- s3: {
69
- endpoint: string
70
- bucket: string
71
- region?: string
72
- accessKeyId: string
73
- secretAccessKey: string
74
- attachmentUrlExpiresIn?: number
75
- }
76
- firecrawl: { apiKey: string; apiBaseUrl?: string }
77
- logging?: { level?: 'trace' | 'debug' | 'info' | 'warning' | 'error' | 'fatal' }
78
- memory?: { searchK?: number; embeddingCacheTtlSeconds?: number }
79
- workstreams?: LotaWorkstreamConfig
80
- backgroundProcessing?: {
81
- memoryExtractionFrequency?: number
82
- skillExtractionFrequency?: number
83
- memoryDigestFrequency?: number
84
- memoryConsolidationFrequency?: number
85
- }
86
-
87
- agents: {
88
- roster: readonly string[]
89
- displayNames: Record<string, string>
90
- shortDisplayNames?: Record<string, string>
91
- teamConsultParticipants: readonly string[]
92
- getCoreWorkstreamProfile?: (coreType: string) => CoreWorkstreamProfile
93
- createAgent?: LotaAgentFactoryRegistry
94
- buildAgentTools?: (...args: unknown[]) => unknown
95
- getAgentRuntimeConfig?: (...args: unknown[]) => unknown
96
- }
97
-
98
- toolProviders?: Record<string, unknown>
99
- extraSchemaFiles?: Array<string | URL>
100
- extraWorkers?: Record<string, unknown>
101
- pluginRuntime?: Record<string, LotaPlugin>
102
- runtimeAdapters?: LotaRuntimeAdapters
103
- turnHooks?: LotaRuntimeTurnHooks
104
- }
105
-
106
- export interface LotaRuntime {
107
- services: {
108
- database: SurrealDBService
109
- databaseService: SurrealDBService
110
- redis: RedisConnectionManager
111
- closeRedisConnection: () => Promise<void>
112
- attachmentService: typeof attachmentService
113
- documentChunkService: typeof documentChunkService
114
- generatedDocumentStorageService: typeof generatedDocumentStorageService
115
- memoryService: typeof memoryService
116
- verifyMutatingApproval: typeof verifyMutatingApproval
117
- organizationService: typeof organizationService
118
- organizationMemberService: typeof organizationMemberService
119
- userService: typeof userService
120
- recentActivityService: typeof recentActivityService
121
- recentActivityTitleService: typeof recentActivityTitleService
122
- executionPlanService: typeof executionPlanService
123
- workstreamMessageService: typeof workstreamMessageService
124
- workstreamService: typeof workstreamService
125
- workstreamTitleService: typeof workstreamTitleService
126
- createWorkstreamApprovalContinuationStream: typeof createWorkstreamApprovalContinuationStream
127
- createWorkstreamTurnStream: typeof createWorkstreamTurnStream
128
- isApprovalContinuationRequest: typeof isApprovalContinuationRequest
129
- runWorkstreamTurnInBackground: typeof runWorkstreamTurnInBackground
130
- }
131
- lota: {
132
- organizations: {
133
- create: typeof organizationService.createOrganization
134
- upsert: typeof organizationService.upsertOrganization
135
- get: typeof organizationService.getOrganization
136
- list: typeof organizationService.listOrganizations
137
- update: typeof organizationService.updateOrganization
138
- delete: typeof organizationService.deleteOrganization
139
- }
140
- users: {
141
- upsert: typeof userService.upsertUser
142
- get: typeof userService.getUser
143
- list: typeof userService.listUsers
144
- update: typeof userService.updateUser
145
- delete: typeof userService.deleteUser
146
- }
147
- memberships: {
148
- add: typeof organizationMemberService.addMembership
149
- listForOrganization: typeof organizationMemberService.listMembershipsForOrganization
150
- listForUser: typeof organizationMemberService.listMembershipsForUser
151
- remove: typeof organizationMemberService.removeMembership
152
- isMember: typeof organizationMemberService.isMember
153
- }
154
- workstreams: {
155
- create: typeof workstreamService.createWorkstream
156
- list: typeof workstreamService.listWorkstreams
157
- get: typeof workstreamService.getWorkstream
158
- update: typeof workstreamService.updateTitle
159
- archive: ArchiveSdkWorkstream
160
- unarchive: UnarchiveSdkWorkstream
161
- delete: typeof workstreamService.deleteWorkstream
162
- stop: typeof workstreamService.stopActiveRun
163
- listMessages: typeof workstreamMessageService.listMessageHistoryPage
164
- getMessage: (params: { workstreamId: string; messageId: string }) => Promise<unknown>
165
- sendMessage: (params: {
166
- workstreamId: string
167
- organizationId: string
168
- userId: string
169
- userName: string
170
- messages: Parameters<typeof routeWorkstreamChatMessages>[0]
171
- }) => Promise<Awaited<ReturnType<typeof createWorkstreamTurnStream>>>
172
- continueApproval: (params: {
173
- workstreamId: string
174
- organizationId: string
175
- userId: string
176
- userName: string
177
- messages: Parameters<typeof routeWorkstreamChatMessages>[0]
178
- }) => Promise<Awaited<ReturnType<typeof createWorkstreamApprovalContinuationStream>>>
179
- uploadAttachment: typeof attachmentService.uploadWorkstreamAttachment
180
- }
181
- }
182
- redis: {
183
- manager: RedisConnectionManager
184
- getConnection: () => ReturnType<RedisConnectionManager['getConnection']>
185
- getConnectionForBullMQ: () => ReturnType<RedisConnectionManager['getConnectionForBullMQ']>
186
- closeConnection: () => Promise<void>
187
- }
188
- workers: LotaRuntimeBuiltInWorkers & Record<string, unknown>
189
- schemaFiles: Array<string | URL>
190
- contributions: { envKeys: readonly string[]; schemaFiles: Array<string | URL> }
191
- config: LotaRuntimeConfig
192
- plugins: Record<string, LotaPlugin>
193
- connectPluginDatabases(): Promise<void>
194
- connect(): Promise<void>
195
- disconnect(): Promise<void>
196
- }
197
-
198
- export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<LotaRuntime> {
199
- const { lotaSdkEnvKeys, setEnv } = await import('./config/env-shapes')
200
- const { configureLogger } = await import('./config/logger')
201
- const { publishDatabaseBootstrap } = await import('./db/startup')
202
- const { computeSchemaFingerprint } = await import('./db/schema-fingerprint')
203
- const { LOTA_SDK_DATABASE_NAME } = await import('./db/sdk-database')
204
- const { SurrealDBService: SurrealDBServiceClass, setDatabaseService } = await import('./db/service')
205
- const { createRedisConnectionManager } = await import('./redis/connection')
206
- const { setRedisConnectionManager } = await import('./redis/index')
207
- const { configureAgents, configureAgentFactory } = await import('./config/agent-defaults')
208
- const { configureWorkstreams } = await import('./config/workstream-defaults')
209
- const { configureRuntimeExtensions } = await import('./runtime/runtime-extensions')
210
- const { routeWorkstreamChatMessages } = await import('./runtime/chat-request-routing')
211
- const { configureBackgroundProcessing } = await import('./config/background-processing')
212
- const { configureEmbeddingCache } = await import('./ai/embedding-cache')
213
-
214
- // Resolve config defaults
215
- const memory = {
216
- searchK: config.memory?.searchK ?? 6,
217
- embeddingCacheTtlSeconds: config.memory?.embeddingCacheTtlSeconds ?? 3600,
218
- }
219
- const backgroundProcessing = {
220
- memoryExtractionFrequency: config.backgroundProcessing?.memoryExtractionFrequency ?? 3,
221
- skillExtractionFrequency: config.backgroundProcessing?.skillExtractionFrequency ?? 5,
222
- memoryDigestFrequency: config.backgroundProcessing?.memoryDigestFrequency ?? 1,
223
- memoryConsolidationFrequency: config.backgroundProcessing?.memoryConsolidationFrequency ?? 10,
224
- }
225
-
226
- setEnv({
227
- AI_GATEWAY_URL: config.aiGateway.url,
228
- AI_GATEWAY_KEY: config.aiGateway.key,
229
- AI_GATEWAY_ADMIN: config.aiGateway.admin,
230
- AI_GATEWAY_PASS: config.aiGateway.pass,
231
- AI_EMBEDDING_MODEL: config.aiGateway.embeddingModel ?? 'openai/text-embedding-3-small',
232
- REDIS_URL: config.redis.url,
233
- LOG_LEVEL: config.logging?.level ?? 'info',
234
- S3_ENDPOINT: config.s3.endpoint,
235
- S3_BUCKET: config.s3.bucket,
236
- S3_REGION: config.s3.region ?? 'garage',
237
- S3_ACCESS_KEY_ID: config.s3.accessKeyId,
238
- S3_SECRET_ACCESS_KEY: config.s3.secretAccessKey,
239
- ATTACHMENT_URL_EXPIRES_IN: config.s3.attachmentUrlExpiresIn ?? 1800,
240
- FIRECRAWL_API_KEY: config.firecrawl.apiKey,
241
- FIRECRAWL_API_BASE_URL: config.firecrawl.apiBaseUrl,
242
- MEMORY_SEARCH_K: memory.searchK,
243
- })
244
-
245
- await configureLogger(config.logging?.level ?? 'info')
246
-
247
- const db = new SurrealDBServiceClass({
248
- url: config.database.url,
249
- namespace: config.database.namespace,
250
- database: LOTA_SDK_DATABASE_NAME,
251
- username: config.database.username,
252
- password: config.database.password,
253
- })
254
- setDatabaseService(db)
255
-
256
- const redisManager = createRedisConnectionManager({ url: config.redis.url })
257
- setRedisConnectionManager(redisManager)
258
- configureEmbeddingCache(redisManager.getConnection(), memory.embeddingCacheTtlSeconds)
259
- configureBackgroundProcessing(backgroundProcessing)
260
-
261
- configureAgents({
262
- roster: config.agents.roster,
263
- displayNames: config.agents.displayNames,
264
- shortDisplayNames: config.agents.shortDisplayNames,
265
- teamConsultParticipants: config.agents.teamConsultParticipants,
266
- getCoreWorkstreamProfile: config.agents.getCoreWorkstreamProfile,
267
- })
268
- configureWorkstreams({ agentRoster: config.agents.roster, config: config.workstreams })
269
-
270
- if (config.agents.createAgent || config.agents.buildAgentTools || config.agents.getAgentRuntimeConfig) {
271
- configureAgentFactory({
272
- createAgent: config.agents.createAgent ?? {},
273
- buildAgentTools: config.agents.buildAgentTools,
274
- getAgentRuntimeConfig: config.agents.getAgentRuntimeConfig,
275
- pluginRuntime: config.pluginRuntime,
276
- })
277
- }
278
-
279
- const { attachmentService } = await import('./services/attachment.service')
280
- const { documentChunkService } = await import('./services/document-chunk.service')
281
- const { recentActivityService } = await import('./services/recent-activity.service')
282
- const { recentActivityTitleService } = await import('./services/recent-activity-title.service')
283
- const { executionPlanService } = await import('./services/execution-plan.service')
284
- const { memoryService } = await import('./services/memory.service')
285
- const { verifyMutatingApproval } = await import('./services/mutating-approval.service')
286
- const { organizationMemberService } = await import('./services/organization-member.service')
287
- const { organizationService } = await import('./services/organization.service')
288
- const { userService } = await import('./services/user.service')
289
- const { workstreamMessageService } = await import('./services/workstream-message.service')
290
- const { workstreamService } = await import('./services/workstream.service')
291
- const { workstreamTitleService } = await import('./services/workstream-title.service')
292
- const {
293
- createWorkstreamApprovalContinuationStream,
294
- createWorkstreamTurnStream,
295
- isApprovalContinuationRequest,
296
- runWorkstreamTurnInBackground,
297
- } = await import('./services/workstream-turn')
298
- const { generatedDocumentStorageService } = await import('./storage/generated-document-storage.service')
299
- const { startContextCompactionWorker } = await import('./queues/context-compaction.queue')
300
- const { scheduleRecurringConsolidation, startMemoryConsolidationWorker } =
301
- await import('./queues/memory-consolidation.queue')
302
- const { startPostChatMemoryWorker } = await import('./queues/post-chat-memory.queue')
303
- const { startRecentActivityTitleRefinementWorker } = await import('./queues/recent-activity-title-refinement.queue')
304
- const { startRegularChatMemoryDigestWorker } = await import('./queues/regular-chat-memory-digest.queue')
305
- const { startSkillExtractionWorker } = await import('./queues/skill-extraction.queue')
306
- const { startWorkstreamTitleGenerationWorker } = await import('./queues/workstream-title-generation.queue')
307
-
308
- configureRuntimeExtensions({
309
- adapters: config.runtimeAdapters,
310
- turnHooks: config.turnHooks,
311
- toolProviders: (config.toolProviders ?? {}) as never,
312
- extraWorkers: config.extraWorkers,
313
- })
314
-
315
- const pluginRuntime = config.pluginRuntime ?? {}
316
- const pluginContributions = Object.values(pluginRuntime).map((plugin) => plugin.contributions)
317
- const schemaFiles = [...getBuiltInSchemaFiles(), ...(config.extraSchemaFiles ?? [])]
318
- const hostContributionSchemaFiles = pluginContributions.flatMap((plugin) => plugin.schemaFiles)
319
- const contributionEnvKeys = [...lotaSdkEnvKeys, ...pluginContributions.flatMap((plugin) => plugin.envKeys)]
320
- const connectPluginDatabases = createPluginDatabaseConnector(pluginRuntime)
321
- const builtInWorkers = {
322
- startContextCompactionWorker,
323
- startMemoryConsolidationWorker,
324
- startPostChatMemoryWorker,
325
- startRecentActivityTitleRefinementWorker,
326
- startRegularChatMemoryDigestWorker,
327
- startSkillExtractionWorker,
328
- startWorkstreamTitleGenerationWorker,
329
- scheduleRecurringConsolidation,
330
- } satisfies LotaRuntimeBuiltInWorkers
331
-
332
- const lota = {
333
- organizations: {
334
- create: organizationService.createOrganization.bind(organizationService),
335
- upsert: organizationService.upsertOrganization.bind(organizationService),
336
- get: organizationService.getOrganization.bind(organizationService),
337
- list: organizationService.listOrganizations.bind(organizationService),
338
- update: organizationService.updateOrganization.bind(organizationService),
339
- delete: organizationService.deleteOrganization.bind(organizationService),
340
- },
341
- users: {
342
- upsert: userService.upsertUser.bind(userService),
343
- get: userService.getUser.bind(userService),
344
- list: userService.listUsers.bind(userService),
345
- update: userService.updateUser.bind(userService),
346
- delete: userService.deleteUser.bind(userService),
347
- },
348
- memberships: {
349
- add: organizationMemberService.addMembership.bind(organizationMemberService),
350
- listForOrganization: organizationMemberService.listMembershipsForOrganization.bind(organizationMemberService),
351
- listForUser: organizationMemberService.listMembershipsForUser.bind(organizationMemberService),
352
- remove: organizationMemberService.removeMembership.bind(organizationMemberService),
353
- isMember: organizationMemberService.isMember.bind(organizationMemberService),
354
- },
355
- workstreams: {
356
- create: workstreamService.createWorkstream.bind(workstreamService),
357
- list: workstreamService.listWorkstreams.bind(workstreamService),
358
- get: workstreamService.getWorkstream.bind(workstreamService),
359
- update: workstreamService.updateTitle.bind(workstreamService),
360
- archive: async (workstreamId, status = 'archived') => await workstreamService.updateStatus(workstreamId, status),
361
- unarchive: async (workstreamId, status = 'regular') => await workstreamService.updateStatus(workstreamId, status),
362
- delete: workstreamService.deleteWorkstream.bind(workstreamService),
363
- stop: workstreamService.stopActiveRun.bind(workstreamService),
364
- listMessages: workstreamMessageService.listMessageHistoryPage.bind(workstreamMessageService),
365
- getMessage: async ({ workstreamId, messageId }) => {
366
- const messages = await workstreamMessageService.listMessages(ensureRecordId(workstreamId, TABLES.WORKSTREAM))
367
- const message = messages.find((candidate) => candidate.id === messageId)
368
- if (!message) {
369
- throw new Error(`Workstream message not found: ${messageId}`)
370
- }
371
- return message
372
- },
373
- sendMessage: async ({ workstreamId, organizationId, userId, userName, messages }) => {
374
- const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
375
- const workstream = await workstreamService.getWorkstream(workstreamRef)
376
- const routed = routeWorkstreamChatMessages(messages)
377
- if (routed.kind !== 'turn') {
378
- throw new Error(routed.kind === 'invalid' ? routed.message : 'Expected a user turn payload.')
379
- }
380
-
381
- return await createWorkstreamTurnStream({
382
- workstream,
383
- workstreamRef,
384
- orgRef: ensureRecordId(organizationId, TABLES.ORGANIZATION),
385
- userRef: ensureRecordId(userId, TABLES.USER),
386
- userName,
387
- inputMessage: routed.inputMessage,
388
- })
389
- },
390
- continueApproval: async ({ workstreamId, organizationId, userId, userName, messages }) => {
391
- const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
392
- const workstream = await workstreamService.getWorkstream(workstreamRef)
393
- const routed = routeWorkstreamChatMessages(messages)
394
- if (routed.kind !== 'approval-continuation') {
395
- throw new Error(
396
- routed.kind === 'invalid' ? routed.message : 'Expected approval continuation messages payload.',
397
- )
398
- }
399
-
400
- return await createWorkstreamApprovalContinuationStream({
401
- workstream,
402
- workstreamRef,
403
- orgRef: ensureRecordId(organizationId, TABLES.ORGANIZATION),
404
- userRef: ensureRecordId(userId, TABLES.USER),
405
- userName,
406
- approvalMessages: routed.approvalMessages,
407
- })
408
- },
409
- uploadAttachment: attachmentService.uploadWorkstreamAttachment.bind(attachmentService),
410
- },
411
- } satisfies LotaRuntime['lota']
412
-
413
- return {
414
- services: {
415
- database: db,
416
- databaseService: db,
417
- redis: redisManager,
418
- closeRedisConnection: async () => await redisManager.closeConnection(),
419
- attachmentService,
420
- documentChunkService,
421
- generatedDocumentStorageService,
422
- memoryService,
423
- verifyMutatingApproval,
424
- organizationService,
425
- organizationMemberService,
426
- userService,
427
- recentActivityService,
428
- recentActivityTitleService,
429
- executionPlanService,
430
- workstreamMessageService,
431
- workstreamService,
432
- workstreamTitleService,
433
- createWorkstreamApprovalContinuationStream,
434
- createWorkstreamTurnStream,
435
- isApprovalContinuationRequest,
436
- runWorkstreamTurnInBackground,
437
- },
438
- lota,
439
- redis: {
440
- manager: redisManager,
441
- getConnection: () => redisManager.getConnection(),
442
- getConnectionForBullMQ: () => redisManager.getConnectionForBullMQ(),
443
- closeConnection: async () => await redisManager.closeConnection(),
444
- },
445
- workers: { ...builtInWorkers, ...config.extraWorkers },
446
- schemaFiles,
447
- contributions: { envKeys: [...new Set(contributionEnvKeys)], schemaFiles: hostContributionSchemaFiles },
448
- config,
449
- plugins: pluginRuntime,
450
- async connectPluginDatabases() {
451
- await connectPluginDatabases()
452
- },
453
- async connect() {
454
- await db.connect()
455
- const bunFiles = schemaFiles.map((schemaFile) =>
456
- schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile),
457
- )
458
- await db.applySchemaAndMigrations(bunFiles)
459
- const schemaFingerprint = await computeSchemaFingerprint(schemaFiles)
460
- await publishDatabaseBootstrap({ databaseService: db, schemaFingerprint })
461
- },
462
- async disconnect() {
463
- await db.disconnect()
464
- await redisManager.closeConnection()
465
- },
466
- }
467
- }
468
-
469
- function getBuiltInSchemaFiles(): URL[] {
470
- return [
471
- new URL('../infrastructure/schema/00_identity.surql', import.meta.url),
472
- new URL('../infrastructure/schema/00_workstream.surql', import.meta.url),
473
- new URL('../infrastructure/schema/01_memory.surql', import.meta.url),
474
- new URL('../infrastructure/schema/02_execution_plan.surql', import.meta.url),
475
- new URL('../infrastructure/schema/03_learned_skill.surql', import.meta.url),
476
- new URL('../infrastructure/schema/05_recent_activity.surql', import.meta.url),
477
- new URL('../infrastructure/schema/04_runtime_bootstrap.surql', import.meta.url),
478
- ]
479
- }
480
-
481
- function createPluginDatabaseConnector(pluginRuntime: Record<string, LotaPlugin>): () => Promise<void> {
482
- return async () => {
483
- for (const plugin of Object.values(pluginRuntime)) {
484
- const services = plugin.services as Record<string, unknown>
485
- const connectDatabase = services.connectDatabase
486
- if (typeof connectDatabase !== 'function') {
487
- continue
488
- }
489
-
490
- await Reflect.apply(connectDatabase, services, [])
491
- }
492
- }
493
- }
494
-
495
- export type { CoreWorkstreamProfile } from './config/agent-defaults'
496
- export type { SurrealDBService } from './db/service'
497
- export type { RedisConnectionManager } from './redis/connection'
498
- export type { LotaPlugin, LotaPluginContributions } from './runtime/plugin-types'
499
- export type { LotaRuntimeAdapters, LotaRuntimeTurnHooks } from './runtime/runtime-extensions'
1
+ export * from './runtime'
2
+ export * from './ai'
3
+ export * from './bifrost'
4
+ export * from './config'
5
+ export * from './db'
6
+ export * from './document'
7
+ export * from './queues'
8
+ export * from './redis'
9
+ export * from './runtime/index'
10
+ export * from './services'
11
+ export * from './storage'
12
+ export * from './system-agents'
13
+ export * from './tools'
14
+ export * from './utils'
15
+ export * from './workers'
@@ -0,0 +1,10 @@
1
+ export * from './context-compaction.queue'
2
+ export * from './document-processor.queue'
3
+ export * from './memory-consolidation.queue'
4
+ export * from './post-chat-memory.queue'
5
+ export * from './recent-activity-title-refinement.queue'
6
+ export * from './regular-chat-memory-digest.config'
7
+ export * from './regular-chat-memory-digest.queue'
8
+ export * from './skill-extraction.config'
9
+ export * from './skill-extraction.queue'
10
+ export * from './workstream-title-generation.queue'
@@ -0,0 +1,26 @@
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
+ }
@@ -10,7 +10,7 @@ export interface RedisConnectionLogger {
10
10
  error?: (message: string) => void
11
11
  }
12
12
 
13
- export interface CreateRedisConnectionManagerOptions {
13
+ interface CreateRedisConnectionManagerOptions {
14
14
  url: string
15
15
  redisOptions?: RedisOptions
16
16
  healthCheckIntervalMs?: number
@@ -1,30 +1,14 @@
1
- import type IORedis from 'ioredis'
2
-
3
1
  import { createRedisConnectionManager } from './connection'
4
2
  import type { RedisConnectionManager } from './connection'
3
+ export { DEFAULT_REDIS_OPTIONS, type RedisConnectionLogger } from './connection'
4
+ export {
5
+ getRedisConnection,
6
+ getRedisConnectionForBullMQ,
7
+ setRedisConnectionManager,
8
+ type RedisConnectionAccessor,
9
+ } from './connection-accessor'
10
+ export { withOrgMemoryLock } from './org-memory-lock'
11
+ export { createWorkstreamResumableContext } from './stream-context'
5
12
 
6
13
  export { createRedisConnectionManager }
7
14
  export type { RedisConnectionManager }
8
-
9
- let _redisManager: { getConnection(): IORedis; getConnectionForBullMQ(): IORedis } | undefined
10
-
11
- export function setRedisConnectionManager(manager: {
12
- getConnection(): IORedis
13
- getConnectionForBullMQ(): IORedis
14
- }): void {
15
- _redisManager = manager
16
- }
17
-
18
- export function getRedisConnection(): IORedis {
19
- if (!_redisManager) {
20
- throw new Error('Redis connection manager not configured. Call setRedisConnectionManager() first.')
21
- }
22
- return _redisManager.getConnection()
23
- }
24
-
25
- export function getRedisConnectionForBullMQ(): IORedis {
26
- if (!_redisManager) {
27
- throw new Error('Redis connection manager not configured. Call setRedisConnectionManager() first.')
28
- }
29
- return _redisManager.getConnectionForBullMQ()
30
- }
@@ -1,5 +1,5 @@
1
- import { getRedisConnection } from '.'
2
1
  import { serverLogger } from '../config/logger'
2
+ import { getRedisConnection } from './connection-accessor'
3
3
  import { withRedisLeaseLock } from './redis-lease-lock'
4
4
 
5
5
  const ORG_MEMORY_LOCK_PREFIX = 'lock:org-memory:org:'
@@ -11,7 +11,7 @@ interface RedisLeaseLockLogger {
11
11
  warn?: (message: string) => void
12
12
  }
13
13
 
14
- export interface RedisLeaseLockOptions {
14
+ interface RedisLeaseLockOptions {
15
15
  redis: IORedis
16
16
  lockKey: string
17
17
  lockTtlMs: number
@@ -0,0 +1,54 @@
1
+ import type { Redis } from 'ioredis'
2
+ import type { Publisher, Subscriber } from 'resumable-stream/ioredis'
3
+ import { createResumableStreamContext } from 'resumable-stream/ioredis'
4
+
5
+ import { getRedisConnection } from './connection-accessor'
6
+
7
+ function toSubscriber(client: Redis): Subscriber {
8
+ const handlers = new Map<string, (message: string) => void>()
9
+ const messageListener = (channel: string, message: string) => {
10
+ handlers.get(channel)?.(message)
11
+ }
12
+ return {
13
+ connect: () => Promise.resolve(),
14
+ subscribe: async (channel, callback) => {
15
+ if (handlers.size === 0) client.on('message', messageListener)
16
+ handlers.set(channel, callback)
17
+ await client.subscribe(channel)
18
+ },
19
+ unsubscribe: async (channel) => {
20
+ handlers.delete(channel)
21
+ if (handlers.size === 0) client.removeListener('message', messageListener)
22
+ return client.unsubscribe(channel)
23
+ },
24
+ }
25
+ }
26
+
27
+ function toPublisher(client: Redis): Publisher {
28
+ return {
29
+ connect: () => Promise.resolve(),
30
+ publish: (channel, message) => client.publish(channel, message),
31
+ set: (key, value, options) => (options?.EX ? client.set(key, value, 'EX', options.EX) : client.set(key, value)),
32
+ get: (key) => client.get(key),
33
+ incr: (key) => client.incr(key),
34
+ }
35
+ }
36
+
37
+ let sharedSubscriber: { client: Redis; subscriber: Subscriber } | undefined
38
+
39
+ function getSharedSubscriber(): Subscriber {
40
+ if (!sharedSubscriber) {
41
+ const client = getRedisConnection().duplicate()
42
+ sharedSubscriber = { client, subscriber: toSubscriber(client) }
43
+ }
44
+ return sharedSubscriber.subscriber
45
+ }
46
+
47
+ export function createWorkstreamResumableContext() {
48
+ const redis = getRedisConnection()
49
+ return createResumableStreamContext({
50
+ waitUntil: null,
51
+ subscriber: getSharedSubscriber(),
52
+ publisher: toPublisher(redis),
53
+ })
54
+ }