@lota-sdk/core 0.4.9 → 0.4.11

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 (182) hide show
  1. package/package.json +2 -2
  2. package/src/ai/embedding-cache.ts +3 -1
  3. package/src/ai-gateway/ai-gateway.ts +164 -82
  4. package/src/ai-gateway/index.ts +16 -1
  5. package/src/config/agent-defaults.ts +4 -107
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/config/background-processing.ts +1 -1
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +22 -25
  10. package/src/config/thread-defaults.ts +1 -10
  11. package/src/create-runtime.ts +145 -670
  12. package/src/db/base.service.ts +30 -38
  13. package/src/db/memory-query-builder.ts +2 -1
  14. package/src/db/memory-store.ts +29 -20
  15. package/src/db/memory.ts +188 -195
  16. package/src/db/service-normalization.ts +97 -64
  17. package/src/db/service.ts +496 -384
  18. package/src/db/startup.ts +30 -19
  19. package/src/effect/helpers.ts +30 -5
  20. package/src/effect/index.ts +7 -7
  21. package/src/effect/layers.ts +75 -72
  22. package/src/effect/services.ts +15 -11
  23. package/src/embeddings/provider.ts +65 -71
  24. package/src/index.ts +13 -12
  25. package/src/queues/autonomous-job.queue.ts +177 -143
  26. package/src/queues/context-compaction.queue.ts +41 -39
  27. package/src/queues/delayed-node-promotion.queue.ts +61 -42
  28. package/src/queues/document-processor.queue.ts +5 -3
  29. package/src/queues/index.ts +1 -0
  30. package/src/queues/memory-consolidation.queue.ts +79 -53
  31. package/src/queues/organization-learning.queue.ts +70 -33
  32. package/src/queues/plan-agent-heartbeat.queue.ts +111 -83
  33. package/src/queues/plan-scheduler.queue.ts +101 -97
  34. package/src/queues/post-chat-memory.queue.ts +56 -46
  35. package/src/queues/queue-factory.ts +146 -69
  36. package/src/queues/queues.service.ts +61 -0
  37. package/src/queues/title-generation.queue.ts +44 -44
  38. package/src/redis/connection.ts +181 -164
  39. package/src/redis/org-memory-lock.ts +24 -9
  40. package/src/redis/redis-lease-lock.ts +8 -1
  41. package/src/redis/stream-context.ts +17 -9
  42. package/src/runtime/agent-identity-overrides.ts +7 -3
  43. package/src/runtime/agent-runtime-policy.ts +10 -5
  44. package/src/runtime/agent-stream-helpers.ts +24 -15
  45. package/src/runtime/chat-run-orchestration.ts +1 -1
  46. package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
  47. package/src/runtime/context-compaction/context-compaction.ts +131 -85
  48. package/src/runtime/domain-layer.ts +203 -0
  49. package/src/runtime/execution-plan-visibility.ts +5 -2
  50. package/src/runtime/graph-designer.ts +0 -14
  51. package/src/runtime/helper-model.ts +8 -4
  52. package/src/runtime/index.ts +1 -1
  53. package/src/runtime/indexed-repositories-policy.ts +2 -6
  54. package/src/runtime/memory/memory-block.ts +19 -9
  55. package/src/runtime/memory/memory-pipeline.ts +53 -66
  56. package/src/runtime/memory/memory-scope.ts +33 -29
  57. package/src/runtime/plugin-resolution.ts +58 -62
  58. package/src/runtime/post-turn-side-effects.ts +139 -161
  59. package/src/runtime/retrieval-adapters.ts +4 -4
  60. package/src/runtime/runtime-config.ts +3 -9
  61. package/src/runtime/runtime-extensions.ts +0 -43
  62. package/src/runtime/runtime-lifecycle.ts +124 -0
  63. package/src/runtime/runtime-services.ts +455 -0
  64. package/src/runtime/runtime-worker-registry.ts +113 -30
  65. package/src/runtime/social-chat/social-chat-agent-runner.ts +13 -8
  66. package/src/runtime/social-chat/social-chat-history.ts +24 -13
  67. package/src/runtime/social-chat/social-chat.ts +420 -369
  68. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +64 -57
  69. package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
  70. package/src/runtime/thread-chat-helpers.ts +18 -9
  71. package/src/runtime/thread-turn-context.ts +28 -74
  72. package/src/runtime/turn-lifecycle.ts +6 -14
  73. package/src/services/agent-activity.service.ts +169 -176
  74. package/src/services/agent-executor.service.ts +207 -196
  75. package/src/services/artifact.service.ts +10 -5
  76. package/src/services/attachment.service.ts +16 -48
  77. package/src/services/autonomous-job.service.ts +81 -87
  78. package/src/services/background-work.service.ts +54 -0
  79. package/src/services/chat-run-registry.service.ts +3 -1
  80. package/src/services/context-compaction.service.ts +8 -10
  81. package/src/services/document-chunk.service.ts +8 -17
  82. package/src/services/execution-plan/execution-plan-graph.ts +122 -109
  83. package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
  84. package/src/services/execution-plan/execution-plan.service.ts +68 -51
  85. package/src/services/feedback-loop.service.ts +1 -1
  86. package/src/services/global-orchestrator.service.ts +49 -15
  87. package/src/services/graph-full-routing.ts +49 -37
  88. package/src/services/index.ts +1 -0
  89. package/src/services/institutional-memory.service.ts +8 -17
  90. package/src/services/learned-skill.service.ts +38 -35
  91. package/src/services/memory/memory-conversation.ts +10 -5
  92. package/src/services/memory/memory-errors.ts +27 -0
  93. package/src/services/memory/memory-org-memory.ts +14 -3
  94. package/src/services/memory/memory-preseeded.ts +10 -4
  95. package/src/services/memory/memory-utils.ts +2 -1
  96. package/src/services/memory/memory.service.ts +37 -52
  97. package/src/services/memory/rerank.service.ts +3 -11
  98. package/src/services/monitoring-window.service.ts +1 -1
  99. package/src/services/mutating-approval.service.ts +1 -1
  100. package/src/services/node-workspace.service.ts +2 -2
  101. package/src/services/notification.service.ts +16 -4
  102. package/src/services/organization-member.service.ts +1 -1
  103. package/src/services/organization.service.ts +34 -51
  104. package/src/services/ownership-dispatcher.service.ts +148 -95
  105. package/src/services/plan/plan-agent-heartbeat.service.ts +30 -16
  106. package/src/services/plan/plan-agent-query.service.ts +13 -9
  107. package/src/services/plan/plan-approval.service.ts +52 -48
  108. package/src/services/plan/plan-artifact.service.ts +2 -2
  109. package/src/services/plan/plan-builder.service.ts +2 -2
  110. package/src/services/plan/plan-checkpoint.service.ts +1 -1
  111. package/src/services/plan/plan-compiler.service.ts +1 -1
  112. package/src/services/plan/plan-completion-side-effects.ts +99 -113
  113. package/src/services/plan/plan-coordination.service.ts +1 -1
  114. package/src/services/plan/plan-cycle.service.ts +171 -202
  115. package/src/services/plan/plan-deadline.service.ts +304 -307
  116. package/src/services/plan/plan-event-delivery.service.ts +84 -72
  117. package/src/services/plan/plan-executor-context.ts +2 -0
  118. package/src/services/plan/plan-executor-graph.ts +375 -353
  119. package/src/services/plan/plan-executor-helpers.ts +60 -75
  120. package/src/services/plan/plan-executor.service.ts +494 -489
  121. package/src/services/plan/plan-run.service.ts +12 -19
  122. package/src/services/plan/plan-scheduler.service.ts +89 -82
  123. package/src/services/plan/plan-template.service.ts +1 -1
  124. package/src/services/plan/plan-transaction-events.ts +8 -5
  125. package/src/services/plan/plan-validator.service.ts +1 -1
  126. package/src/services/plan/plan-workspace.service.ts +17 -11
  127. package/src/services/plugin-executor.service.ts +26 -21
  128. package/src/services/quality-metrics.service.ts +1 -1
  129. package/src/services/queue-job.service.ts +8 -17
  130. package/src/services/recent-activity-title.service.ts +22 -10
  131. package/src/services/recent-activity.service.ts +1 -1
  132. package/src/services/skill-resolver.service.ts +1 -1
  133. package/src/services/social-chat-history.service.ts +37 -20
  134. package/src/services/system-executor.service.ts +25 -20
  135. package/src/services/thread/thread-bootstrap.ts +37 -19
  136. package/src/services/thread/thread-listing.ts +2 -1
  137. package/src/services/thread/thread-memory-block.ts +18 -5
  138. package/src/services/thread/thread-message.service.ts +30 -13
  139. package/src/services/thread/thread-title.service.ts +1 -1
  140. package/src/services/thread/thread-turn-execution.ts +87 -83
  141. package/src/services/thread/thread-turn-preparation.service.ts +65 -40
  142. package/src/services/thread/thread-turn-streaming.ts +32 -36
  143. package/src/services/thread/thread-turn.ts +43 -29
  144. package/src/services/thread/thread.service.ts +32 -8
  145. package/src/services/user.service.ts +1 -1
  146. package/src/services/write-intent-validator.service.ts +1 -1
  147. package/src/storage/attachment-storage.service.ts +7 -4
  148. package/src/storage/generated-document-storage.service.ts +1 -1
  149. package/src/system-agents/context-compaction.agent.ts +1 -1
  150. package/src/system-agents/helper-agent-options.ts +1 -1
  151. package/src/system-agents/memory-reranker.agent.ts +1 -1
  152. package/src/system-agents/memory.agent.ts +1 -1
  153. package/src/system-agents/recent-activity-title-refiner.agent.ts +9 -6
  154. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  155. package/src/system-agents/skill-extractor.agent.ts +1 -1
  156. package/src/system-agents/skill-manager.agent.ts +1 -1
  157. package/src/system-agents/thread-router.agent.ts +23 -20
  158. package/src/system-agents/title-generator.agent.ts +1 -1
  159. package/src/tools/execution-plan.tool.ts +36 -20
  160. package/src/tools/fetch-webpage.tool.ts +30 -22
  161. package/src/tools/firecrawl-client.ts +1 -6
  162. package/src/tools/plan-approval.tool.ts +9 -1
  163. package/src/tools/remember-memory.tool.ts +3 -6
  164. package/src/tools/research-topic.tool.ts +12 -3
  165. package/src/tools/search-web.tool.ts +26 -18
  166. package/src/tools/search.tool.ts +4 -5
  167. package/src/tools/team-think.tool.ts +139 -121
  168. package/src/utils/async.ts +15 -6
  169. package/src/utils/errors.ts +27 -15
  170. package/src/workers/bootstrap.ts +34 -58
  171. package/src/workers/memory-consolidation.worker.ts +4 -1
  172. package/src/workers/organization-learning.worker.ts +16 -3
  173. package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
  174. package/src/workers/regular-chat-memory-digest.runner.ts +46 -29
  175. package/src/workers/skill-extraction.runner.ts +13 -15
  176. package/src/workers/worker-utils.ts +14 -8
  177. package/src/config/search.ts +0 -3
  178. package/src/effect/awaitable-effect.ts +0 -87
  179. package/src/effect/runtime-ref.ts +0 -25
  180. package/src/effect/runtime.ts +0 -31
  181. package/src/redis/runtime-connection.ts +0 -10
  182. package/src/runtime/agent-types.ts +0 -1
