@lota-sdk/core 0.4.9 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) 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 +38 -10
  4. package/src/config/agent-defaults.ts +22 -9
  5. package/src/config/agent-types.ts +1 -1
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/index.ts +0 -1
  8. package/src/config/logger.ts +20 -7
  9. package/src/config/thread-defaults.ts +12 -4
  10. package/src/create-runtime.ts +69 -656
  11. package/src/db/memory-query-builder.ts +2 -1
  12. package/src/db/memory-store.ts +29 -20
  13. package/src/db/memory.ts +188 -195
  14. package/src/db/service-normalization.ts +97 -64
  15. package/src/db/service.ts +706 -538
  16. package/src/db/startup.ts +30 -19
  17. package/src/effect/awaitable-effect.ts +46 -37
  18. package/src/effect/helpers.ts +30 -5
  19. package/src/effect/index.ts +7 -5
  20. package/src/effect/layers.ts +82 -72
  21. package/src/effect/runtime.ts +18 -3
  22. package/src/effect/services.ts +15 -11
  23. package/src/embeddings/provider.ts +65 -66
  24. package/src/index.ts +13 -11
  25. package/src/queues/autonomous-job.queue.ts +59 -71
  26. package/src/queues/context-compaction.queue.ts +6 -18
  27. package/src/queues/delayed-node-promotion.queue.ts +9 -17
  28. package/src/queues/organization-learning.queue.ts +17 -4
  29. package/src/queues/plan-agent-heartbeat.queue.ts +23 -20
  30. package/src/queues/plan-scheduler.queue.ts +6 -18
  31. package/src/queues/post-chat-memory.queue.ts +6 -18
  32. package/src/queues/queue-factory.ts +128 -50
  33. package/src/queues/title-generation.queue.ts +6 -17
  34. package/src/redis/connection.ts +181 -164
  35. package/src/redis/runtime-connection.ts +13 -3
  36. package/src/redis/stream-context.ts +17 -9
  37. package/src/runtime/agent-runtime-policy.ts +1 -1
  38. package/src/runtime/agent-stream-helpers.ts +15 -11
  39. package/src/runtime/chat-run-orchestration.ts +1 -1
  40. package/src/runtime/context-compaction/context-compaction-runtime.ts +1 -1
  41. package/src/runtime/context-compaction/context-compaction.ts +126 -82
  42. package/src/runtime/domain-layer.ts +192 -0
  43. package/src/runtime/graph-designer.ts +15 -7
  44. package/src/runtime/helper-model.ts +8 -4
  45. package/src/runtime/index.ts +0 -1
  46. package/src/runtime/memory/memory-block.ts +19 -9
  47. package/src/runtime/memory/memory-pipeline.ts +53 -66
  48. package/src/runtime/memory/memory-scope.ts +33 -29
  49. package/src/runtime/plugin-resolution.ts +33 -54
  50. package/src/runtime/post-turn-side-effects.ts +6 -26
  51. package/src/runtime/retrieval-adapters.ts +4 -4
  52. package/src/runtime/runtime-accessors.ts +92 -0
  53. package/src/runtime/runtime-config.ts +3 -3
  54. package/src/runtime/runtime-extensions.ts +20 -9
  55. package/src/runtime/runtime-lifecycle.ts +124 -0
  56. package/src/runtime/runtime-services.ts +386 -0
  57. package/src/runtime/runtime-token.ts +47 -0
  58. package/src/runtime/social-chat/social-chat-agent-runner.ts +7 -5
  59. package/src/runtime/social-chat/social-chat-history.ts +21 -12
  60. package/src/runtime/social-chat/social-chat.ts +401 -365
  61. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +58 -52
  62. package/src/runtime/thread-turn-context.ts +21 -27
  63. package/src/services/agent-activity.service.ts +1 -1
  64. package/src/services/agent-executor.service.ts +179 -187
  65. package/src/services/artifact.service.ts +10 -5
  66. package/src/services/attachment.service.ts +35 -1
  67. package/src/services/autonomous-job.service.ts +58 -56
  68. package/src/services/background-work.service.ts +54 -0
  69. package/src/services/chat-run-registry.service.ts +3 -1
  70. package/src/services/context-compaction.service.ts +1 -1
  71. package/src/services/document-chunk.service.ts +8 -17
  72. package/src/services/execution-plan/execution-plan-graph.ts +74 -52
  73. package/src/services/execution-plan/execution-plan.service.ts +1 -1
  74. package/src/services/feedback-loop.service.ts +1 -1
  75. package/src/services/global-orchestrator.service.ts +33 -10
  76. package/src/services/graph-full-routing.ts +44 -33
  77. package/src/services/index.ts +1 -0
  78. package/src/services/institutional-memory.service.ts +8 -17
  79. package/src/services/learned-skill.service.ts +38 -35
  80. package/src/services/memory/memory-errors.ts +27 -0
  81. package/src/services/memory/memory-org-memory.ts +14 -3
  82. package/src/services/memory/memory-preseeded.ts +10 -4
  83. package/src/services/memory/memory-utils.ts +2 -1
  84. package/src/services/memory/memory.service.ts +26 -44
  85. package/src/services/memory/rerank.service.ts +3 -11
  86. package/src/services/monitoring-window.service.ts +1 -1
  87. package/src/services/mutating-approval.service.ts +1 -1
  88. package/src/services/node-workspace.service.ts +2 -2
  89. package/src/services/notification.service.ts +16 -4
  90. package/src/services/organization-member.service.ts +1 -1
  91. package/src/services/organization.service.ts +34 -51
  92. package/src/services/ownership-dispatcher.service.ts +132 -90
  93. package/src/services/plan/plan-agent-heartbeat.service.ts +1 -1
  94. package/src/services/plan/plan-agent-query.service.ts +1 -1
  95. package/src/services/plan/plan-approval.service.ts +52 -48
  96. package/src/services/plan/plan-artifact.service.ts +2 -2
  97. package/src/services/plan/plan-builder.service.ts +2 -2
  98. package/src/services/plan/plan-checkpoint.service.ts +1 -1
  99. package/src/services/plan/plan-compiler.service.ts +1 -1
  100. package/src/services/plan/plan-completion-side-effects.ts +18 -24
  101. package/src/services/plan/plan-coordination.service.ts +1 -1
  102. package/src/services/plan/plan-cycle.service.ts +171 -164
  103. package/src/services/plan/plan-deadline.service.ts +290 -304
  104. package/src/services/plan/plan-event-delivery.service.ts +44 -39
  105. package/src/services/plan/plan-executor-graph.ts +114 -67
  106. package/src/services/plan/plan-executor-helpers.ts +60 -75
  107. package/src/services/plan/plan-executor.service.ts +550 -467
  108. package/src/services/plan/plan-run.service.ts +12 -19
  109. package/src/services/plan/plan-scheduler.service.ts +27 -33
  110. package/src/services/plan/plan-template.service.ts +1 -1
  111. package/src/services/plan/plan-transaction-events.ts +8 -5
  112. package/src/services/plan/plan-validator.service.ts +1 -1
  113. package/src/services/plan/plan-workspace.service.ts +17 -11
  114. package/src/services/plugin-executor.service.ts +26 -21
  115. package/src/services/quality-metrics.service.ts +1 -1
  116. package/src/services/queue-job.service.ts +8 -17
  117. package/src/services/recent-activity-title.service.ts +17 -9
  118. package/src/services/recent-activity.service.ts +1 -1
  119. package/src/services/skill-resolver.service.ts +1 -1
  120. package/src/services/social-chat-history.service.ts +37 -20
  121. package/src/services/system-executor.service.ts +25 -20
  122. package/src/services/thread/thread-bootstrap.ts +26 -10
  123. package/src/services/thread/thread-listing.ts +2 -1
  124. package/src/services/thread/thread-memory-block.ts +18 -5
  125. package/src/services/thread/thread-message.service.ts +24 -8
  126. package/src/services/thread/thread-title.service.ts +1 -1
  127. package/src/services/thread/thread-turn-execution.ts +1 -1
  128. package/src/services/thread/thread-turn-preparation.service.ts +18 -16
  129. package/src/services/thread/thread-turn-streaming.ts +12 -11
  130. package/src/services/thread/thread-turn.ts +43 -10
  131. package/src/services/thread/thread.service.ts +11 -2
  132. package/src/services/user.service.ts +1 -1
  133. package/src/services/write-intent-validator.service.ts +1 -1
  134. package/src/storage/attachment-storage.service.ts +7 -4
  135. package/src/storage/generated-document-storage.service.ts +1 -1
  136. package/src/system-agents/context-compaction.agent.ts +1 -1
  137. package/src/system-agents/helper-agent-options.ts +1 -1
  138. package/src/system-agents/memory-reranker.agent.ts +1 -1
  139. package/src/system-agents/memory.agent.ts +1 -1
  140. package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
  141. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  142. package/src/system-agents/skill-extractor.agent.ts +1 -1
  143. package/src/system-agents/skill-manager.agent.ts +1 -1
  144. package/src/system-agents/title-generator.agent.ts +1 -1
  145. package/src/tools/execution-plan.tool.ts +28 -17
  146. package/src/tools/fetch-webpage.tool.ts +20 -13
  147. package/src/tools/firecrawl-client.ts +13 -3
  148. package/src/tools/plan-approval.tool.ts +9 -1
  149. package/src/tools/search-web.tool.ts +16 -9
  150. package/src/tools/team-think.tool.ts +2 -2
  151. package/src/utils/async.ts +15 -6
  152. package/src/utils/errors.ts +27 -15
  153. package/src/workers/bootstrap.ts +25 -48
  154. package/src/workers/organization-learning.worker.ts +1 -1
  155. package/src/workers/regular-chat-memory-digest.runner.ts +25 -15
  156. package/src/workers/worker-utils.ts +20 -2
  157. package/src/config/search.ts +0 -3
  158. package/src/runtime/agent-types.ts +0 -1
