@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
@@ -8,22 +8,34 @@ import {
8
8
  } from '@lota-sdk/shared'
9
9
  import { Effect } from 'effect'
10
10
  import { RecordId } from 'surrealdb'
11
+ import type { z } from 'zod'
11
12
 
12
13
  import type { RecordIdInput } from '../../db/record-id'
13
14
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
14
15
  import type { DatabaseTransaction } from '../../db/service'
15
16
  import { TABLES } from '../../db/tables'
16
- import { runPromise } from '../../effect/runtime'
17
+ import { ServiceError } from '../../effect/errors'
17
18
  import { nowDate } from '../../utils/date-time'
18
19
  import type { CompiledPlanNode } from '../plan/plan-compiler.service'
19
20
  import type { syncRunGraph } from '../plan/plan-executor-graph'
20
21
  import { toRunData } from '../plan/plan-run-data'
21
22
 
23
+ function parseRowOrFail<TSchema extends z.ZodTypeAny>(
24
+ schema: TSchema,
25
+ value: unknown,
26
+ operation: string,
27
+ ): Effect.Effect<z.infer<TSchema>, ServiceError> {
28
+ return Effect.try({
29
+ try: () => schema.parse(value) as z.infer<TSchema>,
30
+ catch: (cause) => new ServiceError({ message: `Failed to parse row for ${operation}.`, cause }),
31
+ })
32
+ }
33
+
22
34
  type SyncRunGraphParams = Parameters<typeof syncRunGraph>[1]
23
- type SyncRunGraphResult = Awaited<ReturnType<typeof syncRunGraph>>
35
+ type SyncRunGraphResult = ReturnType<typeof syncRunGraph>
24
36
 
25
37
  interface ExecutionPlanGraphExecutor {
26
- syncRunGraph(params: SyncRunGraphParams): Promise<SyncRunGraphResult>
38
+ syncRunGraph(params: SyncRunGraphParams): SyncRunGraphResult
27
39
  }
28
40
 