@@ -2,11 +2,11 @@ import type { ChatMessage } from '@lota-sdk/shared'
2
2
  import { createUIMessageStream } from 'ai'
3
3
  import { Context, Schema, Effect, Layer } from 'effect'
4
4
 
5
+ import type { ResolvedAgentConfig } from '../../config/agent-defaults'
5
6
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
6
7
  import { TABLES } from '../../db/tables'
7
8
  import { BadRequestError } from '../../effect/errors'
8
- import { runPromise } from '../../effect/runtime'
9
- import { getCurrentRuntime } from '../../effect/runtime-ref'
9
+ import { AgentConfigServiceTag } from '../../effect/services'
10
10
  import { hasApprovalRespondedParts, isApprovalContinuationRequest } from '../../runtime/approval-continuation'
11
11
  import { shouldPlanNodeUseVisibleTurn } from '../../runtime/execution-plan-visibility'
12
12
  import { wrapResponseWithKeepalive } from '../../utils/sse-keepalive'
@@ -39,6 +39,7 @@ class ThreadTurnServiceError extends Schema.TaggedErrorClass<ThreadTurnServiceEr
39
39
  }) {}
40
40
 
41
41
  interface ThreadTurnDeps {
42
+ agentConfig: ResolvedAgentConfig
42
43
  planExecutor: ReturnType<typeof makePlanExecutorService>
43
44
  planRun: ReturnType<typeof makePlanRunService>
44
45
  thread: ReturnType<typeof makeThreadService>
@@ -179,7 +180,7 @@ const triggerPlanNodeTurnEffect = Effect.fn('ThreadTurn.triggerPlanNodeTurn')(fu
179
180
  const spec = yield* deps.planRun.getPlanSpecById(run.planSpecId)
180
181
  const nodeSpec = yield* deps.planRun.getNodeSpecByNodeId(spec.id, params.nodeId)
181
182
 
182
- if (!shouldPlanNodeUseVisibleTurn(spec, nodeSpec) || nodeSpec.owner.executorType !== 'agent') {
183
+ if (!shouldPlanNodeUseVisibleTurn(deps.agentConfig, spec, nodeSpec) || nodeSpec.owner.executorType !== 'agent') {
183
184
  return yield* new BadRequestError({
184
185
  message: `Plan node "${params.nodeId}" is not eligible for a visible plan turn.`,
185
186
  })
@@ -188,10 +189,13 @@ const triggerPlanNodeTurnEffect = Effect.fn('ThreadTurn.triggerPlanNodeTurn')(fu
188
189
  let activeRun = run
189
190
  let nodeRun = yield* deps.planRun.getNodeRunByNodeId(run.id, params.nodeId)
190
191
  if (nodeRun.status === 'ready') {
191
- yield* Effect.tryPromise({
192
- try: () => deps.planExecutor.transitionNodeToRunning({ runId: params.runId, nodeId: params.nodeId }),
193
- catch: (cause) => new ThreadTurnServiceError({ message: 'Failed to transition plan node to running.', cause }),
194
- })
192
+ yield* deps.planExecutor
193
+ .transitionNodeToRunning({ runId: params.runId, nodeId: params.nodeId })
194
+ .pipe(
195
+ Effect.mapError(
196
+ (cause) => new ThreadTurnServiceError({ message: 'Failed to transition plan node to running.', cause }),
197
+ ),
198
+ )
195
199
  activeRun = yield* deps.planRun.getRunById(params.runId)
196
200
  nodeRun = yield* deps.planRun.getNodeRunByNodeId(run.id, params.nodeId)
197
201
  }
@@ -299,45 +303,55 @@ export function makeThreadTurnService(deps: ThreadTurnDeps) {
299
303
  export class ThreadTurnServiceTag extends Context.Service<
300
304
  ThreadTurnServiceTag,
301
305
  ReturnType<typeof makeThreadTurnService>
302
- >()('ThreadTurnService') {}
306
+ >()('@lota-sdk/core/ThreadTurnService') {}
303
307
 
304
308
  export const ThreadTurnServiceLive = Layer.effect(
305
309
  ThreadTurnServiceTag,
306
310
  Effect.gen(function* () {
311
+ const agentConfig = yield* AgentConfigServiceTag
307
312
  const planExecutor = yield* PlanExecutorServiceTag
308
313
  const planRun = yield* PlanRunServiceTag
309
314
  const thread = yield* ThreadServiceTag
310
315
  const threadTurnPreparation = yield* ThreadTurnPreparationServiceTag
311
316
  const user = yield* UserServiceTag
312
- return makeThreadTurnService({ planExecutor, planRun, thread, threadTurnPreparation, user })
317
+ return makeThreadTurnService({ agentConfig, planExecutor, planRun, thread, threadTurnPreparation, user })
313
318
  }),
314
319
  )
315
320
 
316
- function getThreadTurnService() {
317
- return getCurrentRuntime().runSync(Effect.service(ThreadTurnServiceTag))
318
- }
319
-
320
- export function createThreadApprovalContinuationStream(params: ThreadApprovalContinuationParams) {
321
- return runPromise(getThreadTurnService().createThreadApprovalContinuationStream(params))
322
- }
321
+ export const createThreadApprovalContinuationStream = Effect.fn('ThreadTurn.createApprovalContinuationStream')(
322
+ function* (params: ThreadApprovalContinuationParams) {
323
+ const threadTurnService = yield* ThreadTurnServiceTag
324
+ return yield* threadTurnService.createThreadApprovalContinuationStream(params)
325
+ },
326
+ )
323
327
 
324
- export function createThreadNativeToolApprovalStream(params: ThreadApprovalContinuationParams) {
325
- return runPromise(getThreadTurnService().createThreadNativeToolApprovalStream(params))
326
- }
328
+ export const createThreadNativeToolApprovalStream = Effect.fn('ThreadTurn.createNativeToolApprovalStream')(function* (
329
+ params: ThreadApprovalContinuationParams,
330
+ ) {
331
+ const threadTurnService = yield* ThreadTurnServiceTag
332
+ return yield* threadTurnService.createThreadNativeToolApprovalStream(params)
333
+ })
327
334
 
328
- export function createThreadTurnStream(params: ThreadTurnParams) {
329
- return runPromise(getThreadTurnService().createThreadTurnStream(params))
330
- }
335
+ export const createThreadTurnStream = Effect.fn('ThreadTurn.createThreadTurnStream')(function* (
336
+ params: ThreadTurnParams,
337
+ ) {
338
+ const threadTurnService = yield* ThreadTurnServiceTag
339
+ return yield* threadTurnService.createThreadTurnStream(params)
340
+ })
331
341
 
332
- export function runThreadTurnInBackground(params: ThreadTurnParams): Promise<PreparedThreadTurnResult> {
333
- return runPromise(getThreadTurnService().runThreadTurnInBackground(params))
334
- }
342
+ export const runThreadTurnInBackground = Effect.fn('ThreadTurn.runThreadTurnInBackground')(function* (
343
+ params: ThreadTurnParams,
344
+ ) {
345
+ const threadTurnService = yield* ThreadTurnServiceTag
346
+ return yield* threadTurnService.runThreadTurnInBackground(params)
347
+ })
335
348
 
336
- export function triggerPlanNodeTurn(params: {
349
+ export const triggerPlanNodeTurn = Effect.fn('ThreadTurn.triggerPlanNodeTurn')(function* (params: {
337
350
  runId: string
338
351
  nodeId: string
339
352
  abortSignal?: AbortSignal
340
353
  streamId?: string
341
- }): Promise<PreparedThreadTurnResult> {
342
- return runPromise(getThreadTurnService().triggerPlanNodeTurn(params))
343
- }
354
+ }) {
355
+ const threadTurnService = yield* ThreadTurnServiceTag
356
+ return yield* threadTurnService.triggerPlanNodeTurn(params)
357
+ })
@@ -2,17 +2,25 @@ import { THREAD, sdkThreadStatusSchema } from '@lota-sdk/shared'
2
2
  import { Context, Effect, Layer } from 'effect'
3
3
  import { surql } from 'surrealdb'
4
4
 
5
- import { getCoreThreadProfile, isAgentName } from '../../config/agent-defaults'
5
+ import type { ResolvedAgentConfig } from '../../config/agent-defaults'
6
+ import { isAgentName } from '../../config/agent-defaults'
7
+ import type { ResolvedThreadBootstrapConfig } from '../../config/thread-defaults'
6
8
  import { ensureRecordId, isRecordIdInput, recordIdToString } from '../../db/record-id'
7
9
  import type { RecordIdRef } from '../../db/record-id'
8
10
  import type { SurrealDBService } from '../../db/service'
9
11
  import { TABLES } from '../../db/tables'
10
12
  import { BadRequestError, ServiceError } from '../../effect/errors'
11
13
  import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
12
- import { DatabaseServiceTag, RedisServiceTag } from '../../effect/services'
14
+ import {
15
+ AgentConfigServiceTag,
16
+ DatabaseServiceTag,
17
+ RedisServiceTag,
18
+ ThreadConfigServiceTag,
19
+ } from '../../effect/services'
13
20
  import type { RedisConnectionManager } from '../../redis/connection'
14
21
  import { CompactionCoordinationTag } from '../../runtime/chat-run-orchestration'
15
22
  import { toIsoDateTimeString } from '../../utils/date-time'
23
+ import { BackgroundWorkService } from '../background-work.service'
16
24
  import { ChatRunRegistryTag } from '../chat-run-registry.service'
17
25
  import { ContextCompactionServiceTag } from '../context-compaction.service'
18
26
  import type { makeContextCompactionService } from '../context-compaction.service'
@@ -34,9 +42,9 @@ function assertMutableThreadEffect(thread: ThreadRecord): Effect.Effect<void, Ba
34
42
  : Effect.void
35
43
  }
36
44
 
37
- function getDefaultTitle(thread: Pick<ThreadRecord, 'type' | 'threadType'>): string {
45
+ function getDefaultTitle(agentConfig: ResolvedAgentConfig, thread: Pick<ThreadRecord, 'type' | 'threadType'>): string {
38
46
  if (thread.type === 'thread' && typeof thread.threadType === 'string') {
39
- return getCoreThreadProfile(thread.threadType).config.title
47
+ return agentConfig.getCoreThreadProfile(thread.threadType).config.title
40
48
  }
41
49
 
42
50
  return THREAD.DEFAULT_TITLE
@@ -62,12 +70,15 @@ type ChatRunRegistry = Context.Service.Shape<typeof ChatRunRegistryTag>
62
70
  type CompactionCoordination = Context.Service.Shape<typeof CompactionCoordinationTag>
63
71
 
64
72
  interface ThreadServiceDeps {
73
+ agentConfig: ResolvedAgentConfig
74
+ threadBootstrapConfig: ResolvedThreadBootstrapConfig
65
75
  db: SurrealDBService
66
76
  redis: RedisConnectionManager
67
77
  chatRunRegistry: ChatRunRegistry
68
78
  threadMessageService: ReturnType<typeof makeThreadMessageService>
69
79
  contextCompactionService: ReturnType<typeof makeContextCompactionService>
70
80
  compactionCoordination: CompactionCoordination
81
+ background: Context.Service.Shape<typeof BackgroundWorkService>
71
82
  }
72
83
 
73
84
  export function makeThreadService(deps: ThreadServiceDeps) {
@@ -115,7 +126,7 @@ export function makeThreadService(deps: ThreadServiceDeps) {
115
126
  normalizeRecordIdStringEffect(thread.userId, TABLES.USER),
116
127
  normalizeRecordIdStringEffect(thread.organizationId, TABLES.ORGANIZATION),
117
128
  ])
118
- return NormalizedThreadSchema.parse({
129
+ const candidate = {
119
130
  id,
120
131
  userId,
121
132
  organizationId,
@@ -124,13 +135,17 @@ export function makeThreadService(deps: ThreadServiceDeps) {
124
135
  nameGenerated: thread.nameGenerated,
125
136
  isRunning,
126
137
  isCompacting: thread.isCompacting === true,
127
- ...(isAgentName(thread.agentId) ? { agentId: thread.agentId } : {}),
128
- title: thread.title ?? getDefaultTitle(thread),
138
+ ...(isAgentName(deps.agentConfig, thread.agentId) ? { agentId: thread.agentId } : {}),
139
+ title: thread.title ?? getDefaultTitle(deps.agentConfig, thread),
129
140
  status: thread.status,
130
141
  memoryBlock: formatMemoryBlockForPrompt(thread),
131
142
  members: thread.members,
132
143
  createdAt: toIsoDateTimeString(thread.createdAt),
133
144
  updatedAt: toIsoDateTimeString(thread.updatedAt),
145
+ }
146
+ return yield* Effect.try({
147
+ try: () => NormalizedThreadSchema.parse(candidate),
148
+ catch: (cause) => new ServiceError({ message: 'Failed to parse normalized thread.', cause }),
134
149
  })
135
150
  })
136
151
  }
@@ -143,6 +158,8 @@ export function makeThreadService(deps: ThreadServiceDeps) {
143
158
  }
144
159
 
145
160
  const bootstrap = createThreadBootstrapHelpers({
161
+ agentConfig: deps.agentConfig,
162
+ threadBootstrapConfig: deps.threadBootstrapConfig,
146
163
  threadStore,
147
164
  threadMessageService: deps.threadMessageService,
148
165
  redis: deps.redis,
@@ -154,6 +171,7 @@ export function makeThreadService(deps: ThreadServiceDeps) {
154
171
  const memory = createThreadMemoryBlockHelpers({
155
172
  threadStore,
156
173
  contextCompactionService: { compactMemoryBlock: deps.contextCompactionService.compactMemoryBlock },
174
+ background: deps.background,
157
175
  })
158
176
 
159
177
  function getById(threadId: RecordIdRef) {
@@ -311,25 +329,31 @@ export function makeThreadService(deps: ThreadServiceDeps) {
311
329
  }
312
330
 
313
331
  export class ThreadServiceTag extends Context.Service<ThreadServiceTag, ReturnType<typeof makeThreadService>>()(
314
- 'ThreadService',
332
+ '@lota-sdk/core/ThreadService',
315
333
  ) {}
316
334
 
317
335
  export const ThreadServiceLive = Layer.effect(
318
336
  ThreadServiceTag,
319
337
  Effect.gen(function* () {
338
+ const agentConfig = yield* AgentConfigServiceTag
339
+ const threadBootstrapConfig = yield* ThreadConfigServiceTag
320
340
  const db = yield* DatabaseServiceTag
321
341
  const redis = yield* RedisServiceTag
322
342
  const chatRunRegistry = yield* ChatRunRegistryTag
323
343
  const threadMessageService = yield* ThreadMessageServiceTag
324
344
  const contextCompactionService = yield* ContextCompactionServiceTag
325
345
  const compactionCoordination = yield* CompactionCoordinationTag
346
+ const background = yield* BackgroundWorkService
326
347
  return makeThreadService({
348
+ agentConfig,
349
+ threadBootstrapConfig,
327
350
  db,
328
351
  redis,
329
352
  chatRunRegistry,
330
353
  threadMessageService,
331
354
  contextCompactionService,
332
355
  compactionCoordination,
356
+ background,
333
357
  })
334
358
  }),
335
359
  )
@@ -94,7 +94,7 @@ export function makeUserService(db: SurrealDBService) {
94
94
  }
95
95
 
96
96
  export class UserServiceTag extends Context.Service<UserServiceTag, ReturnType<typeof makeUserService>>()(
97
- 'UserService',
97
+ '@lota-sdk/core/UserService',
98
98
  ) {}
99
99
 
100
100
  export const UserServiceLive = Layer.effect(
@@ -85,7 +85,7 @@ export function makeWriteIntentValidatorService() {
85
85
  export class WriteIntentValidatorServiceTag extends Context.Service<
86
86
  WriteIntentValidatorServiceTag,
87
87
  ReturnType<typeof makeWriteIntentValidatorService>
88
- >()('WriteIntentValidatorService') {}
88
+ >()('@lota-sdk/core/WriteIntentValidatorService') {}
89
89
 
90
90
  export const WriteIntentValidatorServiceLive = Layer.succeed(
91
91
  WriteIntentValidatorServiceTag,
@@ -107,11 +107,14 @@ export function makeAttachmentStorageService(config: ResolvedLotaRuntimeConfig)
107
107
  storageKey: string
108
108
  orgId: string
109
109
  userId: string
110
- }): void {
110
+ }): Effect.Effect<void, ForbiddenError> {
111
111
  const storagePrefix = buildUploadStoragePrefix({ orgId, userId })
112
112
  if (!storageKey.startsWith(storagePrefix)) {
113
- throw new ForbiddenError({ message: 'Upload does not belong to the current organization/user scope.' })
113
+ return Effect.fail(
114
+ new ForbiddenError({ message: 'Upload does not belong to the current organization/user scope.' }),
115
+ )
114
116
  }
117
+ return Effect.void
115
118
  }
116
119
 
117
120
  function writeOrganizationDocumentEffect({
@@ -284,7 +287,7 @@ export function makeAttachmentStorageService(config: ResolvedLotaRuntimeConfig)
284
287
  pagesPerPart?: number
285
288
  }) {
286
289
  return Effect.gen(function* () {
287
- assertUploadOwnership({ storageKey: upload.storageKey, orgId, userId })
290
+ yield* assertUploadOwnership({ storageKey: upload.storageKey, orgId, userId })
288
291
 
289
292
  const parsed = yield* extractStoredAttachmentPagesEffect({
290
293
  storageKey: upload.storageKey,
@@ -437,7 +440,7 @@ export function makeAttachmentStorageService(config: ResolvedLotaRuntimeConfig)
437
440
  export class AttachmentStorageServiceTag extends Context.Service<
438
441
  AttachmentStorageServiceTag,
439
442
  ReturnType<typeof makeAttachmentStorageService>
440
- >()('AttachmentStorageService') {}
443
+ >()('@lota-sdk/core/AttachmentStorageService') {}
441
444
 
442
445
  export const AttachmentStorageServiceLive = Layer.effect(
443
446
  AttachmentStorageServiceTag,
@@ -86,7 +86,7 @@ export function makeGeneratedDocumentStorageService(config: ResolvedLotaRuntimeC
86
86
  export class GeneratedDocumentStorageServiceTag extends Context.Service<
87
87
  GeneratedDocumentStorageServiceTag,
88
88
  ReturnType<typeof makeGeneratedDocumentStorageService>
89
- >()('GeneratedDocumentStorageService') {}
89
+ >()('@lota-sdk/core/GeneratedDocumentStorageService') {}
90
90
 
91
91
  export const GeneratedDocumentStorageServiceLive = Layer.effect(
92
92
  GeneratedDocumentStorageServiceTag,
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
 
3
4
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
@@ -6,7 +7,6 @@ import {
6
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
8
9
  } from '../config/model-constants'
9
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
10
10
  import { resolveHelperAgentOptions } from './helper-agent-options'
11
11
 
12
12
  const CONTEXT_COMPACTION_PROMPT = `<agent-instructions>
@@ -1,4 +1,4 @@
1
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
2
2
 
3
3
  interface HelperAgentOptionOverrides {
4
4
  instructions?: string
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
 
3
4
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
@@ -6,7 +7,6 @@ import {
6
7
  OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
7
8
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
8
9
  } from '../config/model-constants'
9
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
10
10
  import { resolveHelperAgentOptions } from './helper-agent-options'
11
11
 
12
12
  export const MEMORY_RERANKER_PROMPT = `<agent-instructions>
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
 
3
4
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
@@ -6,7 +7,6 @@ import {
6
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
8
9
  } from '../config/model-constants'
9
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
10
10
  import { resolveHelperAgentOptions } from './helper-agent-options'
11
11
 
12
12
  export const ORG_MEMORY_PROMPT = `<agent-instructions>
@@ -1,19 +1,19 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
 
3
4
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
4
5
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
5
- import { getLeadAgentDisplayName } from '../config/agent-defaults'
6
+ import type { ResolvedAgentConfig } from '../config/agent-defaults'
6
7
  import {
7
8
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
8
9
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
9
10
  } from '../config/model-constants'
10
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
11
11
  import { resolveHelperAgentOptions } from './helper-agent-options'
12
12
 
13
13
  const RECENT_ACTIVITY_TITLE_MAX_TOKENS = 256
14
14
 
15
- export function buildRecentActivityTitleRefinerPrompt(): string {
16
- const leadAgentDisplayName = getLeadAgentDisplayName() || 'the lead agent'
15
+ export function buildRecentActivityTitleRefinerPrompt(agentConfig: ResolvedAgentConfig): string {
16
+ const leadAgentDisplayName = agentConfig.displayNames[agentConfig.leadAgentId] || 'the lead agent'
17
17
 
18
18
  return `<agent-instructions>
19
19
  You are ${leadAgentDisplayName} writing the visible title for a recent activity item.
@@ -76,14 +76,17 @@ Return only the title text. No quotes, labels, JSON, markdown, or explanation.
76
76
  </output>
77
77
  </agent-instructions>`
78
78
 
79
- export function createRecentActivityTitleRefinerAgent(options: CreateHelperToolLoopAgentOptions) {
79
+ export function createRecentActivityTitleRefinerAgent(
80
+ agentConfig: ResolvedAgentConfig,
81
+ options: CreateHelperToolLoopAgentOptions,
82
+ ) {
80
83
  return new ToolLoopAgent({
81
84
  id: 'recent-activity-title-refiner',
82
85
  model: aiGatewayChatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
83
86
  headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
84
87
  providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
85
88
  ...resolveHelperAgentOptions(options, {
86
- instructions: buildRecentActivityTitleRefinerPrompt(),
89
+ instructions: buildRecentActivityTitleRefinerPrompt(agentConfig),
87
90
  maxOutputTokens: RECENT_ACTIVITY_TITLE_MAX_TOKENS,
88
91
  }),
89
92
  })
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
 
3
4
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
@@ -6,7 +7,6 @@ import {
6
7
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
7
8
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
8
9
  } from '../config/model-constants'
9
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
10
10
  import { resolveHelperAgentOptions } from './helper-agent-options'
11
11
 
12
12
  const REGULAR_CHAT_MEMORY_DIGEST_MAX_TOKENS = 8_192
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
  import { z } from 'zod'
3
4
 
@@ -7,7 +8,6 @@ import {
7
8
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
8
9
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
9
10
  } from '../config/model-constants'
10
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
11
11
  import { resolveHelperAgentOptions } from './helper-agent-options'
12
12
 
13
13
  const SKILL_EXTRACTOR_MAX_TOKENS = 8_192
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
  import { z } from 'zod'
3
4
 
@@ -7,7 +8,6 @@ import {
7
8
  OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
8
9
  OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
9
10
  } from '../config/model-constants'
10
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
11
11
  import { resolveHelperAgentOptions } from './helper-agent-options'
12
12
 
13
13
  const SKILL_MANAGER_MAX_TOKENS = 4_096
@@ -4,12 +4,7 @@ import { z } from 'zod'
4
4
 
5
5
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
6
6
  import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
7
- import {
8
- getAgentDescriptions,
9
- getAgentDisplayNames,
10
- getAgentShortDisplayNames,
11
- getRouterModelId,
12
- } from '../config/agent-defaults'
7
+ import type { ResolvedAgentConfig } from '../config/agent-defaults'
13
8
  import { chatLogger } from '../config/logger'
14
9
  import type { ValidationError } from '../effect/errors'
15
10
  import { AiGenerationError } from '../effect/errors'
@@ -28,6 +23,7 @@ export type RouterTriageResult = z.infer<typeof TriageResultSchema>
28
23
  export type RouterCheckResult = { done: true } | RouterCheckContinueResult
29
24
 
30
25
  interface RouterDisplayOptions {
26
+ agentConfig: ResolvedAgentConfig
31
27
  displayNamesById?: Partial<Record<string, string>>
32
28
  shortDisplayNamesById?: Partial<Record<string, string>>
33
29
  routingAliasesByAgentId?: Partial<Record<string, string[]>>
@@ -44,26 +40,26 @@ function readStringArrayOverride(
44
40
  return record && Object.hasOwn(record, key) ? record[key] : undefined
45
41
  }
46
42
 
47
- function readDisplayName(agentId: string, options?: RouterDisplayOptions): string {
48
- const override = readStringOverride(options?.displayNamesById, agentId)
43
+ function readDisplayName(agentId: string, options: RouterDisplayOptions): string {
44
+ const override = readStringOverride(options.displayNamesById, agentId)
49
45
  if (override !== undefined) {
50
46
  return override
51
47
  }
52
48
 
53
- return getAgentDisplayNames()[agentId] ?? agentId
49
+ return options.agentConfig.displayNames[agentId] ?? agentId
54
50
  }
55
51
 
56
- function readShortDisplayName(agentId: string, options?: RouterDisplayOptions): string {
57
- const override = readStringOverride(options?.shortDisplayNamesById, agentId)
52
+ function readShortDisplayName(agentId: string, options: RouterDisplayOptions): string {
53
+ const override = readStringOverride(options.shortDisplayNamesById, agentId)
58
54
  if (override !== undefined) {
59
55
  return override
60
56
  }
61
57
 
62
- return getAgentShortDisplayNames()[agentId] ?? readDisplayName(agentId, options)
58
+ return options.agentConfig.shortDisplayNames[agentId] ?? readDisplayName(agentId, options)
63
59
  }
64
60
 
65
- function buildMembersDescription(members: readonly string[], options?: RouterDisplayOptions): string {
66
- const agentDescriptions = getAgentDescriptions()
61
+ function buildMembersDescription(members: readonly string[], options: RouterDisplayOptions): string {
62
+ const agentDescriptions = options.agentConfig.descriptions
67
63
  return members
68
64
  .map((id) => {
69
65
  const display = readDisplayName(id, options)
@@ -77,7 +73,7 @@ function escapeRegex(value: string): string {
77
73
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
78
74
  }
79
75
 
80
- function buildExplicitAgentRoutingContext(agentId: string, options?: RouterDisplayOptions): string {
76
+ function buildExplicitAgentRoutingContext(agentId: string, options: RouterDisplayOptions): string {
81
77
  const displayName = readDisplayName(agentId, options)
82
78
  return `Respond directly to the part of the user's request explicitly addressed to ${displayName}.`
83
79
  }
@@ -85,16 +81,16 @@ function buildExplicitAgentRoutingContext(agentId: string, options?: RouterDispl
85
81
  function extractExplicitAgentTargets(
86
82
  messageText: string,
87
83
  members: readonly string[],
88
- options?: RouterDisplayOptions,
84
+ options: RouterDisplayOptions,
89
85
  ): string[] {
90
86
  const normalizedMessage = messageText.trim()
91
87
  if (!normalizedMessage) return []
92
88
 
93
89
  const memberSet = new Set(members)
94
90
  const aliases = new Map<string, string>()
95
- const defaultDisplayNames = options?.displayNamesById === undefined ? getAgentDisplayNames() : undefined
91
+ const defaultDisplayNames = options.displayNamesById === undefined ? options.agentConfig.displayNames : undefined
96
92
  const defaultShortDisplayNames =
97
- options?.shortDisplayNamesById === undefined ? getAgentShortDisplayNames() : undefined
93
+ options.shortDisplayNamesById === undefined ? options.agentConfig.shortDisplayNames : undefined
98
94
 
99
95
  for (const member of members) {
100
96
  for (const rawAlias of [
@@ -103,7 +99,7 @@ function extractExplicitAgentTargets(
103
99
  defaultShortDisplayNames?.[member],
104
100
  readDisplayName(member, options),
105
101
  readShortDisplayName(member, options),
106
- ...(readStringArrayOverride(options?.routingAliasesByAgentId, member) ?? []),
102
+ ...(readStringArrayOverride(options.routingAliasesByAgentId, member) ?? []),
107
103
  ]) {
108
104
  if (typeof rawAlias !== 'string') continue
109
105
  const alias = rawAlias.trim().toLowerCase()
@@ -173,12 +169,13 @@ function toRouterGenerationError(label: 'triage' | 'check', error: unknown): AiG
173
169
  }
174
170
 
175
171
  function generateRouterObjectEffect<TSchema extends z.ZodTypeAny>(params: {
172
+ agentConfig: ResolvedAgentConfig
176
173
  schema: TSchema
177
174
  system: string
178
175
  prompt: string
179
176
  label: 'triage' | 'check'
180
177
  }): Effect.Effect<z.infer<TSchema> | null, never> {
181
- const modelId = getRouterModelId() ?? 'openrouter/openai/gpt-5.4-nano'
178
+ const modelId = params.agentConfig.routerModelId ?? 'openrouter/openai/gpt-5.4-nano'
182
179
 
183
180
  return Effect.tryPromise({
184
181
  try: () =>
@@ -219,6 +216,7 @@ function generateRouterObjectEffect<TSchema extends z.ZodTypeAny>(params: {
219
216
  }
220
217
 
221
218
  export function triageThreadMessage(params: {
219
+ agentConfig: ResolvedAgentConfig
222
220
  threadTitle: string
223
221
  members: readonly string[]
224
222
  messageText: string
@@ -230,6 +228,7 @@ export function triageThreadMessage(params: {
230
228
  return Effect.runPromise(
231
229
  Effect.gen(function* () {
232
230
  const displayOptions: RouterDisplayOptions = {
231
+ agentConfig: params.agentConfig,
233
232
  displayNamesById: params.displayNamesById,
234
233
  shortDisplayNamesById: params.shortDisplayNamesById,
235
234
  routingAliasesByAgentId: params.routingAliasesByAgentId,
@@ -254,6 +253,7 @@ export function triageThreadMessage(params: {
254
253
  .join('\n\n')
255
254
 
256
255
  const parsed = yield* generateRouterObjectEffect({
256
+ agentConfig: params.agentConfig,
257
257
  schema: TriageResultSchema,
258
258
  system: TRIAGE_SYSTEM_PROMPT,
259
259
  prompt,
@@ -279,6 +279,7 @@ export function triageThreadMessage(params: {
279
279
  }
280
280
 
281
281
  export function checkForNextAgent(params: {
282
+ agentConfig: ResolvedAgentConfig
282
283
  threadTitle: string
283
284
  members: readonly string[]
284
285
  messageText: string
@@ -291,6 +292,7 @@ export function checkForNextAgent(params: {
291
292
  return Effect.runPromise(
292
293
  Effect.gen(function* () {
293
294
  const displayOptions: RouterDisplayOptions = {
295
+ agentConfig: params.agentConfig,
294
296
  displayNamesById: params.displayNamesById,
295
297
  shortDisplayNamesById: params.shortDisplayNamesById,
296
298
  routingAliasesByAgentId: params.routingAliasesByAgentId,
@@ -320,6 +322,7 @@ export function checkForNextAgent(params: {
320
322
  ].join('\n\n')
321
323
 
322
324
  const parsed = yield* generateRouterObjectEffect({
325
+ agentConfig: params.agentConfig,
323
326
  schema: CheckResultObjectSchema,
324
327
  system: CHECK_SYSTEM_PROMPT,
325
328
  prompt,
@@ -1,3 +1,4 @@
1
+ import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
1
2
  import { ToolLoopAgent } from 'ai'
2
3
 
3
4
  import { aiGatewayChatModel } from '../ai-gateway/ai-gateway'
@@ -6,7 +7,6 @@ import {
6
7
  OPENROUTER_FAST_REASONING_MODEL_ID,
7
8
  OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS,
8
9
  } from '../config/model-constants'
9
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
10
10
  import { resolveHelperAgentOptions } from './helper-agent-options'
11
11
 
12
12
  const THREAD_TITLE_MAX_TOKENS = 512