@lota-sdk/core 0.4.8 → 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 (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -9,12 +9,18 @@ import type {
9
9
  } from '@lota-sdk/shared'
10
10
  import { PlanNodeResultSubmissionSchema, WriteIntentSchema } from '@lota-sdk/shared'
11
11
  import { stepCountIs, tool } from 'ai'
12
- import type { ToolLoopAgent, ToolSet } from 'ai'
12
+ import type { ToolSet } from 'ai'
13
+ import { Context, Schema, Effect, Layer } from 'effect'
13
14
 
14
- import { agentRoster, buildAgentTools, createAgent, getAgentRuntimeConfig } from '../config/agent-defaults'
15
+ import { getAgentRoster, getAgentRuntimeConfig, getResolvedAgentFactoryConfig } from '../config/agent-defaults'
15
16
  import { ensureRecordId } from '../db/record-id'
16
- import { databaseService } from '../db/service'
17
+ import type { SurrealDBService } from '../db/service'
17
18
  import { TABLES } from '../db/tables'
19
+ import { BadRequestError, NotFoundError } from '../effect/errors'
20
+ import { makeEffectTryPromiseWithOperation } from '../effect/helpers'
21
+ import { runPromise } from '../effect/runtime'
22
+ import { DatabaseServiceTag } from '../effect/services'
23
+ import { toValidationError } from '../effect/zod'
18
24
  import {
19
25
  OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES,
20
26
  buildCompletionCheckStructuredOutputHints,
@@ -22,13 +28,24 @@ import {
22
28
  buildOwnershipDispatchResponseGuard,
23
29
  } from '../runtime/agent-runtime-policy'
24
30
  import { mergeInstructionSections } from '../runtime/instruction-sections'
25
- import { buildIndexedRepositoriesContext, getPluginService } from '../runtime/plugin-resolution'
31
+ import {
32
+ buildIndexedRepositoriesContext,
33
+ getGithubInstallationForOrganization,
34
+ getLinearInstallationByOrgId,
35
+ } from '../runtime/plugin-resolution'
26
36
  import { getTurnHooks } from '../runtime/runtime-extensions'
27
- import { asRecord, readInstructionSections, readOptionalString } from '../runtime/thread-chat-helpers'
28
- import { nodeWorkspaceService } from './node-workspace.service'
29
- import type { PlanValidationIssueInput } from './plan-validator.service'
30
- import { ThreadSchema } from './thread.types'
31
- import { writeIntentValidatorService } from './write-intent-validator.service'
37
+ import { readInstructionSections, readOptionalString } from '../runtime/thread-chat-helpers'
38
+ import { nowDate } from '../utils/date-time'
39
+ import type { makeNodeWorkspaceService } from './node-workspace.service'
40
+ import { NodeWorkspaceServiceTag } from './node-workspace.service'
41
+ import type { PlanValidationIssueInput } from './plan/plan-validator.service'
42
+ import { ThreadSchema } from './thread/thread.types'
43
+ import type { makeWriteIntentValidatorService } from './write-intent-validator.service'
44
+ import { WriteIntentValidatorServiceTag } from './write-intent-validator.service'
45
+
46
+ function isRecord(value: unknown): value is Record<string, unknown> {
47
+ return typeof value === 'object' && value !== null
48
+ }
32
49
 
33
50
  function applyToolPolicy(tools: ToolSet, nodeSpec: PlanNodeSpec): ToolSet {
34
51
  const blockedToolNames = new Set([...OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES, ...nodeSpec.toolPolicy.deny])
@@ -74,79 +91,151 @@ export function buildWriteIntentDispatchPrompt(nodeSpec: PlanNodeSpec): string {
74
91
 
75
92
  const MAX_SELF_CORRECTION_RETRIES = 3
76
93
 
77
- class AgentExecutorService {
78
- validateOwner(agentId: string, nodeId: string): PlanValidationIssueInput[] {
79
- if (!agentRoster.includes(agentId)) {
80
- return [
81
- {
82
- severity: 'blocking',
83
- code: 'agent_executor_missing',
84
- message: `Node "${nodeId}" references unknown agent executor "${agentId}".`,
85
- nodeId,
86
- detail: { agentId },
87
- },
88
- ]
89
- }
90
-
91
- return []
94
+ function validateOwner(agentId: string, nodeId: string): PlanValidationIssueInput[] {
95
+ if (!getAgentRoster().includes(agentId)) {
96
+ return [
97
+ {
98
+ severity: 'blocking',
99
+ code: 'agent_executor_missing',
100
+ message: `Node "${nodeId}" references unknown agent executor "${agentId}".`,
101
+ nodeId,
102
+ detail: { agentId },
103
+ },
104
+ ]
92
105
  }
93
106
 
94
- async executeNode(params: {
107
+ return []
108
+ }
109
+
110
+ class AgentExecutorError extends Schema.TaggedErrorClass<AgentExecutorError>()('AgentExecutorError', {
111
+ operation: Schema.String,
112
+ message: Schema.String,
113
+ cause: Schema.Defect,
114
+ }) {}
115
+
116
+ const tryAgentExecutorPromise = makeEffectTryPromiseWithOperation(
117
+ (operation, message, cause) => new AgentExecutorError({ operation, message, cause }),
118
+ )
119
+ const tryAgentExecutorTask = <A>(
120
+ operation: string,
121
+ message: string,
122
+ task: () => PromiseLike<A>,
123
+ ): Effect.Effect<A, AgentExecutorError> =>
124
+ Effect.tryPromise({ try: task, catch: (cause) => new AgentExecutorError({ operation, message, cause }) })
125
+
126
+ function isAgentExecutorOwner(
127
+ owner: PlanNodeSpec['owner'],
128
+ ): owner is Extract<PlanNodeSpec['owner'], { executorType: 'agent' }> {
129
+ return owner.executorType === 'agent'
130
+ }
131
+
132
+ function buildWriteIntentTool(params: {
133
+ nodeSpec: PlanNodeSpec
134
+ workspace: ReturnType<ReturnType<typeof makeNodeWorkspaceService>['initialize']>
135
+ writeIntentValidatorService: ReturnType<typeof makeWriteIntentValidatorService>
136
+ stageWrite: (intent: WriteIntent) => Effect.Effect<void, BadRequestError>
137
+ }) {
138
+ return tool({
139
+ description:
140
+ 'Write a validated artifact or structured output field. Call this for each deliverable. If validation fails, correct your payload and try again.',
141
+ inputSchema: WriteIntentSchema,
142
+ execute: (intent: WriteIntent) =>
143
+ runPromise(
144
+ Effect.gen(function* () {
145
+ const correctionCount = params.workspace.sys.correctionCounts.get(intent.targetPath) ?? 0
146
+
147
+ const validation = params.writeIntentValidatorService.validate({
148
+ intent,
149
+ nodeSpec: params.nodeSpec,
150
+ schemaRegistry: params.workspace.ctx.schemaRegistry,
151
+ existingDeliverables: params.workspace.deliverables,
152
+ })
153
+
154
+ if (validation.issues.length > 0) {
155
+ if (correctionCount >= MAX_SELF_CORRECTION_RETRIES) {
156
+ return yield* new BadRequestError({
157
+ message: `Write validation failed for "${intent.targetPath}" after ${MAX_SELF_CORRECTION_RETRIES} retries: ${validation.issues.map((i) => i.message).join(', ')}`,
158
+ })
159
+ }
160
+ params.workspace.sys.correctionCounts.set(intent.targetPath, correctionCount + 1)
161
+ params.workspace.sys.writeLog.push({
162
+ targetPath: intent.targetPath,
163
+ action: intent.action,
164
+ timestamp: nowDate(),
165
+ result: 'rejected',
166
+ })
167
+ return {
168
+ status: 'validation_failed',
169
+ error: { targetPath: intent.targetPath, action: intent.action, issues: validation.issues },
170
+ hint: `Correct and re-emit. Attempt ${correctionCount + 1}/${MAX_SELF_CORRECTION_RETRIES}.`,
171
+ }
172
+ }
173
+
174
+ yield* params.stageWrite(intent)
175
+ return { status: 'accepted', message: `Write to "${intent.targetPath}" validated and staged.` }
176
+ }),
177
+ ),
178
+ })
179
+ }
180
+
181
+ function createExecuteNode(deps: AgentExecutorDeps) {
182
+ return Effect.fn('AgentExecutor.executeNode')(function* (params: {
95
183
  nodeSpec: PlanNodeSpec
96
184
  resolvedInput: Record<string, unknown>
97
185
  inputArtifacts: PlanArtifactSubmission[]
98
186
  context: OwnershipDispatchContext
99
187
  executionMode?: ExecutionMode
100
188
  schemaRegistry?: PlanSchemaRegistry
101
- }): Promise<PlanNodeResult> {
102
- if (params.nodeSpec.owner.executorType !== 'agent') {
103
- throw new Error(`AgentExecutor cannot execute owner type "${params.nodeSpec.owner.executorType}".`)
189
+ }) {
190
+ const owner = params.nodeSpec.owner
191
+ if (!isAgentExecutorOwner(owner)) {
192
+ return yield* new BadRequestError({ message: `AgentExecutor cannot execute owner type "${owner.executorType}".` })
104
193
  }
105
194
 
106
- const agentId = params.nodeSpec.owner.ref
107
- if (!agentRoster.includes(agentId)) {
108
- throw new Error(`Agent executor "${agentId}" is not registered.`)
195
+ const agentId = owner.ref
196
+ if (!getAgentRoster().includes(agentId)) {
197
+ return yield* new BadRequestError({ message: `Agent executor "${agentId}" is not registered.` })
109
198
  }
110
199
 
111
- const thread = await databaseService.findOne(
112
- TABLES.THREAD,
113
- { id: ensureRecordId(params.context.threadId, TABLES.THREAD) },
114
- ThreadSchema,
115
- )
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'))
116
203
  if (!thread) {
117
- throw new Error(`Thread ${params.context.threadId} not found for dispatched execution.`)
204
+ return yield* new NotFoundError({
205
+ resource: TABLES.THREAD,
206
+ message: `Thread ${params.context.threadId} not found for dispatched execution.`,
207
+ })
118
208
  }
119
209
 
120
210
  const organizationRef = ensureRecordId(params.context.organizationId, TABLES.ORGANIZATION)
121
211
  const threadRef = ensureRecordId(params.context.threadId, TABLES.THREAD)
122
212
  const userRefSource = params.context.userId ?? thread.userId
123
213
  if (!userRefSource) {
124
- throw new Error(`Thread ${params.context.threadId} is missing a user context for dispatched execution.`)
214
+ return yield* new BadRequestError({
215
+ message: `Thread ${params.context.threadId} is missing a user context for dispatched execution.`,
216
+ })
125
217
  }
126
218
  const userRef = ensureRecordId(userRefSource, TABLES.USER)
127
219
  const userName = params.context.userName ?? 'User'
128
- const getLinearInstallationByOrgId = getPluginService([
129
- 'linear',
130
- 'services',
131
- 'linearService',
132
- 'getInstallationByOrgId',
133
- ])
134
- const getGithubInstallationForOrganization = getPluginService([
135
- 'github',
136
- 'services',
137
- 'githubService',
138
- 'getInstallationForOrganization',
139
- ])
140
- const [linearInstallation, githubInstallation, indexedRepoContext] = await Promise.all([
141
- getLinearInstallationByOrgId ? getLinearInstallationByOrgId(organizationRef) : Promise.resolve(null),
142
- getGithubInstallationForOrganization
143
- ? getGithubInstallationForOrganization(params.context.organizationId)
144
- : Promise.resolve(null),
145
- buildIndexedRepositoriesContext(params.context.organizationId),
146
- ])
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'))
147
237
 
148
238
  const mode = params.executionMode ?? 'linear'
149
-
150
239
  const dispatchMode = thread.type === 'group' ? 'fixedThreadMode' : 'direct'
151
240
  const dispatchInstructionSections = [
152
241
  buildOwnershipDispatchContextSection({
@@ -156,23 +245,30 @@ class AgentExecutorService {
156
245
  upstreamHandoffs: params.context.upstreamHandoffs,
157
246
  }),
158
247
  ]
248
+
159
249
  const turnHooks = getTurnHooks()
160
- const agentResolution = asRecord(
161
- await turnHooks.resolveAgent?.({
162
- agentId,
163
- mode: dispatchMode,
164
- thread,
165
- threadRef,
166
- orgRef: organizationRef,
167
- userRef,
168
- userName,
169
- onboardingActive: false,
170
- linearInstalled: Boolean(linearInstallation),
171
- githubInstalled: Boolean(githubInstallation),
172
- additionalInstructionSections: dispatchInstructionSections,
173
- context: null,
174
- }),
250
+ const agentResolutionValue = yield* tryAgentExecutorPromise(
251
+ 'resolve-agent',
252
+ `Failed to resolve agent "${agentId}" for dispatched execution.`,
253
+ () =>
254
+ Promise.resolve(
255
+ turnHooks.resolveAgent?.({
256
+ agentId,
257
+ mode: dispatchMode,
258
+ thread,
259
+ threadRef,
260
+ orgRef: organizationRef,
261
+ userRef,
262
+ userName,
263
+ onboardingActive: false,
264
+ linearInstalled: Boolean(linearInstallation),
265
+ githubInstalled: Boolean(githubInstallation),
266
+ additionalInstructionSections: dispatchInstructionSections,
267
+ context: null,
268
+ }),
269
+ ),
175
270
  )
271
+ const agentResolution = isRecord(agentResolutionValue) ? agentResolutionValue : null
176
272
  const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? agentId
177
273
  const runtimeConfig = getAgentRuntimeConfig({
178
274
  agentId: resolvedAgentId,
@@ -185,32 +281,40 @@ class AgentExecutorService {
185
281
  readInstructionSections(agentResolution?.additionalInstructionSections),
186
282
  ),
187
283
  responseGuardSection: buildOwnershipDispatchResponseGuard({ node: params.nodeSpec, executionMode: mode }),
188
- }) as Record<string, unknown>
284
+ })
189
285
 
190
- const rawTools = (await buildAgentTools({
191
- agentId: resolvedAgentId,
192
- orgId: organizationRef,
193
- userId: userRef,
194
- userName,
195
- threadId: threadRef,
196
- orgIdString: params.context.organizationId,
197
- threadType: thread.type,
198
- mode: dispatchMode,
199
- linearInstalled: Boolean(linearInstallation),
200
- onboardingActive: false,
201
- githubInstalled: Boolean(githubInstallation),
202
- provideRepoTool: indexedRepoContext.provideRepoTool,
203
- defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[resolvedAgentId],
204
- memoryBlock: '',
205
- onAppendMemoryBlock: () => undefined,
206
- availableUploads: [],
207
- includeExecutionPlanTools: false,
208
- })) as ToolSet
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
+ )
209
313
  const tools = applyToolPolicy(rawTools, params.nodeSpec)
210
314
 
211
- const agentFactory = createAgent[resolvedAgentId] as ((...args: unknown[]) => unknown) | undefined
315
+ const agentFactory = agentFactoryConfig.createAgent[resolvedAgentId]
212
316
  if (!agentFactory) {
213
- throw new Error(`Agent factory "${resolvedAgentId}" is not registered.`)
317
+ return yield* new BadRequestError({ message: `Agent factory "${resolvedAgentId}" is not registered.` })
214
318
  }
215
319
 
216
320
  const maxSteps = typeof runtimeConfig.maxSteps === 'number' ? runtimeConfig.maxSteps : 8
@@ -223,67 +327,40 @@ class AgentExecutorService {
223
327
  typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
224
328
  maxRetries: 1,
225
329
  stopWhen: [stepCountIs(maxSteps)],
226
- }) as ToolLoopAgent<never, ToolSet>
330
+ })
227
331
 
228
- const result = await agent.generate({ prompt: buildDispatchPrompt(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'))
229
337
  const outputCandidate = PlanNodeResultSubmissionSchema.safeParse(result.output)
230
338
  if (outputCandidate.success) {
231
339
  return outputCandidate.data
232
340
  }
233
341
 
234
- throw new Error(`Agent executor "${agentId}" returned an invalid node result.`)
342
+ return yield* toValidationError(
343
+ outputCandidate.error,
344
+ `Agent executor "${agentId}" returned an invalid node result`,
345
+ )
235
346
  }
236
347
 
237
- // --- Graph-lite / Graph-full path ---
238
-
239
- const workspace = nodeWorkspaceService.initialize({
348
+ const workspace = deps.nodeWorkspaceService.initialize({
240
349
  nodeSpec: params.nodeSpec,
241
350
  resolvedInput: params.resolvedInput,
242
351
  inputArtifacts: params.inputArtifacts,
243
352
  schemaRegistry: params.schemaRegistry ?? {},
244
353
  })
245
354
 
246
- const writeIntentTool = tool({
247
- description:
248
- 'Write a validated artifact or structured output field. Call this for each deliverable. If validation fails, correct your payload and try again.',
249
- inputSchema: WriteIntentSchema,
250
- execute: async (intent: WriteIntent) => {
251
- const correctionCount = workspace.sys.correctionCounts.get(intent.targetPath) ?? 0
252
-
253
- const validation = writeIntentValidatorService.validate({
254
- intent,
255
- nodeSpec: params.nodeSpec,
256
- schemaRegistry: workspace.ctx.schemaRegistry,
257
- existingDeliverables: workspace.deliverables,
258
- })
259
-
260
- if (validation.issues.length > 0) {
261
- if (correctionCount >= MAX_SELF_CORRECTION_RETRIES) {
262
- throw new Error(
263
- `Write validation failed for "${intent.targetPath}" after ${MAX_SELF_CORRECTION_RETRIES} retries: ${validation.issues.map((i) => i.message).join(', ')}`,
264
- )
265
- }
266
- workspace.sys.correctionCounts.set(intent.targetPath, correctionCount + 1)
267
- workspace.sys.writeLog.push({
268
- targetPath: intent.targetPath,
269
- action: intent.action,
270
- timestamp: new Date(),
271
- result: 'rejected',
272
- })
273
- return {
274
- status: 'validation_failed',
275
- error: { targetPath: intent.targetPath, action: intent.action, issues: validation.issues },
276
- hint: `Correct and re-emit. Attempt ${correctionCount + 1}/${MAX_SELF_CORRECTION_RETRIES}.`,
277
- }
278
- }
279
-
280
- nodeWorkspaceService.stageWrite(workspace, intent, 'validated')
281
- return { status: 'accepted', message: `Write to "${intent.targetPath}" validated and staged.` }
282
- },
283
- })
284
-
285
- const graphTools = { ...tools, writeIntent: writeIntentTool }
286
-
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
+ }
287
364
  const agent = agentFactory({
288
365
  mode: dispatchMode,
289
366
  tools: graphTools,
@@ -291,11 +368,15 @@ class AgentExecutorService {
291
368
  typeof runtimeConfig.extraInstructions === 'string' ? runtimeConfig.extraInstructions : undefined,
292
369
  maxRetries: 1,
293
370
  stopWhen: [stepCountIs(maxSteps)],
294
- }) as ToolLoopAgent<never, ToolSet>
371
+ })
295
372
 
296
- await agent.generate({ prompt: buildWriteIntentDispatchPrompt(params.nodeSpec) })
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'))
297
378
 
298
- const finalResult = nodeWorkspaceService.finalize(workspace)
379
+ const finalResult = yield* deps.nodeWorkspaceService.finalize(workspace)
299
380
 
300
381
  if (!finalResult.isComplete) {
301
382
  const result: PlanNodeResult = {
@@ -303,7 +384,7 @@ class AgentExecutorService {
303
384
  artifacts: finalResult.artifacts,
304
385
  notes: 'Execution incomplete: missing required deliverables or validation failures.',
305
386
  }
306
- nodeWorkspaceService.cleanup(workspace)
387
+ deps.nodeWorkspaceService.cleanup(workspace)
307
388
  return result
308
389
  }
309
390
 
@@ -313,9 +394,32 @@ class AgentExecutorService {
313
394
  notes: finalResult.notes,
314
395
  }
315
396
 
316
- nodeWorkspaceService.cleanup(workspace)
397
+ deps.nodeWorkspaceService.cleanup(workspace)
317
398
  return result
318
- }
399
+ })
400
+ }
401
+
402
+ interface AgentExecutorDeps {
403
+ db: SurrealDBService
404
+ nodeWorkspaceService: ReturnType<typeof makeNodeWorkspaceService>
405
+ writeIntentValidatorService: ReturnType<typeof makeWriteIntentValidatorService>
406
+ }
407
+
408
+ export function makeAgentExecutorService(deps: AgentExecutorDeps) {
409
+ return { validateOwner, executeNode: createExecuteNode(deps) }
319
410
  }
320
411
 
321
- export const agentExecutorService = new AgentExecutorService()
412
+ export class AgentExecutorServiceTag extends Context.Service<
413
+ AgentExecutorServiceTag,
414
+ ReturnType<typeof makeAgentExecutorService>
415
+ >()('@lota-sdk/core/AgentExecutorService') {}
416
+
417
+ export const AgentExecutorServiceLive = Layer.effect(
418
+ AgentExecutorServiceTag,
419
+ Effect.gen(function* () {
420
+ const db = yield* DatabaseServiceTag
421
+ const nodeWorkspaceService = yield* NodeWorkspaceServiceTag
422
+ const writeIntentValidatorService = yield* WriteIntentValidatorServiceTag
423
+ return makeAgentExecutorService({ db, nodeWorkspaceService, writeIntentValidatorService })
424
+ }),
425
+ )