29
41
  function buildNodeSpecCreateContent(compiledNode: CompiledPlanNode, planSpecId: RecordIdInput) {
@@ -86,7 +98,7 @@ function createNodeSpecs(tx: DatabaseTransaction, planSpecId: RecordIdInput, nod
86
98
  .create(nodeSpecId)
87
99
  .content(buildNodeSpecCreateContent(compiledNode, planSpecId))
88
100
  .output('after')
89
- createdRecords.push(PlanNodeSpecRecordSchema.parse(created))
101
+ createdRecords.push(yield* parseRowOrFail(PlanNodeSpecRecordSchema, created, 'createNodeSpecs'))
90
102
  }
91
103
 
92
104
  return createdRecords
@@ -109,7 +121,7 @@ function createNodeRuns(
109
121
  .create(nodeRunId)
110
122
  .content(buildNodeRunCreateContent(runId, planSpecId, nodeSpec))
111
123
  .output('after')
112
- createdNodeRuns.push(PlanNodeRunSchema.parse(created))
124
+ createdNodeRuns.push(yield* parseRowOrFail(PlanNodeRunSchema, created, 'createNodeRuns'))
113
125
  }
114
126
 
115
127
  return createdNodeRuns
@@ -135,122 +147,123 @@ export function createInitializedRunGraph(params: {
135
147
  runPatch?: { replacedRunId?: RecordIdInput }
136
148
  planExecutor: ExecutionPlanGraphExecutor
137
149
  }) {
138
- return runPromise(
139
- Effect.gen(function* () {
140
- const nodeSpecs = yield* createNodeSpecs(params.tx, params.spec.id, params.nodes)
141
- const run = PlanRunSchema.parse(
142
- yield* params.tx
143
- .create(ensureRecordId(params.runId, TABLES.PLAN_RUN))
144
- .content({
145
- planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
146
- organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
147
- threadId: ensureRecordId(params.threadId, TABLES.THREAD),
148
- ...(params.sourceThreadId ? { sourceThreadId: ensureRecordId(params.sourceThreadId, TABLES.THREAD) } : {}),
149
- leadAgentId: params.leadAgentId,
150
- ...(params.createdByAgentId ? { createdByAgentId: params.createdByAgentId } : {}),
151
- status: params.requireApproval ? 'pending-approval' : 'running',
152
- readyNodeIds: [],
153
- failureCount: 0,
154
- ...(params.runPatch?.replacedRunId
155
- ? { replacedRunId: ensureRecordId(params.runPatch.replacedRunId, TABLES.PLAN_RUN) }
156
- : {}),
157
- ...(params.requireApproval ? {} : { startedAt: nowDate() }),
158
- })
159
- .output('after'),
160
- )
150
+ return Effect.gen(function* () {
151
+ const nodeSpecs = yield* createNodeSpecs(params.tx, params.spec.id, params.nodes)
152
+ const runRaw = yield* params.tx
153
+ .create(ensureRecordId(params.runId, TABLES.PLAN_RUN))
154
+ .content({
155
+ planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
156
+ organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
157
+ threadId: ensureRecordId(params.threadId, TABLES.THREAD),
158
+ ...(params.sourceThreadId ? { sourceThreadId: ensureRecordId(params.sourceThreadId, TABLES.THREAD) } : {}),
159
+ leadAgentId: params.leadAgentId,
160
+ ...(params.createdByAgentId ? { createdByAgentId: params.createdByAgentId } : {}),
161
+ status: params.requireApproval ? 'pending-approval' : 'running',
162
+ readyNodeIds: [],
163
+ failureCount: 0,
164
+ ...(params.runPatch?.replacedRunId
165
+ ? { replacedRunId: ensureRecordId(params.runPatch.replacedRunId, TABLES.PLAN_RUN) }
166
+ : {}),
167
+ ...(params.requireApproval ? {} : { startedAt: nowDate() }),
168
+ })
169
+ .output('after')
170
+ const run = yield* parseRowOrFail(PlanRunSchema, runRaw, 'createInitializedRunGraph:run')
161
171
 
162
- const nodeRuns = yield* createNodeRuns(params.tx, run.id, params.spec.id, nodeSpecs)
163
- const synced = params.requireApproval
164
- ? { run, nodeRuns }
165
- : yield* Effect.tryPromise(() =>
166
- params.planExecutor.syncRunGraph({
167
- tx: params.tx,
168
- run,
169
- spec: params.spec,
170
- nodeSpecs,
171
- nodeRuns,
172
- artifacts: [],
173
- emittedBy: params.leadAgentId,
174
- capturedEvents: params.emittedEvents,
175
- }),
176
- )
172
+ const nodeRuns = yield* createNodeRuns(params.tx, run.id, params.spec.id, nodeSpecs)
173
+ const synced = params.requireApproval
174
+ ? { run, nodeRuns }
175
+ : yield* params.planExecutor.syncRunGraph({
176
+ tx: params.tx,
177
+ run,
178
+ spec: params.spec,
179
+ nodeSpecs,
180
+ nodeRuns,
181
+ artifacts: [],
182
+ emittedBy: params.leadAgentId,
183
+ capturedEvents: params.emittedEvents,
184
+ })
185
+
186
+ const event = yield* params.tx
187
+ .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
188
+ .content({
189
+ planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
190
+ runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
191
+ eventType: params.createdEventType,
192
+ message: params.createdEventMessage,
193
+ emittedBy: params.leadAgentId,
194
+ detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
195
+ })
196
+ .output('after')
197
+ params.emittedEvents.push(yield* parseRowOrFail(PlanEventSchema, event, 'createInitializedRunGraph:createdEvent'))
177
198
 
178
- const event = yield* params.tx
199
+ if (params.requireApproval) {
200
+ const pendingApprovalEvent = yield* params.tx
179
201
  .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
180
202
  .content({
181
203
  planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
182
204
  runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
183
- eventType: params.createdEventType,
184
- message: params.createdEventMessage,
205
+ eventType: 'plan-pending-approval',
206
+ toStatus: synced.run.status,
207
+ message: `Execution plan "${params.spec.title}" is pending approval.`,
185
208
  emittedBy: params.leadAgentId,
186
209
  detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
187
210
  })
188
211
  .output('after')
189
- params.emittedEvents.push(PlanEventSchema.parse(event))
190
-
191
- if (params.requireApproval) {
192
- const pendingApprovalEvent = yield* params.tx
193
- .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
194
- .content({
195
- planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
196
- runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
197
- eventType: 'plan-pending-approval',
198
- toStatus: synced.run.status,
199
- message: `Execution plan "${params.spec.title}" is pending approval.`,
200
- emittedBy: params.leadAgentId,
201
- detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
202
- })
203
- .output('after')
204
- params.emittedEvents.push(PlanEventSchema.parse(pendingApprovalEvent))
205
- }
206
-
207
- const checkpoint = PlanCheckpointSchema.parse(
208
- yield* params.tx
209
- .create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
210
- .content({
211
- runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
212
- sequence: 1,
213
- runStatus: synced.run.status,
214
- readyNodeIds: [...synced.run.readyNodeIds],
215
- activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
216
- artifactIds: [],
217
- lastCompletedNodeIds: synced.nodeRuns
218
- .filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
219
- .map((nodeRun) => nodeRun.nodeId),
220
- snapshot: {
221
- reason: params.checkpointReason,
222
- currentNodeId: synced.run.currentNodeId,
223
- waitingNodeId: synced.run.waitingNodeId,
224
- readyNodeIds: synced.run.readyNodeIds,
225
- },
226
- })
227
- .output('after'),
212
+ params.emittedEvents.push(
213
+ yield* parseRowOrFail(PlanEventSchema, pendingApprovalEvent, 'createInitializedRunGraph:pendingApprovalEvent'),
228
214
  )
215
+ }
229
216
 
230
- const updatedRun = PlanRunSchema.parse(
231
- yield* params.tx
232
- .update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
233
- .content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
234
- .output('after'),
235
- )
217
+ const checkpointRaw = yield* params.tx
218
+ .create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
219
+ .content({
220
+ runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
221
+ sequence: 1,
222
+ runStatus: synced.run.status,
223
+ readyNodeIds: [...synced.run.readyNodeIds],
224
+ activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
225
+ artifactIds: [],
226
+ lastCompletedNodeIds: synced.nodeRuns
227
+ .filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
228
+ .map((nodeRun) => nodeRun.nodeId),
229
+ snapshot: {
230
+ reason: params.checkpointReason,
231
+ currentNodeId: synced.run.currentNodeId,
232
+ waitingNodeId: synced.run.waitingNodeId,
233
+ readyNodeIds: synced.run.readyNodeIds,
234
+ },
235
+ })
236
+ .output('after')
237
+ const checkpoint = yield* parseRowOrFail(
238
+ PlanCheckpointSchema,
239
+ checkpointRaw,
240
+ 'createInitializedRunGraph:checkpoint',
241
+ )
236
242
 
237
- const checkpointEvent = yield* params.tx
238
- .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
239
- .content({
240
- planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
241
- runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
242
- eventType: 'checkpoint-saved',
243
- message: 'Saved checkpoint 1.',
244
- emittedBy: 'system',
245
- detail: {
246
- checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT),
247
- reason: params.checkpointReason,
248
- },
249
- })
250
- .output('after')
251
- params.emittedEvents.push(PlanEventSchema.parse(checkpointEvent))
243
+ const updatedRunRaw = yield* params.tx
244
+ .update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
245
+ .content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
246
+ .output('after')
247
+ const updatedRun = yield* parseRowOrFail(PlanRunSchema, updatedRunRaw, 'createInitializedRunGraph:updatedRun')
252
248
 
253
- return updatedRun
254
- }),
255
- )
249
+ const checkpointEvent = yield* params.tx
250
+ .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
251
+ .content({
252
+ planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
253
+ runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
254
+ eventType: 'checkpoint-saved',
255
+ message: 'Saved checkpoint 1.',
256
+ emittedBy: 'system',
257
+ detail: {
258
+ checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT),
259
+ reason: params.checkpointReason,
260
+ },
261
+ })
262
+ .output('after')
263
+ params.emittedEvents.push(
264
+ yield* parseRowOrFail(PlanEventSchema, checkpointEvent, 'createInitializedRunGraph:checkpointEvent'),
265
+ )
266
+
267
+ return updatedRun
268
+ })
256
269
  }
