@lota-sdk/core 0.4.13 → 0.4.14
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.
- package/package.json +4 -4
- package/src/ai/embedding-cache.ts +17 -11
- package/src/ai-gateway/ai-gateway.ts +164 -94
- package/src/ai-gateway/index.ts +4 -1
- package/src/config/agent-defaults.ts +2 -2
- package/src/config/agent-types.ts +1 -1
- package/src/create-runtime.ts +259 -200
- package/src/db/cursor-pagination.ts +2 -9
- package/src/db/memory-store.ts +194 -175
- package/src/db/memory.ts +125 -71
- package/src/db/schema-fingerprint.ts +5 -4
- package/src/db/service-normalization.ts +4 -3
- package/src/db/service.ts +3 -2
- package/src/db/startup.ts +15 -16
- package/src/effect/errors.ts +161 -21
- package/src/effect/index.ts +0 -1
- package/src/embeddings/provider.ts +15 -7
- package/src/queues/autonomous-job.queue.ts +10 -22
- package/src/queues/delayed-node-promotion.queue.ts +8 -14
- package/src/queues/document-processor.queue.ts +13 -4
- package/src/queues/memory-consolidation.queue.ts +26 -14
- package/src/queues/plan-agent-heartbeat.queue.ts +10 -9
- package/src/queues/plan-scheduler.queue.ts +37 -15
- package/src/queues/queue-factory.ts +59 -35
- package/src/queues/standalone-worker.ts +3 -2
- package/src/redis/connection.ts +10 -3
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +5 -5
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/chat-message.ts +64 -1
- package/src/runtime/chat-run-orchestration.ts +33 -20
- package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
- package/src/runtime/context-compaction/context-compaction.ts +78 -66
- package/src/runtime/domain-layer.ts +13 -7
- package/src/runtime/execution-plan.ts +7 -3
- package/src/runtime/memory/memory-block.ts +3 -9
- package/src/runtime/memory/memory-scope.ts +3 -1
- package/src/runtime/plugin-resolution.ts +2 -1
- package/src/runtime/post-turn-side-effects.ts +6 -5
- package/src/runtime/retrieval-adapters.ts +8 -20
- package/src/runtime/runtime-config.ts +3 -9
- package/src/runtime/runtime-extensions.ts +2 -4
- package/src/runtime/runtime-lifecycle.ts +56 -16
- package/src/runtime/runtime-services.ts +180 -102
- package/src/runtime/runtime-worker-registry.ts +3 -1
- package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
- package/src/runtime/social-chat/social-chat-history.ts +21 -18
- package/src/runtime/social-chat/social-chat.ts +356 -223
- package/src/runtime/specialist-runner.ts +3 -1
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
- package/src/runtime/thread-turn-context.ts +142 -102
- package/src/runtime/turn-lifecycle.ts +15 -46
- package/src/services/agent-activity.service.ts +1 -1
- package/src/services/agent-executor.service.ts +107 -77
- package/src/services/autonomous-job.service.ts +354 -293
- package/src/services/background-work.service.ts +3 -3
- package/src/services/context-compaction.service.ts +7 -2
- package/src/services/document-chunk.service.ts +50 -32
- package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
- package/src/services/execution-plan/execution-plan.service.ts +162 -179
- package/src/services/feedback-loop.service.ts +5 -4
- package/src/services/graph-full-routing.ts +37 -36
- package/src/services/institutional-memory.service.ts +28 -30
- package/src/services/learned-skill.service.ts +107 -72
- package/src/services/memory/memory-errors.ts +4 -23
- package/src/services/memory/memory-org-memory.ts +10 -5
- package/src/services/memory/memory-rerank.ts +18 -6
- package/src/services/memory/memory.service.ts +170 -111
- package/src/services/memory/rerank.service.ts +29 -20
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +69 -75
- package/src/services/ownership-dispatcher.service.ts +40 -39
- package/src/services/plan/plan-agent-heartbeat.service.ts +26 -23
- package/src/services/plan/plan-agent-query.service.ts +39 -31
- package/src/services/plan/plan-completion-side-effects.ts +13 -17
- package/src/services/plan/plan-coordination.service.ts +2 -1
- package/src/services/plan/plan-cycle.service.ts +6 -5
- package/src/services/plan/plan-deadline.service.ts +57 -54
- package/src/services/plan/plan-event-delivery.service.ts +5 -4
- package/src/services/plan/plan-executor-graph.ts +18 -15
- package/src/services/plan/plan-executor.service.ts +235 -262
- package/src/services/plan/plan-run.service.ts +169 -93
- package/src/services/plan/plan-scheduler.service.ts +192 -202
- package/src/services/plan/plan-template.service.ts +1 -1
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/plan/plan-workspace.service.ts +23 -14
- package/src/services/plugin-executor.service.ts +5 -9
- package/src/services/queue-job.service.ts +117 -59
- package/src/services/recent-activity-title.service.ts +13 -12
- package/src/services/recent-activity.service.ts +6 -1
- package/src/services/social-chat-history.service.ts +29 -25
- package/src/services/system-executor.service.ts +5 -9
- package/src/services/thread/thread-active-run.ts +2 -2
- package/src/services/thread/thread-listing.ts +61 -57
- package/src/services/thread/thread-memory-block.ts +73 -48
- package/src/services/thread/thread-message.service.ts +76 -65
- package/src/services/thread/thread-record-store.ts +8 -8
- package/src/services/thread/thread-title.service.ts +10 -4
- package/src/services/thread/thread-turn-execution.ts +43 -45
- package/src/services/thread/thread-turn-preparation.service.ts +257 -135
- package/src/services/thread/thread-turn-streaming.ts +82 -85
- package/src/services/thread/thread-turn.ts +8 -8
- package/src/services/thread/thread.service.ts +135 -100
- package/src/services/user.service.ts +45 -48
- package/src/storage/attachment-parser.ts +6 -2
- package/src/storage/attachment-storage.service.ts +5 -6
- package/src/storage/generated-document-storage.service.ts +1 -1
- package/src/system-agents/context-compaction.agent.ts +10 -9
- package/src/system-agents/delegated-agent-factory.ts +30 -6
- package/src/system-agents/memory-reranker.agent.ts +10 -9
- package/src/system-agents/memory.agent.ts +10 -9
- package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
- package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
- package/src/system-agents/skill-extractor.agent.ts +13 -12
- package/src/system-agents/skill-manager.agent.ts +13 -12
- package/src/system-agents/thread-router.agent.ts +10 -5
- package/src/system-agents/title-generator.agent.ts +13 -12
- package/src/tools/fetch-webpage.tool.ts +13 -13
- package/src/tools/memory-block.tool.ts +3 -1
- package/src/tools/plan-approval.tool.ts +4 -2
- package/src/tools/read-file-parts.tool.ts +10 -4
- package/src/tools/remember-memory.tool.ts +3 -1
- package/src/tools/research-topic.tool.ts +9 -5
- package/src/tools/search-web.tool.ts +16 -16
- package/src/tools/search.tool.ts +20 -5
- package/src/tools/team-think.tool.ts +61 -38
- package/src/utils/async.ts +5 -5
- package/src/utils/errors.ts +19 -18
- package/src/utils/sse-keepalive.ts +28 -25
- package/src/workers/bootstrap.ts +75 -11
- package/src/workers/memory-consolidation.worker.ts +82 -91
- package/src/workers/organization-learning.worker.ts +14 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
- package/src/workers/skill-extraction.runner.ts +97 -61
- package/src/workers/utils/repo-structure-extractor.ts +13 -8
- package/src/workers/utils/thread-message-query.ts +24 -24
- package/src/workers/worker-utils.ts +23 -4
- package/src/effect/helpers.ts +0 -123
package/src/create-runtime.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import type { Fiber } from 'effect'
|
|
2
|
-
import { Effect, ManagedRuntime } from 'effect'
|
|
2
|
+
import { Deferred, Effect, Layer, ManagedRuntime } from 'effect'
|
|
3
3
|
import type { Subscriber } from 'resumable-stream/ioredis'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
AiGatewayModelsTag,
|
|
7
|
+
AiGatewayTag,
|
|
8
|
+
RuntimeBridgeTag,
|
|
9
|
+
createAiGatewayModels,
|
|
10
|
+
makeAiGatewayService,
|
|
11
|
+
} from './ai-gateway/ai-gateway'
|
|
12
|
+
import type { AiGatewayModels, RuntimeBridge } from './ai-gateway/ai-gateway'
|
|
6
13
|
import { computeSchemaFingerprint } from './db/schema-fingerprint'
|
|
7
14
|
import { publishDatabaseBootstrapEffect } from './db/startup'
|
|
8
15
|
import {
|
|
@@ -12,10 +19,8 @@ import {
|
|
|
12
19
|
DatabaseServiceTag as EffectDatabaseService,
|
|
13
20
|
RedisServiceTag as EffectRedisService,
|
|
14
21
|
RuntimeAdaptersServiceTag,
|
|
15
|
-
RuntimeConfigServiceTag,
|
|
16
22
|
} from './effect'
|
|
17
23
|
import { ConfigurationError, ServiceError } from './effect/errors'
|
|
18
|
-
import { effectTryPromise } from './effect/helpers'
|
|
19
24
|
import { LotaQueuesServiceTag } from './queues/queues.service'
|
|
20
25
|
import type { RedisConnectionManager } from './redis/connection'
|
|
21
26
|
import { SharedThreadStreamSubscriberTag } from './redis/stream-context'
|
|
@@ -74,6 +79,10 @@ export interface LotaRuntime {
|
|
|
74
79
|
config: ResolvedLotaRuntimeConfig
|
|
75
80
|
plugins: Record<string, LotaPlugin>
|
|
76
81
|
systemExecutors: Record<string, SystemNodeExecutor>
|
|
82
|
+
/** Pre-bound AI gateway model factories. Use instead of the legacy
|
|
83
|
+
* `aiGatewayChatModel(modelId)` / `aiGatewayModel(modelId)` module-level
|
|
84
|
+
* helpers — those now require a `deps` argument. */
|
|
85
|
+
ai: AiGatewayModels
|
|
77
86
|
connectPluginDatabases(): Promise<void>
|
|
78
87
|
connect(): Promise<void>
|
|
79
88
|
disconnect(): Promise<void>
|
|
@@ -88,87 +97,136 @@ export function createLotaRuntimeFromEnv(
|
|
|
88
97
|
)
|
|
89
98
|
}
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
// @effect-diagnostics-next-line asyncFunction:off -- public host entrypoint; returns Promise by design.
|
|
101
|
+
export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<LotaRuntime> {
|
|
102
|
+
const resolvedConfig = parseLotaRuntimeConfig(config)
|
|
103
|
+
const systemExecutors = { ...getBuiltInSystemExecutors(), ...resolvedConfig.systemExecutors }
|
|
104
|
+
const runtimeConfig = { ...resolvedConfig, systemExecutors } satisfies ResolvedLotaRuntimeConfig
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
const socialChatAgentId = runtimeConfig.socialChat?.agentId?.trim() || 'socialChat'
|
|
107
|
+
const socialChatAgentDisplayName = runtimeConfig.socialChat?.agentDisplayName?.trim() || 'Lota'
|
|
108
|
+
if (runtimeConfig.socialChat && !runtimeConfig.agents.roster.includes(socialChatAgentId)) {
|
|
109
|
+
throw new ConfigurationError({
|
|
110
|
+
message: `socialChat.agentId must be present in agents.roster: ${socialChatAgentId}`,
|
|
111
|
+
key: 'socialChat.agentId',
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
const resolvedAgentDisplayNames = runtimeConfig.socialChat
|
|
115
|
+
? { ...runtimeConfig.agents.displayNames, [socialChatAgentId]: socialChatAgentDisplayName }
|
|
116
|
+
: runtimeConfig.agents.displayNames
|
|
99
117
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
118
|
+
const infrastructureLayer = buildInfrastructureLayer(runtimeConfig, { resolvedAgentDisplayNames })
|
|
119
|
+
const aiGateway = Effect.runSync(makeAiGatewayService(runtimeConfig))
|
|
120
|
+
const aiGatewayModelsDeferred = Effect.runSync(Deferred.make<AiGatewayModels>())
|
|
121
|
+
const runtimeBridgeDeferred = Effect.runSync(Deferred.make<RuntimeBridge>())
|
|
122
|
+
const bridgeLayer = Layer.mergeAll(
|
|
123
|
+
Layer.succeed(AiGatewayTag, aiGateway),
|
|
124
|
+
Layer.effect(AiGatewayModelsTag, Deferred.await(aiGatewayModelsDeferred)),
|
|
125
|
+
Layer.effect(RuntimeBridgeTag, Deferred.await(runtimeBridgeDeferred)),
|
|
126
|
+
)
|
|
127
|
+
const fullLayer = buildDomainServiceLayer(infrastructureLayer, bridgeLayer)
|
|
128
|
+
const managedRuntime = ManagedRuntime.make(fullLayer)
|
|
129
|
+
const runtimeBridge: RuntimeBridge = {
|
|
130
|
+
runPromise: (effect, options) => managedRuntime.runPromise(effect, options),
|
|
131
|
+
runFork: (effect) => managedRuntime.runFork(effect),
|
|
132
|
+
}
|
|
133
|
+
const aiGatewayModels = createAiGatewayModels({
|
|
134
|
+
gateway: aiGateway,
|
|
135
|
+
runtimeConfig,
|
|
136
|
+
runPromise: runtimeBridge.runPromise,
|
|
137
|
+
runFork: runtimeBridge.runFork,
|
|
138
|
+
})
|
|
111
139
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
140
|
+
if (!Effect.runSync(Deferred.succeed(runtimeBridgeDeferred, runtimeBridge))) {
|
|
141
|
+
throw new ServiceError({ message: 'Failed to initialize the runtime bridge.' })
|
|
142
|
+
}
|
|
143
|
+
if (!Effect.runSync(Deferred.succeed(aiGatewayModelsDeferred, aiGatewayModels))) {
|
|
144
|
+
throw new ServiceError({ message: 'Failed to initialize AI gateway models.' })
|
|
145
|
+
}
|
|
115
146
|
|
|
116
|
-
|
|
117
|
-
|
|
147
|
+
try {
|
|
148
|
+
const resolvedServices = await managedRuntime.runPromise(
|
|
149
|
+
Effect.gen(function* () {
|
|
150
|
+
return {
|
|
151
|
+
db: yield* EffectDatabaseService,
|
|
152
|
+
redisManager: yield* EffectRedisService,
|
|
153
|
+
sharedSubscriber: yield* SharedThreadStreamSubscriberTag,
|
|
154
|
+
threadTurnService: yield* ThreadTurnServiceTag,
|
|
155
|
+
socialChatHistoryService: yield* SocialChatHistoryServiceTag,
|
|
156
|
+
learnedSkillService: yield* LearnedSkillServiceTag,
|
|
157
|
+
memoryService: yield* MemoryServiceTag,
|
|
158
|
+
runtimeAdapters: yield* RuntimeAdaptersServiceTag,
|
|
159
|
+
agentConfig: yield* AgentConfigServiceTag,
|
|
160
|
+
agentFactoryConfig: yield* AgentFactoryServiceTag,
|
|
161
|
+
queues: yield* LotaQueuesServiceTag,
|
|
162
|
+
autonomousJobService: yield* AutonomousJobServiceTag,
|
|
163
|
+
contextCompactionService: yield* ContextCompactionServiceTag,
|
|
164
|
+
threadService: yield* ThreadServiceTag,
|
|
165
|
+
planAgentHeartbeatService: yield* PlanAgentHeartbeatServiceTag,
|
|
166
|
+
planSchedulerService: yield* PlanSchedulerServiceTag,
|
|
167
|
+
planDeadlineService: yield* PlanDeadlineServiceTag,
|
|
168
|
+
planExecutorService: yield* PlanExecutorServiceTag,
|
|
169
|
+
planCycleService: yield* PlanCycleServiceTag,
|
|
170
|
+
threadTitleService: yield* ThreadTitleServiceTag,
|
|
171
|
+
recentActivityTitleService: yield* RecentActivityTitleServiceTag,
|
|
172
|
+
}
|
|
173
|
+
}),
|
|
174
|
+
)
|
|
175
|
+
const {
|
|
176
|
+
db,
|
|
177
|
+
redisManager,
|
|
178
|
+
sharedSubscriber,
|
|
179
|
+
threadTurnService,
|
|
180
|
+
socialChatHistoryService,
|
|
181
|
+
learnedSkillService,
|
|
182
|
+
memoryService,
|
|
183
|
+
runtimeAdapters,
|
|
184
|
+
agentConfig,
|
|
185
|
+
agentFactoryConfig,
|
|
186
|
+
queues,
|
|
187
|
+
autonomousJobService,
|
|
188
|
+
contextCompactionService,
|
|
189
|
+
threadService,
|
|
190
|
+
planAgentHeartbeatService,
|
|
191
|
+
planSchedulerService,
|
|
192
|
+
planDeadlineService,
|
|
193
|
+
planExecutorService,
|
|
194
|
+
planCycleService,
|
|
195
|
+
threadTitleService,
|
|
196
|
+
recentActivityTitleService,
|
|
197
|
+
} = resolvedServices
|
|
118
198
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
aiGateway: yield* AiGatewayTag,
|
|
147
|
-
runtimeConfigService: yield* RuntimeConfigServiceTag,
|
|
148
|
-
}
|
|
149
|
-
}),
|
|
150
|
-
),
|
|
151
|
-
catch: (error) =>
|
|
152
|
-
new ServiceError({
|
|
153
|
-
message: `Failed to initialize Effect runtime services: ${error instanceof Error ? error.message : String(error)}`,
|
|
154
|
-
cause: error,
|
|
155
|
-
}),
|
|
156
|
-
})
|
|
157
|
-
const {
|
|
158
|
-
db,
|
|
159
|
-
redisManager,
|
|
160
|
-
sharedSubscriber,
|
|
161
|
-
threadTurnService,
|
|
162
|
-
socialChatHistoryService,
|
|
163
|
-
learnedSkillService,
|
|
164
|
-
memoryService,
|
|
165
|
-
runtimeAdapters,
|
|
166
|
-
agentConfig,
|
|
167
|
-
agentFactoryConfig,
|
|
168
|
-
queues,
|
|
169
|
-
autonomousJobService,
|
|
170
|
-
contextCompactionService,
|
|
199
|
+
// ── Schema + plugin + worker + social-chat composition ────────────
|
|
200
|
+
const pluginRuntime = runtimeConfig.pluginRuntime ?? {}
|
|
201
|
+
const pluginContributions = Object.values(pluginRuntime).map((plugin) => plugin.contributions)
|
|
202
|
+
const hostContributionSchemaFiles = pluginContributions.flatMap((plugin) => plugin.schemaFiles)
|
|
203
|
+
const schemaFiles = [
|
|
204
|
+
...getBuiltInSchemaFiles(),
|
|
205
|
+
...(runtimeConfig.extraSchemaFiles ?? []),
|
|
206
|
+
...hostContributionSchemaFiles,
|
|
207
|
+
]
|
|
208
|
+
const contributionEnvKeys = [...LOTA_RUNTIME_ENV_KEYS, ...pluginContributions.flatMap((plugin) => plugin.envKeys)]
|
|
209
|
+
const connectedPluginDatabases = new Set<string>()
|
|
210
|
+
const connectPluginDatabases = createPluginDatabaseConnector(
|
|
211
|
+
managedRuntime,
|
|
212
|
+
pluginRuntime,
|
|
213
|
+
connectedPluginDatabases,
|
|
214
|
+
)
|
|
215
|
+
const disconnectPluginDatabases = createPluginDatabaseDisconnector(
|
|
216
|
+
managedRuntime,
|
|
217
|
+
pluginRuntime,
|
|
218
|
+
connectedPluginDatabases,
|
|
219
|
+
)
|
|
220
|
+
const workers = buildRuntimeWorkerRegistry(
|
|
221
|
+
queues,
|
|
222
|
+
{
|
|
223
|
+
databaseService: db,
|
|
224
|
+
runPromise: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
225
|
+
managedRuntime.runPromise(effect as unknown as Effect.Effect<A, E, never>),
|
|
171
226
|
threadService,
|
|
227
|
+
contextCompactionService,
|
|
228
|
+
autonomousJobService,
|
|
229
|
+
memoryService,
|
|
172
230
|
planAgentHeartbeatService,
|
|
173
231
|
planSchedulerService,
|
|
174
232
|
planDeadlineService,
|
|
@@ -176,134 +234,135 @@ export function createLotaRuntime(config: LotaRuntimeConfig): Promise<LotaRuntim
|
|
|
176
234
|
planCycleService,
|
|
177
235
|
threadTitleService,
|
|
178
236
|
recentActivityTitleService,
|
|
179
|
-
aiGateway,
|
|
180
|
-
runtimeConfigService,
|
|
181
|
-
} = resolvedServices
|
|
182
|
-
|
|
183
|
-
// Seed the AI gateway middleware so wrapGenerate/wrapStream/wrapEmbed
|
|
184
|
-
// edges can run Effects without reaching for an ambient runtime slot.
|
|
185
|
-
bindAiGatewayRuntime({
|
|
186
|
-
gateway: aiGateway,
|
|
187
|
-
runtimeConfig: runtimeConfigService,
|
|
188
|
-
runPromise: (effect) => managedRuntime.runPromise(effect),
|
|
189
|
-
runFork: (effect) => managedRuntime.runFork(effect),
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// ── Schema + plugin + worker + social-chat composition ────────────
|
|
193
|
-
const pluginRuntime = runtimeConfig.pluginRuntime ?? {}
|
|
194
|
-
const pluginContributions = Object.values(pluginRuntime).map((plugin) => plugin.contributions)
|
|
195
|
-
const hostContributionSchemaFiles = pluginContributions.flatMap((plugin) => plugin.schemaFiles)
|
|
196
|
-
const schemaFiles = [
|
|
197
|
-
...getBuiltInSchemaFiles(),
|
|
198
|
-
...(runtimeConfig.extraSchemaFiles ?? []),
|
|
199
|
-
...hostContributionSchemaFiles,
|
|
200
|
-
]
|
|
201
|
-
const contributionEnvKeys = [...LOTA_RUNTIME_ENV_KEYS, ...pluginContributions.flatMap((plugin) => plugin.envKeys)]
|
|
202
|
-
const connectedPluginDatabases = new Set<string>()
|
|
203
|
-
const connectPluginDatabases = createPluginDatabaseConnector(
|
|
204
|
-
managedRuntime,
|
|
205
|
-
pluginRuntime,
|
|
206
|
-
connectedPluginDatabases,
|
|
207
|
-
)
|
|
208
|
-
const disconnectPluginDatabases = createPluginDatabaseDisconnector(
|
|
209
|
-
managedRuntime,
|
|
210
|
-
pluginRuntime,
|
|
211
|
-
connectedPluginDatabases,
|
|
212
|
-
)
|
|
213
|
-
const workers = buildRuntimeWorkerRegistry(
|
|
214
|
-
queues,
|
|
215
|
-
{
|
|
216
|
-
databaseService: db,
|
|
217
|
-
threadService,
|
|
218
|
-
contextCompactionService,
|
|
219
|
-
autonomousJobService,
|
|
220
|
-
memoryService,
|
|
221
|
-
planAgentHeartbeatService,
|
|
222
|
-
planSchedulerService,
|
|
223
|
-
planDeadlineService,
|
|
224
|
-
planExecutorService,
|
|
225
|
-
planCycleService,
|
|
226
|
-
threadTitleService,
|
|
227
|
-
recentActivityTitleService,
|
|
228
|
-
redisManager,
|
|
229
|
-
},
|
|
230
|
-
runtimeConfig.extraWorkers,
|
|
231
|
-
)
|
|
232
|
-
const socialChat = createSocialChatRuntime({
|
|
233
|
-
agentConfig,
|
|
234
|
-
agentFactoryConfig,
|
|
235
|
-
redisClient: redisManager.getConnection() as unknown as Parameters<
|
|
236
|
-
typeof createSocialChatRuntime
|
|
237
|
-
>[0]['redisClient'],
|
|
238
|
-
socialChat: runtimeConfig.socialChat,
|
|
239
|
-
services: { learnedSkillService, memoryService, socialChatHistoryService, runtimeAdapters, queues },
|
|
240
|
-
})
|
|
241
|
-
const currentContext = yield* Effect.context()
|
|
242
|
-
const runPromiseWithCurrentContext = Effect.runPromiseWith(currentContext)
|
|
243
|
-
|
|
244
|
-
// ── Service surface (eager, plain-property) ───────────────────────
|
|
245
|
-
const { services, lota } = buildRuntimeServiceSurface({
|
|
246
|
-
managedRuntime,
|
|
247
|
-
db,
|
|
248
237
|
redisManager,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
238
|
+
},
|
|
239
|
+
runtimeConfig.extraWorkers,
|
|
240
|
+
)
|
|
241
|
+
const socialChat = createSocialChatRuntime({
|
|
242
|
+
agentConfig,
|
|
243
|
+
agentFactoryConfig,
|
|
244
|
+
// Bridge ioredis version mismatch: `@chat-adapter/state-ioredis` ships
|
|
245
|
+
// its own Redis type with newer methods (hexpireat etc.) not present
|
|
246
|
+
// in our pinned ioredis. The runtime behaviour is compatible.
|
|
247
|
+
redisClient: redisManager.getConnection() as unknown as Parameters<
|
|
248
|
+
typeof createSocialChatRuntime
|
|
249
|
+
>[0]['redisClient'],
|
|
250
|
+
socialChat: runtimeConfig.socialChat,
|
|
251
|
+
services: { learnedSkillService, memoryService, socialChatHistoryService, runtimeAdapters, queues },
|
|
252
|
+
})
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
254
|
+
// ── Service surface (eager, plain-property) ───────────────────────
|
|
255
|
+
const { services, lota } = buildRuntimeServiceSurface({
|
|
256
|
+
managedRuntime,
|
|
257
|
+
db,
|
|
258
|
+
redisManager,
|
|
259
|
+
sharedSubscriber,
|
|
260
|
+
threadTurnService,
|
|
261
|
+
socialChatHistoryService,
|
|
262
|
+
})
|
|
260
263
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
264
|
+
const disconnect = createRuntimeDisconnect({
|
|
265
|
+
managedRuntime,
|
|
266
|
+
socialChatShutdown: () => socialChat.shutdown(),
|
|
267
|
+
disconnectPluginDatabases,
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
// Host boundary: the ManagedRuntime carries the full 76-service union
|
|
271
|
+
// the SDK composes, but exposing that union in the public `LotaRuntime`
|
|
272
|
+
// interface would force every consumer to import every service tag. The
|
|
273
|
+
// cast keeps the public signature generic while trusting the runtime's
|
|
274
|
+
// actual capabilities — any missing service surfaces at runtime.
|
|
275
|
+
const lotaRuntime: LotaRuntime = {
|
|
276
|
+
runPromise: <A, E, R>(effect: Effect.Effect<A, E, R>, options?: { readonly signal?: AbortSignal }) =>
|
|
277
|
+
managedRuntime.runPromise(effect as unknown as Effect.Effect<A, E, never>, options),
|
|
278
|
+
runSync: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
279
|
+
managedRuntime.runSync(effect as unknown as Effect.Effect<A, E, never>),
|
|
280
|
+
runFork: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
281
|
+
managedRuntime.runFork(effect as unknown as Effect.Effect<A, E, never>),
|
|
282
|
+
services,
|
|
283
|
+
lota,
|
|
284
|
+
redis: {
|
|
285
|
+
manager: redisManager,
|
|
286
|
+
subscriber: sharedSubscriber.subscriber,
|
|
287
|
+
getConnection: () => redisManager.getConnection(),
|
|
288
|
+
getConnectionForBullMQ: () => redisManager.getConnectionForBullMQ(),
|
|
289
|
+
closeConnection: () =>
|
|
286
290
|
managedRuntime.runPromise(
|
|
287
|
-
Effect.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile),
|
|
291
|
-
)
|
|
292
|
-
yield* db.applySchema(bunFiles)
|
|
293
|
-
const schemaFingerprint = yield* effectTryPromise(() => computeSchemaFingerprint(schemaFiles))
|
|
294
|
-
yield* publishDatabaseBootstrapEffect({ databaseService: db, schemaFingerprint })
|
|
291
|
+
Effect.tryPromise({
|
|
292
|
+
try: () => redisManager.closeConnection(),
|
|
293
|
+
catch: (cause) => new ServiceError({ message: 'Failed to close Redis connection.', cause }),
|
|
295
294
|
}),
|
|
296
295
|
),
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
296
|
+
},
|
|
297
|
+
workers,
|
|
298
|
+
socialChat,
|
|
299
|
+
ai: aiGatewayModels,
|
|
300
|
+
schemaFiles,
|
|
301
|
+
contributions: { envKeys: [...new Set(contributionEnvKeys)], schemaFiles: hostContributionSchemaFiles },
|
|
302
|
+
config: runtimeConfig,
|
|
303
|
+
plugins: pluginRuntime,
|
|
304
|
+
systemExecutors,
|
|
305
|
+
connectPluginDatabases,
|
|
306
|
+
connect: () =>
|
|
307
|
+
managedRuntime.runPromise(
|
|
308
|
+
Effect.gen(function* () {
|
|
309
|
+
yield* db.connect()
|
|
310
|
+
const bunFiles = schemaFiles.map((schemaFile) =>
|
|
311
|
+
schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile),
|
|
312
|
+
)
|
|
313
|
+
yield* db.applySchema(bunFiles)
|
|
314
|
+
const schemaFingerprint = yield* Effect.tryPromise({
|
|
315
|
+
try: () => computeSchemaFingerprint(schemaFiles),
|
|
316
|
+
catch: (cause) => new ServiceError({ message: 'Failed to compute schema fingerprint.', cause }),
|
|
317
|
+
})
|
|
318
|
+
yield* publishDatabaseBootstrapEffect({ databaseService: db, schemaFingerprint })
|
|
319
|
+
}),
|
|
320
|
+
),
|
|
321
|
+
disconnect,
|
|
304
322
|
}
|
|
305
|
-
|
|
306
|
-
|
|
323
|
+
registerRuntimeShutdownHandlers(lotaRuntime)
|
|
324
|
+
return lotaRuntime
|
|
325
|
+
} catch (error) {
|
|
326
|
+
await managedRuntime.dispose()
|
|
327
|
+
throw new ServiceError({
|
|
328
|
+
message: `Failed to initialize Effect runtime services: ${error instanceof Error ? error.message : String(error)}`,
|
|
329
|
+
cause: error,
|
|
330
|
+
})
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Per-runtime shutdown handler — the previous module-level latch meant that
|
|
336
|
+
* a second `createLotaRuntime` call (e.g. reconnect after a test harness
|
|
337
|
+
* disposes the first runtime) silently skipped signal wiring and left the
|
|
338
|
+
* fresh runtime leaking on SIGTERM/SIGINT. We instead maintain a live set
|
|
339
|
+
* and dispatch each signal to every registered runtime, removing each as it
|
|
340
|
+
* disconnects. `process.on` (not `once`) keeps the dispatcher alive across
|
|
341
|
+
* multiple registrations within the same process.
|
|
342
|
+
*/
|
|
343
|
+
const activeShutdownHandlers = new Set<() => Promise<void>>()
|
|
344
|
+
let signalDispatcherInstalled = false
|
|
345
|
+
|
|
346
|
+
function installSignalDispatcherOnce(): void {
|
|
347
|
+
if (signalDispatcherInstalled) return
|
|
348
|
+
signalDispatcherInstalled = true
|
|
349
|
+
const dispatch = () => {
|
|
350
|
+
for (const handler of activeShutdownHandlers) {
|
|
351
|
+
void handler().catch(() => undefined)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
process.on('SIGTERM', dispatch)
|
|
355
|
+
process.on('SIGINT', dispatch)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function registerRuntimeShutdownHandlers(runtime: LotaRuntime): void {
|
|
359
|
+
installSignalDispatcherOnce()
|
|
360
|
+
// @effect-diagnostics-next-line asyncFunction:off -- signal dispatcher callback; Promise-based by design.
|
|
361
|
+
const handler = async () => {
|
|
362
|
+
activeShutdownHandlers.delete(handler)
|
|
363
|
+
await runtime.disconnect().catch(() => undefined)
|
|
364
|
+
}
|
|
365
|
+
activeShutdownHandlers.add(handler)
|
|
307
366
|
}
|
|
308
367
|
|
|
309
368
|
function getBuiltInSchemaFiles(): URL[] {
|
|
@@ -3,6 +3,7 @@ import { Schema, Effect } from 'effect'
|
|
|
3
3
|
import type { BoundQuery, RecordId } from 'surrealdb'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
|
|
6
|
+
import { ERROR_TAGS } from '../effect/errors'
|
|
6
7
|
import type { RecordIdRef } from './record-id'
|
|
7
8
|
import type { SurrealDBService } from './service'
|
|
8
9
|
import type { DatabaseTable } from './tables'
|
|
@@ -25,19 +26,11 @@ export interface CursorPaginationConfig {
|
|
|
25
26
|
queryBefore: (parentId: RecordIdRef, cursorCreatedAt: Date, cursorId: RecordId, limit: number) => BoundQuery
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
class CursorPaginationError extends Schema.TaggedErrorClass<CursorPaginationError>()(
|
|
29
|
+
class CursorPaginationError extends Schema.TaggedErrorClass<CursorPaginationError>()(ERROR_TAGS.CursorPaginationError, {
|
|
29
30
|
message: Schema.String,
|
|
30
31
|
cause: Schema.optional(Schema.Defect),
|
|
31
32
|
}) {}
|
|
32
33
|
|
|
33
|
-
export function listMessageHistoryPage(
|
|
34
|
-
db: SurrealDBService,
|
|
35
|
-
config: CursorPaginationConfig,
|
|
36
|
-
params: { parentId: RecordIdRef; take: number; beforeMessageId?: string },
|
|
37
|
-
): Promise<MessageHistoryPage> {
|
|
38
|
-
return Effect.runPromise(listMessageHistoryPageEffect(db, config, params))
|
|
39
|
-
}
|
|
40
|
-
|
|
41
34
|
export function listMessageHistoryPageEffect(
|
|
42
35
|
db: SurrealDBService,
|
|
43
36
|
config: CursorPaginationConfig,
|