@@ -17,6 +17,7 @@ import { ensureRecordId } from '../db/record-id'
17
17
  import type { SurrealDBService } from '../db/service'
18
18
  import { TABLES } from '../db/tables'
19
19
  import { BadRequestError, NotFoundError } from '../effect/errors'
20
+ import { makeEffectTryPromiseWithOperation } from '../effect/helpers'
20
21
  import { runPromise } from '../effect/runtime'
21
22
  import { DatabaseServiceTag } from '../effect/services'
22
23
  import { toValidationError } from '../effect/zod'
@@ -112,27 +113,15 @@ class AgentExecutorError extends Schema.TaggedErrorClass<AgentExecutorError>()('
112
113
  cause: Schema.Defect,
113
114
  }) {}
114
115
 
115
- function tryAgentExecutorPromise<A>(
116
+ const tryAgentExecutorPromise = makeEffectTryPromiseWithOperation(
117
+ (operation, message, cause) => new AgentExecutorError({ operation, message, cause }),
118
+ )
119
+ const tryAgentExecutorTask = <A>(
116
120
  operation: string,
117
121
  message: string,
118
- thunk: () => PromiseLike<A> | A | Effect.Effect<A, unknown, never>,
119
- ): Effect.Effect<A, AgentExecutorError> {
120
- return Effect.suspend(() => {
121
- try {
122
- const value = thunk()
123
- if (Effect.isEffect(value)) {
124
- return value.pipe(Effect.mapError((cause) => new AgentExecutorError({ operation, message, cause })))
125
- }
126
-
127
- return Effect.tryPromise({
128
- try: () => Promise.resolve(value),
129
- catch: (cause) => new AgentExecutorError({ operation, message, cause }),
130
- })
131
- } catch (cause) {
132
- return Effect.fail(new AgentExecutorError({ operation, message, cause }))
133
- }
134
- })
135
- }
122
+ task: () => PromiseLike<A>,
123
+ ): Effect.Effect<A, AgentExecutorError> =>
124
+ Effect.tryPromise({ try: task, catch: (cause) => new AgentExecutorError({ operation, message, cause }) })
136
125
 
