@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
@@ -12,13 +12,19 @@ import { stepCountIs, tool } from 'ai'
12
12
  import type { ToolSet } from 'ai'
13
13
  import { Context, Schema, Effect, Layer } from 'effect'
14
14
 
15
- import { getAgentRoster, getAgentRuntimeConfig, getResolvedAgentFactoryConfig } from '../config/agent-defaults'
15
+ import type { ResolvedAgentConfig, ResolvedAgentFactoryConfig } from '../config/agent-defaults'
16
16
  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 { runPromise } from '../effect/runtime'
21
- import { DatabaseServiceTag } from '../effect/services'
20
+ import { makeEffectTryPromiseWithOperation } from '../effect/helpers'
21
+ import {
22
+ AgentConfigServiceTag,
23
+ AgentFactoryServiceTag,
24
+ DatabaseServiceTag,
25
+ RuntimeAdaptersServiceTag,
26
+ TurnHooksServiceTag,
27
+ } from '../effect/services'
22
28
  import { toValidationError } from '../effect/zod'
23
29
  import {
24
30
  OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES,
@@ -32,7 +38,6 @@ import {
32
38
  getGithubInstallationForOrganization,
33
39
  getLinearInstallationByOrgId,
34
40
  } from '../runtime/plugin-resolution'
35
- import { getTurnHooks } from '../runtime/runtime-extensions'
36
41
  import { readInstructionSections, readOptionalString } from '../runtime/thread-chat-helpers'
37
42
  import { nowDate } from '../utils/date-time'
38
43
  import type { makeNodeWorkspaceService } from './node-workspace.service'
@@ -90,8 +95,8 @@ export function buildWriteIntentDispatchPrompt(nodeSpec: PlanNodeSpec): string {
90
95
 
91
96
  const MAX_SELF_CORRECTION_RETRIES = 3
92
97
 
93
- function validateOwner(agentId: string, nodeId: string): PlanValidationIssueInput[] {
94
- if (!getAgentRoster().includes(agentId)) {
98
+ function validateOwner(agentConfig: ResolvedAgentConfig, agentId: string, nodeId: string): PlanValidationIssueInput[] {
99
+ if (!agentConfig.roster.includes(agentId)) {
95
100
  return [
96
101
  {
97
102
  severity: 'blocking',
@@ -112,27 +117,15 @@ class AgentExecutorError extends Schema.TaggedErrorClass<AgentExecutorError>()('
112
117
  cause: Schema.Defect,
113
118
  }) {}
114
119
 
115
- function tryAgentExecutorPromise<A>(
120
+ const tryAgentExecutorPromise = makeEffectTryPromiseWithOperation(
121
+ (operation, message, cause) => new AgentExecutorError({ operation, message, cause }),
122
+ )
123
+ const tryAgentExecutorTask = <A>(
116
124
  operation: string,
117
125
  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
- }
126
+ task: () => PromiseLike<A>,
127
+ ): Effect.Effect<A, AgentExecutorError> =>
128
+ Effect.tryPromise({ try: task, catch: (cause) => new AgentExecutorError({ operation, message, cause }) })
136
129
 
137
130
  function isAgentExecutorOwner(
138
131
  owner: PlanNodeSpec['owner'],
@@ -151,7 +144,7 @@ function buildWriteIntentTool(params: {
151
144
  'Write a validated artifact or structured output field. Call this for each deliverable. If validation fails, correct your payload and try again.',
152
145
  inputSchema: WriteIntentSchema,
153
146
  execute: (intent: WriteIntent) =>
154
- runPromise(
147
+ Effect.runPromise(
155
148
  Effect.gen(function* () {
156
149
  const correctionCount = params.workspace.sys.correctionCounts.get(intent.targetPath) ?? 0
157
150
 
@@ -190,7 +183,7 @@ function buildWriteIntentTool(params: {
190
183
  }
191
184
 
192
185
  function createExecuteNode(deps: AgentExecutorDeps) {
193
- return function executeNode(params: {
186
+ return Effect.fn('AgentExecutor.executeNode')(function* (params: {
194
187
  nodeSpec: PlanNodeSpec
195
188
  resolvedInput: Record<string, unknown>
196
189
  inputArtifacts: PlanArtifactSubmission[]
@@ -198,70 +191,72 @@ function createExecuteNode(deps: AgentExecutorDeps) {
198
191
  executionMode?: ExecutionMode
199
192
  schemaRegistry?: PlanSchemaRegistry
200
193
  }) {
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
- }
194
+ const owner = params.nodeSpec.owner
195
+ if (!isAgentExecutorOwner(owner)) {
196
+ return yield* new BadRequestError({ message: `AgentExecutor cannot execute owner type "${owner.executorType}".` })
197
+ }
208
198
 
209
- const agentId = owner.ref
210
- if (!getAgentRoster().includes(agentId)) {
211
- return yield* new BadRequestError({ message: `Agent executor "${agentId}" is not registered.` })
212
- }
199
+ const agentId = owner.ref
200
+ if (!deps.agentConfig.roster.includes(agentId)) {
201
+ return yield* new BadRequestError({ message: `Agent executor "${agentId}" is not registered.` })
202
+ }
213
203
 
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
- }
204
+ const thread = yield* tryAgentExecutorPromise('load-thread', 'Failed to load dispatched thread.', () =>
205
+ deps.db.findOne(TABLES.THREAD, { id: ensureRecordId(params.context.threadId, TABLES.THREAD) }, ThreadSchema),
206
+ ).pipe(Effect.withSpan('AgentExecutor.loadThread'))
207
+ if (!thread) {
208
+ return yield* new NotFoundError({
209
+ resource: TABLES.THREAD,
210
+ message: `Thread ${params.context.threadId} not found for dispatched execution.`,
211
+ })
212
+ }
223
213
 
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
- ]
214
+ const organizationRef = ensureRecordId(params.context.organizationId, TABLES.ORGANIZATION)
215
+ const threadRef = ensureRecordId(params.context.threadId, TABLES.THREAD)
216
+ const userRefSource = params.context.userId ?? thread.userId
217
+ if (!userRefSource) {
218
+ return yield* new BadRequestError({
219
+ message: `Thread ${params.context.threadId} is missing a user context for dispatched execution.`,
220
+ })
221
+ }
222
+ const userRef = ensureRecordId(userRefSource, TABLES.USER)
223
+ const userName = params.context.userName ?? 'User'
224
+ const runtimeAdapters = yield* RuntimeAdaptersServiceTag
225
+ const { linearInstallation, githubInstallation, indexedRepoContext } = yield* Effect.all({
226
+ linearInstallation: tryAgentExecutorTask(
227
+ 'get-linear-installation',
228
+ `Failed to load Linear installation for org ${params.context.organizationId}.`,
229
+ () => getLinearInstallationByOrgId(deps.agentFactoryConfig.pluginRuntime, organizationRef),
230
+ ),
231
+ githubInstallation: tryAgentExecutorTask(
232
+ 'get-github-installation',
233
+ `Failed to load GitHub installation for org ${params.context.organizationId}.`,
234
+ () =>
235
+ getGithubInstallationForOrganization(deps.agentFactoryConfig.pluginRuntime, params.context.organizationId),
236
+ ),
237
+ indexedRepoContext: tryAgentExecutorTask(
238
+ 'build-indexed-repositories-context',
239
+ `Failed to build indexed repository context for org ${params.context.organizationId}.`,
240
+ () => buildIndexedRepositoriesContext(runtimeAdapters, params.context.organizationId),
241
+ ),
242
+ }).pipe(Effect.withSpan('AgentExecutor.loadInstallations'))
262
243
 
263
- const turnHooks = getTurnHooks()
264
- const agentResolutionValue = yield* Effect.tryPromise(() =>
244
+ const mode = params.executionMode ?? 'linear'
245
+ const dispatchMode = thread.type === 'group' ? 'fixedThreadMode' : 'direct'
246
+ const dispatchInstructionSections = [
247
+ buildOwnershipDispatchContextSection({
248
+ node: params.nodeSpec,
249
+ resolvedInput: params.resolvedInput,
250
+ inputArtifacts: params.inputArtifacts,
251
+ upstreamHandoffs: params.context.upstreamHandoffs,
252
+ }),
253
+ ]
254
+
255
+ const turnHooks = yield* TurnHooksServiceTag
256
+ const agentResolutionValue = yield* tryAgentExecutorPromise(
257
+ 'resolve-agent',
258
+ `Failed to resolve agent "${agentId}" for dispatched execution.`,
259
+ () =>
265
260
  Promise.resolve(
266
261
  turnHooks.resolveAgent?.({
267
262
  agentId,
@@ -278,156 +273,172 @@ function createExecuteNode(deps: AgentExecutorDeps) {
278
273
  context: null,
279
274
  }),
280
275
  ),
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)
276
+ )
277
+ const agentResolution = isRecord(agentResolutionValue) ? agentResolutionValue : null
278
+ const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? agentId
279
+ const runtimeConfig = deps.agentFactoryConfig.getAgentRuntimeConfig({
280
+ agentId: resolvedAgentId,
281
+ threadType: thread.type,
282
+ mode: dispatchMode,
283
+ onboardingActive: false,
284
+ linearInstalled: Boolean(linearInstallation),
285
+ additionalInstructionSections: mergeInstructionSections(
286
+ dispatchInstructionSections,
287
+ readInstructionSections(agentResolution?.additionalInstructionSections),
288
+ ),
289
+ responseGuardSection: buildOwnershipDispatchResponseGuard({ node: params.nodeSpec, executionMode: mode }),
290
+ })
325
291
 
326
- const agentFactory = agentFactoryConfig.createAgent[resolvedAgentId]
327
- if (!agentFactory) {
328
- return yield* new BadRequestError({ message: `Agent factory "${resolvedAgentId}" is not registered.` })
329
- }
292
+ const agentFactoryConfig = deps.agentFactoryConfig
293
+ const rawTools = yield* tryAgentExecutorPromise(
294
+ 'build-agent-tools',
295
+ `Failed to build agent tools for "${resolvedAgentId}".`,
296
+ () =>
297
+ Promise.resolve(
298
+ agentFactoryConfig.buildAgentTools({
299
+ agentId: resolvedAgentId,
300
+ orgId: organizationRef,
301
+ userId: userRef,
302
+ userName,
303
+ threadId: threadRef,
304
+ orgIdString: params.context.organizationId,
305
+ threadType: thread.type,
306
+ mode: dispatchMode,
307
+ linearInstalled: Boolean(linearInstallation),
308
+ onboardingActive: false,
309
+ githubInstalled: Boolean(githubInstallation),
310
+ provideRepoTool: indexedRepoContext.provideRepoTool,
311
+ defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[resolvedAgentId],
312
+ memoryBlock: '',
313
+ onAppendMemoryBlock: () => undefined,
314
+ availableUploads: [],
315
+ includeExecutionPlanTools: false,
316
+ }),
317
+ ),
318
+ )
319
+ const tools = applyToolPolicy(rawTools, params.nodeSpec)
330
320
 
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
- }
321
+ const agentFactory = agentFactoryConfig.createAgent[resolvedAgentId]
322
+ if (!agentFactory) {
323
+ return yield* new BadRequestError({ message: `Agent factory "${resolvedAgentId}" is not registered.` })
324
+ }
358
325
 
359
- const workspace = deps.nodeWorkspaceService.initialize({
360
- nodeSpec: params.nodeSpec,
361
- resolvedInput: params.resolvedInput,
362
- inputArtifacts: params.inputArtifacts,
363
- schemaRegistry: params.schemaRegistry ?? {},
364
- })
326
+ const maxSteps = typeof runtimeConfig.maxSteps === 'number' ? runtimeConfig.maxSteps : 8
365
327
 
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
- }
328
+ if (mode === 'linear') {
375
329
  const agent = agentFactory({
376
330
  mode: dispatchMode,
377
- tools: graphTools,
331
+ tools,
378
332
  extraInstructions:
379
333
  typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
380
334
  maxRetries: 1,
381
335
  stopWhen: [stepCountIs(maxSteps)],
382
336
  })
383
337
 
384
- yield* Effect.tryPromise(() => agent.generate({ prompt: buildWriteIntentDispatchPrompt(params.nodeSpec) }))
338
+ const result = yield* tryAgentExecutorPromise(
339
+ 'generate-agent-dispatch',
340
+ `Agent "${resolvedAgentId}" failed to generate a dispatch result.`,
341
+ () => agent.generate({ prompt: buildDispatchPrompt(params.nodeSpec) }),
342
+ ).pipe(Effect.withSpan('AgentExecutor.generateLinearDispatch'))
343
+ const outputCandidate = PlanNodeResultSubmissionSchema.safeParse(result.output)
344
+ if (outputCandidate.success) {
345
+ return outputCandidate.data
346
+ }
385
347
 
386
- const finalResult = yield* deps.nodeWorkspaceService.finalize(workspace)
348
+ return yield* toValidationError(
349
+ outputCandidate.error,
350
+ `Agent executor "${agentId}" returned an invalid node result`,
351
+ )
352
+ }
387
353
 
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
- }
354
+ const workspace = deps.nodeWorkspaceService.initialize({
355
+ nodeSpec: params.nodeSpec,
356
+ resolvedInput: params.resolvedInput,
357
+ inputArtifacts: params.inputArtifacts,
358
+ schemaRegistry: params.schemaRegistry ?? {},
359
+ })
360
+
361
+ const graphTools = {
362
+ ...tools,
363
+ writeIntent: buildWriteIntentTool({
364
+ nodeSpec: params.nodeSpec,
365
+ workspace,
366
+ writeIntentValidatorService: deps.writeIntentValidatorService,
367
+ stageWrite: (intent) => deps.nodeWorkspaceService.stageWrite(workspace, intent, 'validated'),
368
+ }),
369
+ }
370
+ const agent = agentFactory({
371
+ mode: dispatchMode,
372
+ tools: graphTools,
373
+ extraInstructions:
374
+ typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
375
+ maxRetries: 1,
376
+ stopWhen: [stepCountIs(maxSteps)],
377
+ })
378
+
379
+ yield* tryAgentExecutorPromise(
380
+ 'generate-write-intent-dispatch',
381
+ `Agent "${resolvedAgentId}" failed to generate a write-intent dispatch result.`,
382
+ () => agent.generate({ prompt: buildWriteIntentDispatchPrompt(params.nodeSpec) }),
383
+ ).pipe(Effect.withSpan('AgentExecutor.generateWriteIntentDispatch'))
397
384
 
385
+ const finalResult = yield* deps.nodeWorkspaceService.finalize(workspace)
386
+
387
+ if (!finalResult.isComplete) {
398
388
  const result: PlanNodeResult = {
399
389
  structuredOutput: finalResult.structuredOutput,
400
390
  artifacts: finalResult.artifacts,
401
- notes: finalResult.notes,
391
+ notes: 'Execution incomplete: missing required deliverables or validation failures.',
402
392
  }
403
-
404
393
  deps.nodeWorkspaceService.cleanup(workspace)
405
394
  return result
406
- })
407
- }
395
+ }
396
+
397
+ const result: PlanNodeResult = {
398
+ structuredOutput: finalResult.structuredOutput,
399
+ artifacts: finalResult.artifacts,
400
+ notes: finalResult.notes,
401
+ }
402
+
403
+ deps.nodeWorkspaceService.cleanup(workspace)
404
+ return result
405
+ })
408
406
  }
409
407
 
410
408
  interface AgentExecutorDeps {
409
+ agentConfig: ResolvedAgentConfig
410
+ agentFactoryConfig: ResolvedAgentFactoryConfig
411
411
  db: SurrealDBService
412
412
  nodeWorkspaceService: ReturnType<typeof makeNodeWorkspaceService>
413
413
  writeIntentValidatorService: ReturnType<typeof makeWriteIntentValidatorService>
414
414
  }
415
415
 
416
416
  export function makeAgentExecutorService(deps: AgentExecutorDeps) {
417
- return { validateOwner, executeNode: createExecuteNode(deps) }
417
+ return {
418
+ validateOwner: (agentId: string, nodeId: string) => validateOwner(deps.agentConfig, agentId, nodeId),
419
+ executeNode: createExecuteNode(deps),
420
+ }
418
421
  }
419
422
 
420
423
  export class AgentExecutorServiceTag extends Context.Service<
421
424
  AgentExecutorServiceTag,
422
425
  ReturnType<typeof makeAgentExecutorService>
423
- >()('AgentExecutorService') {}
426
+ >()('@lota-sdk/core/AgentExecutorService') {}
424
427
 
425
428
  export const AgentExecutorServiceLive = Layer.effect(
426
429
  AgentExecutorServiceTag,
427
430
  Effect.gen(function* () {
431
+ const agentConfig = yield* AgentConfigServiceTag
432
+ const agentFactoryConfig = yield* AgentFactoryServiceTag
428
433
  const db = yield* DatabaseServiceTag
429
434
  const nodeWorkspaceService = yield* NodeWorkspaceServiceTag
430
435
  const writeIntentValidatorService = yield* WriteIntentValidatorServiceTag
431
- return makeAgentExecutorService({ db, nodeWorkspaceService, writeIntentValidatorService })
436
+ return makeAgentExecutorService({
437
+ agentConfig,
438
+ agentFactoryConfig,
439
+ db,
440
+ nodeWorkspaceService,
441
+ writeIntentValidatorService,
442
+ })
432
443
  }),
433
444
  )
@@ -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(
@@ -3,11 +3,7 @@ import { Context, Effect, Layer } from 'effect'
3
3
  import type { RecordIdRef } from '../db/record-id'
4
4
  import { recordIdToString } from '../db/record-id'
5
5
  import { TABLES } from '../db/tables'
6
- import { runPromise } from '../effect/runtime'
7
- import type {
8
- makeAttachmentStorageService,
9
- UploadedThreadAttachment as SdkUploadedThreadAttachment,
10
- } from '../storage/attachment-storage.service'
6
+ import type { makeAttachmentStorageService } from '../storage/attachment-storage.service'
11
7
  import { AttachmentStorageServiceTag } from '../storage/attachment-storage.service'
12
8
  import type { MessagePartLike, ReadableUploadMetadata as SdkReadableUploadMetadata } from '../storage/attachment-types'
13
9
 
@@ -55,28 +51,12 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
55
51
  })
56
52
  },
57
53
 
58
- extractStoredAttachmentText({
59
- storageKey,
60
- name,
61
- contentType,
62
- }: {
63
- storageKey: string
64
- name: string
65
- contentType: string
66
- }): Promise<string> {
67
- return runPromise(attachmentStorage.extractStoredAttachmentText({ storageKey, name, contentType }))
54
+ extractStoredAttachmentText(params: { storageKey: string; name: string; contentType: string }) {
55
+ return attachmentStorage.extractStoredAttachmentText(params)
68
56
  },
69
57
 
70
- extractStoredAttachmentPages({
71
- storageKey,
72
- name,
73
- contentType,
74
- }: {
75
- storageKey: string
76
- name: string
77
- contentType: string
78
- }): Promise<{ pageMode: 'logical' | 'pdf'; pages: string[] }> {
79
- return runPromise(attachmentStorage.extractStoredAttachmentPages({ storageKey, name, contentType }))
58
+ extractStoredAttachmentPages(params: { storageKey: string; name: string; contentType: string }) {
59
+ return attachmentStorage.extractStoredAttachmentPages(params)
80
60
  },
81
61
 
82
62
  readFilePartsFromUpload({
@@ -117,16 +97,14 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
117
97
  relativePath: string
118
98
  content: string
119
99
  contentType: string
120
- }): Promise<{ storageKey: string; sizeBytes: number }> {
121
- return runPromise(
122
- attachmentStorage.writeOrganizationDocument({
123
- orgId: toOrgId(orgId),
124
- namespace,
125
- relativePath,
126
- content,
127
- contentType,
128
- }),
129
- )
100
+ }) {
101
+ return attachmentStorage.writeOrganizationDocument({
102
+ orgId: toOrgId(orgId),
103
+ namespace,
104
+ relativePath,
105
+ content,
106
+ contentType,
107
+ })
130
108
  },
131
109
 
132
110
  uploadOrganizationDocument({
@@ -143,18 +121,8 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
143
121
  return attachmentStorage.uploadOrganizationDocument({ file, orgId: toOrgId(orgId), namespace, relativePath })
144
122
  },
145
123
 
146
- uploadThreadAttachment({
147
- file,
148
- orgId,
149
- userId,
150
- }: {
151
- file: File
152
- orgId: RecordIdRef
153
- userId: RecordIdRef
154
- }): Promise<SdkUploadedThreadAttachment> {
155
- return runPromise(
156
- attachmentStorage.uploadThreadAttachment({ file, orgId: toOrgId(orgId), userId: toUserId(userId) }),
157
- )
124
+ uploadThreadAttachment({ file, orgId, userId }: { file: File; orgId: RecordIdRef; userId: RecordIdRef }) {
125
+ return attachmentStorage.uploadThreadAttachment({ file, orgId: toOrgId(orgId), userId: toUserId(userId) })
158
126
  },
159
127
  }
160
128
  }
@@ -162,7 +130,7 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
162
130
  export class AttachmentServiceTag extends Context.Service<
163
131
  AttachmentServiceTag,
164
132
  ReturnType<typeof makeAttachmentService>
165
- >()('AttachmentService') {}
133
+ >()('@lota-sdk/core/AttachmentService') {}
166
134
 
167
135
  export const AttachmentServiceLive = Layer.effect(
168
136
  AttachmentServiceTag,