@@ -1,4 +1,3 @@
1
- import type { PlanRunRecord } from '@lota-sdk/shared'
2
1
  import { PlanRunSchema } from '@lota-sdk/shared'
3
2
  import { Schema, Effect } from 'effect'
4
3
 
@@ -6,7 +5,6 @@ import type { RecordIdInput } from '../../db/record-id'
6
5
  import { ensureRecordId } from '../../db/record-id'
7
6
  import type { SurrealDBService } from '../../db/service'
8
7
  import { TABLES } from '../../db/tables'
9
- import { runPromise } from '../../effect/runtime'
10
8
  import { toDatabaseDateTime } from '../../utils/date-time'
11
9
  import { toRunData } from '../plan/plan-run-data'
12
10
  import type { makePlanRunService } from '../plan/plan-run.service'
@@ -21,7 +19,7 @@ function toPlanScheduleAttachError(operation: string, cause: unknown) {
21
19
  return new PlanScheduleAttachError({ cause: `${operation}: ${detail}` })
22
20
  }
23
21
 
24
- function attachPlanScheduleIfNeededEffect(params: {
22
+ export function attachPlanScheduleIfNeeded(params: {
25
23
  db: SurrealDBService
26
24
  planRunService: ReturnType<typeof makePlanRunService>
27
25
  planSchedulerService: ReturnType<typeof makePlanSchedulerService>
@@ -70,15 +68,3 @@ function attachPlanScheduleIfNeededEffect(params: {
70
68
  return updatedRun ?? run
71
69
  })
72
70
  }
73
-
74
- export function attachPlanScheduleIfNeeded(params: {
75
- db: SurrealDBService
76
- planRunService: ReturnType<typeof makePlanRunService>
77
- planSchedulerService: ReturnType<typeof makePlanSchedulerService>
78
- organizationId: RecordIdInput
79
- threadId: RecordIdInput
80
- runId: RecordIdInput
81
- planSpecId: RecordIdInput
82
- }): Promise<PlanRunRecord> {
83
- return runPromise(attachPlanScheduleIfNeededEffect(params))
84
- }
@@ -10,6 +10,7 @@ import type {
10
10
  import { PlanDraftSchema, PlanRunSchema, PlanSpecSchema } from '@lota-sdk/shared'
11
11
  import { Context, Schema, Effect, Layer, Match } from 'effect'
12
12
  import { RecordId } from 'surrealdb'
13
+ import type { z } from 'zod'
13
14
 
14
15
  import type { RecordIdInput } from '../../db/record-id'
15
16
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
@@ -84,6 +85,17 @@ const effectTryPromise = makeEffectTryPromiseWithMessage(
84
85
  (message, cause) => new ExecutionPlanServiceError({ message, cause }),
85
86
  )
86
87
 
88
+ function parseRowEffect<TSchema extends z.ZodTypeAny>(
89
+ schema: TSchema,
90
+ value: unknown,
91
+ label: string,
92
+ ): Effect.Effect<z.infer<TSchema>, ExecutionPlanServiceError> {
93
+ return Effect.try({
94
+ try: () => schema.parse(value) as z.infer<TSchema>,
95
+ catch: (cause) => new ExecutionPlanServiceError({ message: `Failed to parse ${label}.`, cause }),
96
+ })
97
+ }
98
+
87
99
  function toSerializablePlanEffect(
88
100
  deps: ExecutionPlanDeps,
89
101
  run: PlanRunRecord,
@@ -285,7 +297,8 @@ function createPlanEffect(
285
297
  const databaseService = deps.db
286
298
  const requireApproval =
287
299
  params.requireApproval ?? hasCrossThreadSourceContext(params.sourceThreadId, params.threadId)
288
- const preparedDraft = deps.planBuilder.prepareDraft(PlanDraftSchema.parse(params.input))
300
+ const parsedDraft = yield* parseRowEffect(PlanDraftSchema, params.input, 'plan draft input')
301
+ const preparedDraft = deps.planBuilder.prepareDraft(parsedDraft)
289
302
  const validation = deps.planValidator.validateDraft(preparedDraft)
290
303
  if (validation.blocking.length > 0) {
291
304
  return yield* new BadRequestError({
@@ -319,7 +332,7 @@ function createPlanEffect(
319
332
  .output('after'),
320
333
  'Failed to create execution plan spec.',
321
334
  )
322
- const spec = PlanSpecSchema.parse(createdSpec)
335
+ const spec = yield* parseRowEffect(PlanSpecSchema, createdSpec, 'created plan spec')
323
336
 
324
337
  yield* effectTryPromise(
325
338
  () =>
@@ -391,7 +404,8 @@ function replacePlanEffect(
391
404
 
392
405
  const activeSpec = yield* deps.planRun.getPlanSpecById(activeRun.planSpecId)
393
406
  const { runId: _runId, reason: _reason, requireApproval: requestedRequireApproval, ...draftInput } = params.input
394
- const preparedDraft = deps.planBuilder.prepareDraft(PlanDraftSchema.parse(draftInput))
407
+ const parsedDraft = yield* parseRowEffect(PlanDraftSchema, draftInput, 'plan draft replacement input')
408
+ const preparedDraft = deps.planBuilder.prepareDraft(parsedDraft)
395
409
  const validation = deps.planValidator.validateDraft(preparedDraft)
396
410
  if (validation.blocking.length > 0) {
397
411
  return yield* new BadRequestError({
@@ -419,7 +433,7 @@ function replacePlanEffect(
419
433
  .output('after'),
420
434
  'Failed to update superseded execution plan spec.',
421
435
  )
422
- const supersededSpec = PlanSpecSchema.parse(updatedSpec)
436
+ const supersededSpec = yield* parseRowEffect(PlanSpecSchema, updatedSpec, 'superseded plan spec')
423
437
 
424
438
  const updatedRun = yield* effectTryPromise(
425
439
  () =>
@@ -437,7 +451,7 @@ function replacePlanEffect(
437
451
  .output('after'),
438
452
  'Failed to abort previous execution run.',
439
453
  )
440
- const abortedRun = PlanRunSchema.parse(updatedRun)
454
+ const abortedRun = yield* parseRowEffect(PlanRunSchema, updatedRun, 'aborted plan run')
441
455
 
442
456
  const createdSpec = yield* effectTryPromise(
443
457
  () =>
@@ -456,7 +470,7 @@ function replacePlanEffect(
456
470
  .output('after'),
457
471
  'Failed to create replacement execution plan spec.',
458
472
  )
459
- const spec = PlanSpecSchema.parse(createdSpec)
473
+ const spec = yield* parseRowEffect(PlanSpecSchema, createdSpec, 'replacement plan spec')
460
474
 
461
475
  yield* effectTryPromise(
462
476
  () =>
@@ -523,18 +537,19 @@ function submitPlanTurnResultEffect(
523
537
  },
524
538
  ) {
525
539
  return Effect.gen(function* () {
526
- const result = yield* Effect.tryPromise({
527
- try: () =>
528
- deps.planExecutor.submitNodeResult({
529
- threadId: params.threadId,
530
- runId: params.runId,
531
- nodeId: params.nodeId,
532
- emittedBy: params.emittedBy,
533
- result: params.input,
534
- }),
535
- catch: (cause) =>
536
- new ExecutionPlanServiceError({ message: 'Failed to submit execution plan node result.', cause }),
537
- })
540
+ const result = yield* deps.planExecutor
541
+ .submitNodeResult({
542
+ threadId: params.threadId,
543
+ runId: params.runId,
544
+ nodeId: params.nodeId,
545
+ emittedBy: params.emittedBy,
546
+ result: params.input,
547
+ })
548
+ .pipe(
549
+ Effect.mapError(
550
+ (cause) => new ExecutionPlanServiceError({ message: 'Failed to submit execution plan node result.', cause }),
551
+ ),
552
+ )
538
553
 
539
554
  const plan = yield* finalizePlanSnapshotEffect(deps, { runId: params.runId, emittedBy: params.emittedBy })
540
555
  return buildExecutionPlanToolResult({
@@ -550,15 +565,13 @@ function resumeRunEffect(
550
565
  params: { threadId: RecordIdInput; emittedBy: string; input: { runId: string } },
551
566
  ) {
552
567
  return Effect.gen(function* () {
553
- const result = yield* Effect.tryPromise({
554
- try: () =>
555
- deps.planExecutor.resumeRun({
556
- threadId: params.threadId,
557
- runId: params.input.runId,
558
- emittedBy: params.emittedBy,
559
- }),
560
- catch: (cause) => new ExecutionPlanServiceError({ message: 'Failed to resume execution run.', cause }),
561
- })
568
+ const result = yield* deps.planExecutor
569
+ .resumeRun({ threadId: params.threadId, runId: params.input.runId, emittedBy: params.emittedBy })
570
+ .pipe(
571
+ Effect.mapError(
572
+ (cause) => new ExecutionPlanServiceError({ message: 'Failed to resume execution run.', cause }),
573
+ ),
574
+ )
562
575
 
563
576
  const plan = yield* finalizePlanSnapshotEffect(deps, { runId: params.input.runId, emittedBy: params.emittedBy })
564
577
  return buildExecutionPlanToolResult({
@@ -609,7 +622,7 @@ function approvePlanEffect(
609
622
  .output('after'),
610
623
  'Failed to activate execution run.',
611
624
  )
612
- const activatedRun = PlanRunSchema.parse(updatedRun)
625
+ const activatedRun = yield* parseRowEffect(PlanRunSchema, updatedRun, 'activated plan run')
613
626
 
614
627
  const synced = yield* effectTryPromise(
615
628
  () =>
@@ -729,7 +742,7 @@ function rejectPlanEffect(
729
742
  .output('after'),
730
743
  'Failed to abort execution run.',
731
744
  )
732
- const rejectedRun = PlanRunSchema.parse(updatedRun)
745
+ const rejectedRun = yield* parseRowEffect(PlanRunSchema, updatedRun, 'rejected plan run')
733
746
 
734
747
  yield* emitEvent({
735
748
  tx,
@@ -778,17 +791,19 @@ function respondToApprovalEffect(
778
791
  const run = yield* deps.planRun.getActiveRunRecord(params.threadId)
779
792
  if (!run) return null
780
793
 
781
- const plan = yield* Effect.tryPromise({
782
- try: () =>
783
- deps.planExecutor.submitHumanNodeResponse({
784
- threadId: params.threadId,
785
- approvalId: params.input.approvalId,
786
- respondedBy: params.emittedBy,
787
- response: params.input.response,
788
- approvalMessageId: params.input.approvalMessageId,
789
- }),
790
- catch: (cause) => new ExecutionPlanServiceError({ message: 'Failed to submit human approval response.', cause }),
791
- })
794
+ const plan = yield* deps.planExecutor
795
+ .submitHumanNodeResponse({
796
+ threadId: params.threadId,
797
+ approvalId: params.input.approvalId,
798
+ respondedBy: params.emittedBy,
799
+ response: params.input.response,
800
+ approvalMessageId: params.input.approvalMessageId,
801
+ })
802
+ .pipe(
803
+ Effect.mapError(
804
+ (cause) => new ExecutionPlanServiceError({ message: 'Failed to submit human approval response.', cause }),
805
+ ),
806
+ )
792
807
  if (!plan) return null
793
808
 
794
809
  return yield* finalizePlanSnapshotEffect(deps, { runId: run.id, emittedBy: params.emittedBy })
@@ -825,16 +840,18 @@ function applyHumanInputFromUserMessageEffect(
825
840
  )
826
841
  if (!response) return null
827
842
 
828
- const plan = yield* Effect.tryPromise({
829
- try: () =>
830
- deps.planExecutor.submitHumanNodeResponse({
831
- threadId: params.threadId,
832
- respondedBy: params.respondedBy,
833
- response,
834
- approvalMessageId: params.message.id,
835
- }),
836
- catch: (cause) => new ExecutionPlanServiceError({ message: 'Failed to submit human node response.', cause }),
837
- })
843
+ const plan = yield* deps.planExecutor
844
+ .submitHumanNodeResponse({
845
+ threadId: params.threadId,
846
+ respondedBy: params.respondedBy,
847
+ response,
848
+ approvalMessageId: params.message.id,
849
+ })
850
+ .pipe(
851
+ Effect.mapError(
852
+ (cause) => new ExecutionPlanServiceError({ message: 'Failed to submit human node response.', cause }),
853
+ ),
854
+ )
838
855
  if (!plan) return null
839
856
 
840
857
  return yield* finalizePlanSnapshotEffect(deps, { runId: run.id, emittedBy: params.respondedBy })
@@ -1010,7 +1027,7 @@ export function makeExecutionPlanService(deps: ExecutionPlanDeps) {
1010
1027
  export class ExecutionPlanServiceTag extends Context.Service<
1011
1028
  ExecutionPlanServiceTag,
1012
1029
  ReturnType<typeof makeExecutionPlanService>
1013
- >()('ExecutionPlanService') {}
1030
+ >()('@lota-sdk/core/ExecutionPlanService') {}
1014
1031
 
1015
1032
  export const ExecutionPlanServiceLive = Layer.effect(
1016
1033
  ExecutionPlanServiceTag,
@@ -141,7 +141,7 @@ export function makeFeedbackLoopService(planRunService: ReturnType<typeof makePl
141
141
  export class FeedbackLoopServiceTag extends Context.Service<
142
142
  FeedbackLoopServiceTag,
143
143
  ReturnType<typeof makeFeedbackLoopService>
144
- >()('FeedbackLoopService') {}
144
+ >()('@lota-sdk/core/FeedbackLoopService') {}
145
145
 
146
146
  export const FeedbackLoopServiceLive = Layer.effect(
147
147
  FeedbackLoopServiceTag,
@@ -1,10 +1,20 @@
1
- import type { ConvergenceState } from '@lota-sdk/shared'
1
+ import type {
2
+ ConvergenceState,
3
+ ExecutionMode,
4
+ PlanNodeResultSubmission,
5
+ PlanNodeRunRecord,
6
+ PlanNodeSpecRecord,
7
+ PlanRunRecord,
8
+ PlanSpecRecord,
9
+ } from '@lota-sdk/shared'
2
10
  import { Context, Effect, Layer } from 'effect'
3
11
 
4
- import { runPromise } from '../effect/runtime'
5
- import { getCurrentRuntime } from '../effect/runtime-ref'
12
+ import type { ResolvedAgentConfig } from '../config/agent-defaults'
13
+ import { AgentConfigServiceTag } from '../effect/services'
14
+ import type { PlanAgentHeartbeatQueueRuntime } from '../queues/plan-agent-heartbeat.queue'
15
+ import { LotaQueuesServiceTag } from '../queues/queues.service'
6
16
  import { routeGraphFullEffect } from './graph-full-routing'
7
- import type { makeOwnershipDispatcherService } from './ownership-dispatcher.service'
17
+ import type { OwnershipDispatcherService } from './ownership-dispatcher.service'
8
18
  import { OwnershipDispatcherServiceTag } from './ownership-dispatcher.service'
9
19
  import type { makePlanExecutorService } from './plan/plan-executor.service'
10
20
  import { PlanExecutorServiceTag } from './plan/plan-executor.service'
@@ -48,20 +58,36 @@ function decideRerouteAction(params: {
48
58
  }
49
59
 
50
60
  interface GlobalOrchestratorDeps {
51
- ownershipDispatcherService: ReturnType<typeof makeOwnershipDispatcherService>
61
+ agentConfig: ResolvedAgentConfig
62
+ ownershipDispatcherService: Pick<OwnershipDispatcherService, 'dispatchReadyNode'>
52
63
  planExecutorService: ReturnType<typeof makePlanExecutorService>
53
64
  planRunService: ReturnType<typeof makePlanRunService>
65
+ planAgentHeartbeatQueue: PlanAgentHeartbeatQueueRuntime
54
66
  }
55
67
 
68
+ type DispatchReadyNodeParams = {
69
+ run: PlanRunRecord
70
+ nodeSpecRecord: PlanNodeSpecRecord
71
+ nodeRun: PlanNodeRunRecord
72
+ spec: PlanSpecRecord
73
+ executionModeOverride?: ExecutionMode
74
+ }
75
+
76
+ type DispatchReadyNode = (params: DispatchReadyNodeParams) => Effect.Effect<PlanNodeResultSubmission, unknown, never>
77
+
56
78
  export function makeGlobalOrchestratorService(deps: GlobalOrchestratorDeps) {
79
+ const dispatchReadyNode: DispatchReadyNode = (params) => deps.ownershipDispatcherService.dispatchReadyNode(params)
80
+
57
81
  return {
58
82
  detectConvergence,
59
83
  decideRerouteAction,
60
84
  routeGraphFull: (params: { threadId: string; runId: string }) =>
61
85
  routeGraphFullEffect(params, {
62
- dispatchReadyNode: (dispatchParams) => deps.ownershipDispatcherService.dispatchReadyNode(dispatchParams),
86
+ agentConfig: deps.agentConfig,
87
+ dispatchReadyNode,
63
88
  planExecutorService: deps.planExecutorService,
64
89
  planRunService: deps.planRunService,
90
+ planAgentHeartbeatQueue: deps.planAgentHeartbeatQueue,
65
91
  }),
66
92
  }
67
93
  }
@@ -69,22 +95,30 @@ export function makeGlobalOrchestratorService(deps: GlobalOrchestratorDeps) {
69
95
  export class GlobalOrchestratorServiceTag extends Context.Service<
70
96
  GlobalOrchestratorServiceTag,
71
97
  ReturnType<typeof makeGlobalOrchestratorService>
72
- >()('GlobalOrchestratorService') {}
98
+ >()('@lota-sdk/core/GlobalOrchestratorService') {}
73
99
 
74
100
  export const GlobalOrchestratorServiceLive = Layer.effect(
75
101
  GlobalOrchestratorServiceTag,
76
102
  Effect.gen(function* () {
103
+ const agentConfig = yield* AgentConfigServiceTag
77
104
  const ownershipDispatcherService = yield* OwnershipDispatcherServiceTag
78
105
  const planExecutorService = yield* PlanExecutorServiceTag
79
106
  const planRunService = yield* PlanRunServiceTag
80
- return makeGlobalOrchestratorService({ ownershipDispatcherService, planExecutorService, planRunService })
107
+ const queues = yield* LotaQueuesServiceTag
108
+ return makeGlobalOrchestratorService({
109
+ agentConfig,
110
+ ownershipDispatcherService,
111
+ planExecutorService,
112
+ planRunService,
113
+ planAgentHeartbeatQueue: queues.planAgentHeartbeat,
114
+ })
81
115
  }),
82
116
  )
83
117
 
84
- function getGlobalOrchestratorService() {
85
- return getCurrentRuntime().runSync(Effect.service(GlobalOrchestratorServiceTag))
86
- }
87
-
88
- export function routeGraphFull(params: { threadId: string; runId: string }): Promise<void> {
89
- return runPromise(getGlobalOrchestratorService().routeGraphFull(params))
90
- }
118
+ export const routeGraphFull = Effect.fn('GlobalOrchestrator.routeGraphFull')(function* (params: {
119
+ threadId: string
120
+ runId: string
121
+ }) {
122
+ const globalOrchestratorService = yield* GlobalOrchestratorServiceTag
123
+ return yield* globalOrchestratorService.routeGraphFull(params)
124
+ })