137
126
  function isAgentExecutorOwner(
138
127
  owner: PlanNodeSpec['owner'],
@@ -190,7 +179,7 @@ function buildWriteIntentTool(params: {
190
179
  }
191
180
 
192
181
  function createExecuteNode(deps: AgentExecutorDeps) {
193
- return function executeNode(params: {
182
+ return Effect.fn('AgentExecutor.executeNode')(function* (params: {
194
183
  nodeSpec: PlanNodeSpec
195
184
  resolvedInput: Record<string, unknown>
196
185
  inputArtifacts: PlanArtifactSubmission[]
@@ -198,70 +187,70 @@ function createExecuteNode(deps: AgentExecutorDeps) {
198
187
  executionMode?: ExecutionMode
199
188
  schemaRegistry?: PlanSchemaRegistry
200
189
  }) {
201
- return Effect.gen(function* () {
202
- const owner = params.nodeSpec.owner
203
- if (!isAgentExecutorOwner(owner)) {
204
- return yield* new BadRequestError({
205
- message: `AgentExecutor cannot execute owner type "${owner.executorType}".`,
206
- })
207
- }
190
+ const owner = params.nodeSpec.owner
191
+ if (!isAgentExecutorOwner(owner)) {
192
+ return yield* new BadRequestError({ message: `AgentExecutor cannot execute owner type "${owner.executorType}".` })
193
+ }
208
194
 
209
- const agentId = owner.ref
210
- if (!getAgentRoster().includes(agentId)) {
211
- return yield* new BadRequestError({ message: `Agent executor "${agentId}" is not registered.` })
212
- }
195
+ const agentId = owner.ref
196
+ if (!getAgentRoster().includes(agentId)) {
197
+ return yield* new BadRequestError({ message: `Agent executor "${agentId}" is not registered.` })
198
+ }
213
199
 
214
- const thread = yield* tryAgentExecutorPromise('load-thread', 'Failed to load dispatched thread.', () =>
215
- deps.db.findOne(TABLES.THREAD, { id: ensureRecordId(params.context.threadId, TABLES.THREAD) }, ThreadSchema),
216
- )
217
- if (!thread) {
218
- return yield* new NotFoundError({
219
- resource: TABLES.THREAD,
220
- message: `Thread ${params.context.threadId} not found for dispatched execution.`,
221
- })
222
- }
200
+ const thread = yield* tryAgentExecutorPromise('load-thread', 'Failed to load dispatched thread.', () =>
201
+ deps.db.findOne(TABLES.THREAD, { id: ensureRecordId(params.context.threadId, TABLES.THREAD) }, ThreadSchema),
202
+ ).pipe(Effect.withSpan('AgentExecutor.loadThread'))
203
+ if (!thread) {
204
+ return yield* new NotFoundError({
205
+ resource: TABLES.THREAD,
206
+ message: `Thread ${params.context.threadId} not found for dispatched execution.`,
207
+ })
208
+ }
223
209
 
224
- const organizationRef = ensureRecordId(params.context.organizationId, TABLES.ORGANIZATION)
225
- const threadRef = ensureRecordId(params.context.threadId, TABLES.THREAD)
226
- const userRefSource = params.context.userId ?? thread.userId
227
- if (!userRefSource) {
228
- return yield* new BadRequestError({
229
- message: `Thread ${params.context.threadId} is missing a user context for dispatched execution.`,
230
- })
231
- }
232
- const userRef = ensureRecordId(userRefSource, TABLES.USER)
233
- const userName = params.context.userName ?? 'User'
234
- const [linearInstallation, githubInstallation, indexedRepoContext] = yield* Effect.all([
235
- tryAgentExecutorPromise(
236
- 'get-linear-installation',
237
- `Failed to load Linear installation for org ${params.context.organizationId}.`,
238
- () => getLinearInstallationByOrgId(organizationRef),
239
- ),
240
- tryAgentExecutorPromise(
241
- 'get-github-installation',
242
- `Failed to load GitHub installation for org ${params.context.organizationId}.`,
243
- () => getGithubInstallationForOrganization(params.context.organizationId),
244
- ),
245
- tryAgentExecutorPromise(
246
- 'build-indexed-repositories-context',
247
- `Failed to build indexed repository context for org ${params.context.organizationId}.`,
248
- () => buildIndexedRepositoriesContext(params.context.organizationId),
249
- ),
250
- ])
251
-
252
- const mode = params.executionMode ?? 'linear'
253
- const dispatchMode = thread.type === 'group' ? 'fixedThreadMode' : 'direct'
254
- const dispatchInstructionSections = [
255
- buildOwnershipDispatchContextSection({
256
- node: params.nodeSpec,
257
- resolvedInput: params.resolvedInput,
258
- inputArtifacts: params.inputArtifacts,
259
- upstreamHandoffs: params.context.upstreamHandoffs,
260
- }),
261
- ]
210
+ const organizationRef = ensureRecordId(params.context.organizationId, TABLES.ORGANIZATION)
211
+ const threadRef = ensureRecordId(params.context.threadId, TABLES.THREAD)
212
+ const userRefSource = params.context.userId ?? thread.userId
213
+ if (!userRefSource) {
214
+ return yield* new BadRequestError({
215
+ message: `Thread ${params.context.threadId} is missing a user context for dispatched execution.`,
216
+ })
217
+ }
218
+ const userRef = ensureRecordId(userRefSource, TABLES.USER)
219
+ const userName = params.context.userName ?? 'User'
220
+ const { linearInstallation, githubInstallation, indexedRepoContext } = yield* Effect.all({
221
+ linearInstallation: tryAgentExecutorTask(
222
+ 'get-linear-installation',
223
+ `Failed to load Linear installation for org ${params.context.organizationId}.`,
224
+ () => getLinearInstallationByOrgId(organizationRef),
225
+ ),
226
+ githubInstallation: tryAgentExecutorTask(
227
+ 'get-github-installation',
228
+ `Failed to load GitHub installation for org ${params.context.organizationId}.`,
229
+ () => getGithubInstallationForOrganization(params.context.organizationId),
230
+ ),
231
+ indexedRepoContext: tryAgentExecutorTask(
232
+ 'build-indexed-repositories-context',
233
+ `Failed to build indexed repository context for org ${params.context.organizationId}.`,
234
+ () => buildIndexedRepositoriesContext(params.context.organizationId),
235
+ ),
236
+ }).pipe(Effect.withSpan('AgentExecutor.loadInstallations'))
237
+
238
+ const mode = params.executionMode ?? 'linear'
239
+ const dispatchMode = thread.type === 'group' ? 'fixedThreadMode' : 'direct'
240
+ const dispatchInstructionSections = [
241
+ buildOwnershipDispatchContextSection({
242
+ node: params.nodeSpec,
243
+ resolvedInput: params.resolvedInput,
244
+ inputArtifacts: params.inputArtifacts,
245
+ upstreamHandoffs: params.context.upstreamHandoffs,
246
+ }),
247
+ ]
262
248
 
263
- const turnHooks = getTurnHooks()
264
- const agentResolutionValue = yield* Effect.tryPromise(() =>
249
+ const turnHooks = getTurnHooks()
250
+ const agentResolutionValue = yield* tryAgentExecutorPromise(
251
+ 'resolve-agent',
252
+ `Failed to resolve agent "${agentId}" for dispatched execution.`,
253
+ () =>
265
254
  Promise.resolve(
266
255
  turnHooks.resolveAgent?.({
267
256
  agentId,
@@ -278,133 +267,136 @@ function createExecuteNode(deps: AgentExecutorDeps) {
278
267
  context: null,
279
268
  }),
280
269
  ),
281
- )
282
- const agentResolution = isRecord(agentResolutionValue) ? agentResolutionValue : null
283
- const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? agentId
284
- const runtimeConfig = getAgentRuntimeConfig({
285
- agentId: resolvedAgentId,
286
- threadType: thread.type,
287
- mode: dispatchMode,
288
- onboardingActive: false,
289
- linearInstalled: Boolean(linearInstallation),
290
- additionalInstructionSections: mergeInstructionSections(
291
- dispatchInstructionSections,
292
- readInstructionSections(agentResolution?.additionalInstructionSections),
293
- ),
294
- responseGuardSection: buildOwnershipDispatchResponseGuard({ node: params.nodeSpec, executionMode: mode }),
295
- })
296
-
297
- const agentFactoryConfig = getResolvedAgentFactoryConfig()
298
- const rawTools = yield* tryAgentExecutorPromise(
299
- 'build-agent-tools',
300
- `Failed to build agent tools for "${resolvedAgentId}".`,
301
- () =>
302
- Promise.resolve(
303
- agentFactoryConfig.buildAgentTools({
304
- agentId: resolvedAgentId,
305
- orgId: organizationRef,
306
- userId: userRef,
307
- userName,
308
- threadId: threadRef,
309
- orgIdString: params.context.organizationId,
310
- threadType: thread.type,
311
- mode: dispatchMode,
312
- linearInstalled: Boolean(linearInstallation),
313
- onboardingActive: false,
314
- githubInstalled: Boolean(githubInstallation),
315
- provideRepoTool: indexedRepoContext.provideRepoTool,
316
- defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[resolvedAgentId],
317
- memoryBlock: '',
318
- onAppendMemoryBlock: () => undefined,
319
- availableUploads: [],
320
- includeExecutionPlanTools: false,
321
- }),
322
- ),
323
- )
324
- const tools = applyToolPolicy(rawTools, params.nodeSpec)
270
+ )
271
+ const agentResolution = isRecord(agentResolutionValue) ? agentResolutionValue : null
272
+ const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? agentId
273
+ const runtimeConfig = getAgentRuntimeConfig({
274
+ agentId: resolvedAgentId,
275
+ threadType: thread.type,
276
+ mode: dispatchMode,
277
+ onboardingActive: false,
278
+ linearInstalled: Boolean(linearInstallation),
279
+ additionalInstructionSections: mergeInstructionSections(
280
+ dispatchInstructionSections,
281
+ readInstructionSections(agentResolution?.additionalInstructionSections),
282
+ ),
283
+ responseGuardSection: buildOwnershipDispatchResponseGuard({ node: params.nodeSpec, executionMode: mode }),
284
+ })
325
285
 
326
- const agentFactory = agentFactoryConfig.createAgent[resolvedAgentId]
327
- if (!agentFactory) {
328
- return yield* new BadRequestError({ message: `Agent factory "${resolvedAgentId}" is not registered.` })
329
- }
286
+ const agentFactoryConfig = getResolvedAgentFactoryConfig()
287
+ const rawTools = yield* tryAgentExecutorPromise(
288
+ 'build-agent-tools',
289
+ `Failed to build agent tools for "${resolvedAgentId}".`,
290
+ () =>
291
+ Promise.resolve(
292
+ agentFactoryConfig.buildAgentTools({
293
+ agentId: resolvedAgentId,
294
+ orgId: organizationRef,
295
+ userId: userRef,
296
+ userName,
297
+ threadId: threadRef,
298
+ orgIdString: params.context.organizationId,
299
+ threadType: thread.type,
300
+ mode: dispatchMode,
301
+ linearInstalled: Boolean(linearInstallation),
302
+ onboardingActive: false,
303
+ githubInstalled: Boolean(githubInstallation),
304
+ provideRepoTool: indexedRepoContext.provideRepoTool,
305
+ defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[resolvedAgentId],
306
+ memoryBlock: '',
307
+ onAppendMemoryBlock: () => undefined,
308
+ availableUploads: [],
309
+ includeExecutionPlanTools: false,
310
+ }),
311
+ ),
312
+ )
313
+ const tools = applyToolPolicy(rawTools, params.nodeSpec)
330
314
 
331
- const maxSteps = typeof runtimeConfig.maxSteps === 'number' ? runtimeConfig.maxSteps : 8
332
-
333
- if (mode === 'linear') {
334
- const agent = agentFactory({
335
- mode: dispatchMode,
336
- tools,
337
- extraInstructions:
338
- typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
339
- maxRetries: 1,
340
- stopWhen: [stepCountIs(maxSteps)],
341
- })
342
-
343
- const result = yield* tryAgentExecutorPromise(
344
- 'generate-agent-dispatch',
345
- `Agent "${resolvedAgentId}" failed to generate a dispatch result.`,
346
- () => agent.generate({ prompt: buildDispatchPrompt(params.nodeSpec) }),
347
- )
348
- const outputCandidate = PlanNodeResultSubmissionSchema.safeParse(result.output)
349
- if (outputCandidate.success) {
350
- return outputCandidate.data
351
- }
352
-
353
- return yield* toValidationError(
354
- outputCandidate.error,
355
- `Agent executor "${agentId}" returned an invalid node result`,
356
- )
357
- }
315
+ const agentFactory = agentFactoryConfig.createAgent[resolvedAgentId]
316
+ if (!agentFactory) {
317
+ return yield* new BadRequestError({ message: `Agent factory "${resolvedAgentId}" is not registered.` })
318
+ }
358
319
 
359
- const workspace = deps.nodeWorkspaceService.initialize({
360
- nodeSpec: params.nodeSpec,
361
- resolvedInput: params.resolvedInput,
362
- inputArtifacts: params.inputArtifacts,
363
- schemaRegistry: params.schemaRegistry ?? {},
364
- })
320
+ const maxSteps = typeof runtimeConfig.maxSteps === 'number' ? runtimeConfig.maxSteps : 8
365
321
 
366
- const graphTools = {
367
- ...tools,
368
- writeIntent: buildWriteIntentTool({
369
- nodeSpec: params.nodeSpec,
370
- workspace,
371
- writeIntentValidatorService: deps.writeIntentValidatorService,
372
- stageWrite: (intent) => deps.nodeWorkspaceService.stageWrite(workspace, intent, 'validated'),
373
- }),
374
- }
322
+ if (mode === 'linear') {
375
323
  const agent = agentFactory({
376
324
  mode: dispatchMode,
377
- tools: graphTools,
325
+ tools,
378
326
  extraInstructions:
379
327
  typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
380
328
  maxRetries: 1,
381
329
  stopWhen: [stepCountIs(maxSteps)],
382
330
  })
383
331
 
384
- yield* Effect.tryPromise(() => agent.generate({ prompt: buildWriteIntentDispatchPrompt(params.nodeSpec) }))
332
+ const result = yield* tryAgentExecutorPromise(
333
+ 'generate-agent-dispatch',
334
+ `Agent "${resolvedAgentId}" failed to generate a dispatch result.`,
335
+ () => agent.generate({ prompt: buildDispatchPrompt(params.nodeSpec) }),
336
+ ).pipe(Effect.withSpan('AgentExecutor.generateLinearDispatch'))
337
+ const outputCandidate = PlanNodeResultSubmissionSchema.safeParse(result.output)
338
+ if (outputCandidate.success) {
339
+ return outputCandidate.data
340
+ }
341
+
342
+ return yield* toValidationError(
343
+ outputCandidate.error,
344
+ `Agent executor "${agentId}" returned an invalid node result`,
345
+ )
346
+ }
385
347
 
386
- const finalResult = yield* deps.nodeWorkspaceService.finalize(workspace)
348
+ const workspace = deps.nodeWorkspaceService.initialize({
349
+ nodeSpec: params.nodeSpec,
350
+ resolvedInput: params.resolvedInput,
351
+ inputArtifacts: params.inputArtifacts,
352
+ schemaRegistry: params.schemaRegistry ?? {},
353
+ })
387
354
 
388
- if (!finalResult.isComplete) {
389
- const result: PlanNodeResult = {
390
- structuredOutput: finalResult.structuredOutput,
391
- artifacts: finalResult.artifacts,
392
- notes: 'Execution incomplete: missing required deliverables or validation failures.',
393
- }
394
- deps.nodeWorkspaceService.cleanup(workspace)
395
- return result
396
- }
355
+ const graphTools = {
356
+ ...tools,
357
+ writeIntent: buildWriteIntentTool({
358
+ nodeSpec: params.nodeSpec,
359
+ workspace,
360
+ writeIntentValidatorService: deps.writeIntentValidatorService,
361
+ stageWrite: (intent) => deps.nodeWorkspaceService.stageWrite(workspace, intent, 'validated'),
362
+ }),
363
+ }
364
+ const agent = agentFactory({
365
+ mode: dispatchMode,
366
+ tools: graphTools,
367
+ extraInstructions:
368
+ typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
369
+ maxRetries: 1,
370
+ stopWhen: [stepCountIs(maxSteps)],
371
+ })
372
+
373
+ yield* tryAgentExecutorPromise(
374
+ 'generate-write-intent-dispatch',
375
+ `Agent "${resolvedAgentId}" failed to generate a write-intent dispatch result.`,
376
+ () => agent.generate({ prompt: buildWriteIntentDispatchPrompt(params.nodeSpec) }),
377
+ ).pipe(Effect.withSpan('AgentExecutor.generateWriteIntentDispatch'))
397
378
 
379
+ const finalResult = yield* deps.nodeWorkspaceService.finalize(workspace)
380
+
381
+ if (!finalResult.isComplete) {
398
382
  const result: PlanNodeResult = {
399
383
  structuredOutput: finalResult.structuredOutput,
400
384
  artifacts: finalResult.artifacts,
401
- notes: finalResult.notes,
385
+ notes: 'Execution incomplete: missing required deliverables or validation failures.',
402
386
  }
403
-
404
387
  deps.nodeWorkspaceService.cleanup(workspace)
405
388
  return result
406
- })
407
- }
389
+ }
390
+
391
+ const result: PlanNodeResult = {
392
+ structuredOutput: finalResult.structuredOutput,
393
+ artifacts: finalResult.artifacts,
394
+ notes: finalResult.notes,
395
+ }
396
+
397
+ deps.nodeWorkspaceService.cleanup(workspace)
398
+ return result
399
+ })
408
400
  }
409
401
 
410
402
  interface AgentExecutorDeps {
@@ -420,7 +412,7 @@ export function makeAgentExecutorService(deps: AgentExecutorDeps) {
420
412
  export class AgentExecutorServiceTag extends Context.Service<
421
413
  AgentExecutorServiceTag,
422
414
  ReturnType<typeof makeAgentExecutorService>
423
- >()('AgentExecutorService') {}
415
+ >()('@lota-sdk/core/AgentExecutorService') {}
424
416
 
425
417
  export const AgentExecutorServiceLive = Layer.effect(
426
418
  AgentExecutorServiceTag,
@@ -15,7 +15,7 @@ import type { SurrealDBService, DatabaseTransaction } from '../db/service'
15
15
  import { TABLES } from '../db/tables'
16
16
  import { ConfigurationError, DatabaseError, NotFoundError } from '../effect/errors'
17
17
  import { DatabaseServiceTag } from '../effect/services'
18
- import { toValidationError } from '../effect/zod'
18
+ import { toValidationError, zodParse } from '../effect/zod'
19
19
  import type { makeGeneratedDocumentStorageService } from '../storage/generated-document-storage.service'
20
20
  import { GeneratedDocumentStorageServiceTag } from '../storage/generated-document-storage.service'
21
21
  import { sha256Hex } from '../utils/crypto'
@@ -180,7 +180,7 @@ export function makeArtifactService(deps: ArtifactServiceDeps) {
180
180
  },
181
181
  })
182
182
 
183
- return ArtifactRecordSchema.array().parse(records)
183
+ return yield* zodParse(ArtifactRecordSchema.array(), records)
184
184
  })
185
185
 
186
186
  const publishArtifactInTransactionEffect = (
@@ -247,7 +247,7 @@ export function makeArtifactService(deps: ArtifactServiceDeps) {
247
247
  })
248
248
  }
249
249
 
250
- const created = ArtifactRecordSchema.parse(createdRecord)
250
+ const created = yield* zodParse(ArtifactRecordSchema, createdRecord)
251
251
 
252
252
  const previousActive = existing.find((record) => record.status === 'active')
253
253
  if (previousActive) {
@@ -392,7 +392,12 @@ export function makeArtifactService(deps: ArtifactServiceDeps) {
392
392
  listBacklinksEffect(artifact),
393
393
  ])
394
394
 
395
- return GetArtifactResultSchema.parse({ artifact, content, versions: versions.map(toVersionSummary), backlinks })
395
+ return yield* zodParse(GetArtifactResultSchema, {
396
+ artifact,
397
+ content,
398
+ versions: versions.map(toVersionSummary),
399
+ backlinks,
400
+ })
396
401
  })
397
402
 
398
403
  const listBacklinksEffect = (artifact: Pick<ArtifactRecord, 'id' | 'organizationId'>) =>
@@ -439,7 +444,7 @@ export function makeArtifactService(deps: ArtifactServiceDeps) {
439
444
  }
440
445
 
441
446
  export class ArtifactServiceTag extends Context.Service<ArtifactServiceTag, ReturnType<typeof makeArtifactService>>()(
442
- 'ArtifactService',
447
+ '@lota-sdk/core/ArtifactService',
443
448
  ) {}
444
449
 
445
450
  export const ArtifactServiceLive = Layer.effect(
@@ -67,6 +67,10 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
67
67
  return runPromise(attachmentStorage.extractStoredAttachmentText({ storageKey, name, contentType }))
68
68
  },
69
69
 
70
+ extractStoredAttachmentTextEffect(params: { storageKey: string; name: string; contentType: string }) {
71
+ return attachmentStorage.extractStoredAttachmentText(params)
72
+ },
73
+
70
74
  extractStoredAttachmentPages({
71
75
  storageKey,
72
76
  name,
@@ -79,6 +83,10 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
79
83
  return runPromise(attachmentStorage.extractStoredAttachmentPages({ storageKey, name, contentType }))
80
84
  },
81
85
 
86
+ extractStoredAttachmentPagesEffect(params: { storageKey: string; name: string; contentType: string }) {
87
+ return attachmentStorage.extractStoredAttachmentPages(params)
88
+ },
89
+
82
90
  readFilePartsFromUpload({
83
91
  upload,
84
92
  orgId,
@@ -129,6 +137,28 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
129
137
  )
130
138
  },
131
139
 
140
+ writeOrganizationDocumentEffect({
141
+ orgId,
142
+ namespace,
143
+ relativePath,
144
+ content,
145
+ contentType,
146
+ }: {
147
+ orgId: RecordIdRef
148
+ namespace: string
149
+ relativePath: string
150
+ content: string
151
+ contentType: string
152
+ }) {
153
+ return attachmentStorage.writeOrganizationDocument({
154
+ orgId: toOrgId(orgId),
155
+ namespace,
156
+ relativePath,
157
+ content,
158
+ contentType,
159
+ })
160
+ },
161
+
132
162
  uploadOrganizationDocument({
133
163
  file,
134
164
  orgId,
@@ -156,13 +186,17 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
156
186
  attachmentStorage.uploadThreadAttachment({ file, orgId: toOrgId(orgId), userId: toUserId(userId) }),
157
187
  )
158
188
  },
189
+
190
+ uploadThreadAttachmentEffect({ file, orgId, userId }: { file: File; orgId: RecordIdRef; userId: RecordIdRef }) {
191
+ return attachmentStorage.uploadThreadAttachment({ file, orgId: toOrgId(orgId), userId: toUserId(userId) })
192
+ },
159
193
  }
160
194
  }
161
195
 
162
196
  export class AttachmentServiceTag extends Context.Service<
163
197
  AttachmentServiceTag,
164
198
  ReturnType<typeof makeAttachmentService>
165
- >()('AttachmentService') {}
199
+ >()('@lota-sdk/core/AttachmentService') {}
166
200
 
167
201
  export const AttachmentServiceLive = Layer.effect(
168
202
  AttachmentServiceTag,