@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
@@ -1,4 +1,4 @@
1
- import { isRecord } from '../utils/string'
1
+ import { isRecord } from '../../utils/string'
2
2
 
3
3
  export function readPathValue(source: unknown, path: string): unknown {
4
4
  if (!path.trim()) return source
@@ -5,10 +5,10 @@ import type {
5
5
  SerializableExecutionPlan,
6
6
  } from '@lota-sdk/shared'
7
7
 
8
- import type { RecordIdInput } from '../db/record-id'
9
- import { ensureRecordId } from '../db/record-id'
10
- import { TABLES } from '../db/tables'
11
- import { toDatabaseDateTime } from '../utils/date-time'
8
+ import type { RecordIdInput } from '../../db/record-id'
9
+ import { ensureRecordId } from '../../db/record-id'
10
+ import { TABLES } from '../../db/tables'
11
+ import { toDatabaseDateTime } from '../../utils/date-time'
12
12
 
13
13
  export type PlanRunUpdate = Omit<
14
14
  Partial<PlanRunRecord>,
@@ -0,0 +1,15 @@
1
+ import type { PlanRunRecord } from '@lota-sdk/shared'
2
+
3
+ import type { makePlanRunService } from './plan-run.service'
4
+
5
+ const FULL_PLAN_SNAPSHOT_OPTIONS = {
6
+ includeEvents: true,
7
+ includeArtifacts: true,
8
+ includeApprovals: true,
9
+ includeCheckpoints: true,
10
+ includeValidationIssues: true,
11
+ } as const
12
+
13
+ export function serializeRunFull(planRunService: ReturnType<typeof makePlanRunService>, run: PlanRunRecord) {
14
+ return planRunService.toSerializablePlan(run, FULL_PLAN_SNAPSHOT_OPTIONS)
15
+ }
@@ -0,0 +1,637 @@
1
+ import {
2
+ PlanApprovalSchema,
3
+ PlanArtifactSchema,
4
+ PlanCheckpointSchema,
5
+ PlanEventSchema,
6
+ PlanNodeAttemptSchema,
7
+ PlanNodeRunSchema,
8
+ PlanNodeSpecRecordSchema,
9
+ PlanRunSchema,
10
+ PlanSpecSchema,
11
+ PlanValidationIssueSchema,
12
+ } from '@lota-sdk/shared'
13
+ import type {
14
+ PlanApprovalRecord,
15
+ PlanArtifactRecord,
16
+ PlanCheckpointRecord,
17
+ PlanEventRecord,
18
+ PlanNodeRunRecord,
19
+ PlanNodeRunStatus,
20
+ PlanRunRecord,
21
+ PlanValidationIssueRecord,
22
+ SerializablePlanApproval,
23
+ SerializablePlanArtifact,
24
+ SerializablePlanCheckpoint,
25
+ SerializablePlanEvent,
26
+ SerializablePlanNode,
27
+ SerializablePlanValidationIssue,
28
+ } from '@lota-sdk/shared'
29
+ import { Context, Schema, Effect, Layer } from 'effect'
30
+ import type { RecordId } from 'surrealdb'
31
+
32
+ import type { RecordIdInput } from '../../db/record-id'
33
+ import { ensureRecordId, recordIdToString } from '../../db/record-id'
34
+ import type { SurrealDBService } from '../../db/service'
35
+ import { TABLES } from '../../db/tables'
36
+ import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
37
+ import { DatabaseServiceTag } from '../../effect/services'
38
+ import { toIsoDateTimeString, toOptionalIsoDateTimeString } from '../../utils/date-time'
39
+
40
+ const ACTIVE_RUN_STATUSES = new Set(['pending-approval', 'running', 'awaiting-human', 'blocked'])
41
+
42
+ function buildProgress(nodeRuns: PlanNodeRunRecord[]) {
43
+ const counts = nodeRuns.reduce(
44
+ (summary, nodeRun) => {
45
+ summary[nodeRun.status] += 1
46
+ return summary
47
+ },
48
+ {
49
+ pending: 0,
50
+ ready: 0,
51
+ running: 0,
52
+ 'awaiting-human': 0,
53
+ completed: 0,
54
+ partial: 0,
55
+ blocked: 0,
56
+ failed: 0,
57
+ skipped: 0,
58
+ scheduled: 0,
59
+ monitoring: 0,
60
+ } satisfies Record<PlanNodeRunStatus, number>,
61
+ )
62
+
63
+ const total = nodeRuns.length
64
+ const completedWork = counts.completed + counts.partial + counts.skipped
65
+
66
+ return {
67
+ total,
68
+ pending: counts.pending,
69
+ ready: counts.ready,
70
+ running: counts.running,
71
+ awaitingHuman: counts['awaiting-human'],
72
+ completed: counts.completed,
73
+ partial: counts.partial,
74
+ blocked: counts.blocked,
75
+ failed: counts.failed,
76
+ skipped: counts.skipped,
77
+ scheduled: counts.scheduled,
78
+ monitoring: counts.monitoring,
79
+ completionRatio: total > 0 ? Number((completedWork / total).toFixed(4)) : undefined,
80
+ }
81
+ }
82
+
83
+ function serializeArtifact(artifact: PlanArtifactRecord): SerializablePlanArtifact {
84
+ return {
85
+ id: recordIdToString(artifact.id, TABLES.PLAN_ARTIFACT),
86
+ nodeId: artifact.nodeId,
87
+ attemptId: recordIdToString(artifact.attemptId, TABLES.PLAN_NODE_ATTEMPT),
88
+ name: artifact.name,
89
+ kind: artifact.kind,
90
+ pointer: artifact.pointer,
91
+ schemaRef: artifact.schemaRef,
92
+ description: artifact.description,
93
+ content: artifact.content,
94
+ payload: artifact.payload,
95
+ publishedArtifactId: artifact.publishedArtifactId
96
+ ? recordIdToString(artifact.publishedArtifactId, TABLES.ARTIFACT)
97
+ : undefined,
98
+ createdAt: toIsoDateTimeString(artifact.createdAt),
99
+ }
100
+ }
101
+
102
+ function serializeValidationIssue(issue: PlanValidationIssueRecord): SerializablePlanValidationIssue {
103
+ return {
104
+ id: recordIdToString(issue.id, TABLES.PLAN_VALIDATION_ISSUE),
105
+ nodeId: issue.nodeId,
106
+ attemptId: issue.attemptId ? recordIdToString(issue.attemptId, TABLES.PLAN_NODE_ATTEMPT) : undefined,
107
+ severity: issue.severity,
108
+ code: issue.code,
109
+ message: issue.message,
110
+ detail: issue.detail,
111
+ createdAt: toIsoDateTimeString(issue.createdAt),
112
+ }
113
+ }
114
+
115
+ function serializeApproval(approval: PlanApprovalRecord): SerializablePlanApproval {
116
+ return {
117
+ id: recordIdToString(approval.id, TABLES.PLAN_APPROVAL),
118
+ nodeId: approval.nodeId,
119
+ status: approval.status,
120
+ presented: approval.presented,
121
+ response: approval.response,
122
+ requestedBy: approval.requestedBy,
123
+ respondedBy: approval.respondedBy,
124
+ approvalMessageId: approval.approvalMessageId,
125
+ comments: approval.comments,
126
+ requiredEdits: [...approval.requiredEdits],
127
+ createdAt: toIsoDateTimeString(approval.createdAt),
128
+ respondedAt: toOptionalIsoDateTimeString(approval.respondedAt),
129
+ }
130
+ }
131
+
132
+ function serializeCheckpoint(checkpoint: PlanCheckpointRecord): SerializablePlanCheckpoint {
133
+ return {
134
+ id: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT),
135
+ sequence: checkpoint.sequence,
136
+ runStatus: checkpoint.runStatus,
137
+ readyNodeIds: [...checkpoint.readyNodeIds],
138
+ activeNodeIds: [...checkpoint.activeNodeIds],
139
+ artifactIds: checkpoint.artifactIds.map((artifactId) => recordIdToString(artifactId, TABLES.PLAN_ARTIFACT)),
140
+ lastCompletedNodeIds: [...checkpoint.lastCompletedNodeIds],
141
+ snapshot: checkpoint.snapshot,
142
+ createdAt: toIsoDateTimeString(checkpoint.createdAt),
143
+ }
144
+ }
145
+
146
+ function serializeEvent(event: PlanEventRecord): SerializablePlanEvent {
147
+ return {
148
+ id: recordIdToString(event.id, TABLES.PLAN_EVENT),
149
+ nodeId: event.nodeId,
150
+ attemptId: event.attemptId ? recordIdToString(event.attemptId, TABLES.PLAN_NODE_ATTEMPT) : undefined,
151
+ approvalId: event.approvalId ? recordIdToString(event.approvalId, TABLES.PLAN_APPROVAL) : undefined,
152
+ eventType: event.eventType,
153
+ fromStatus: event.fromStatus,
154
+ toStatus: event.toStatus,
155
+ message: event.message,
156
+ detail: event.detail,
157
+ emittedBy: event.emittedBy,
158
+ createdAt: toIsoDateTimeString(event.createdAt),
159
+ }
160
+ }
161
+
162
+ class PlanRunServiceError extends Schema.TaggedErrorClass<PlanRunServiceError>()('PlanRunServiceError', {
163
+ message: Schema.String,
164
+ cause: Schema.optional(Schema.Defect),
165
+ }) {}
166
+
167
+ class PlanRunNotFoundError extends Schema.TaggedErrorClass<PlanRunNotFoundError>()('PlanRunNotFoundError', {
168
+ message: Schema.String,
169
+ entity: Schema.Literals(['plan spec', 'plan run', 'plan node spec', 'plan node run']),
170
+ id: Schema.String,
171
+ }) {}
172
+
173
+ class PlanRunSerializationError extends Schema.TaggedErrorClass<PlanRunSerializationError>()(
174
+ 'PlanRunSerializationError',
175
+ { message: Schema.String },
176
+ ) {}
177
+
178
+ const effectTryPlanRunPromise = makeEffectTryPromiseWithMessage(
179
+ (message, cause) => new PlanRunServiceError({ message, cause }),
180
+ )
181
+
182
+ function tryPlanRunPromise<A>(
183
+ message: string,
184
+ evaluate: () => PromiseLike<A> | Effect.Effect<A, unknown>,
185
+ ): Effect.Effect<A, PlanRunServiceError> {
186
+ return effectTryPlanRunPromise(evaluate, message)
187
+ }
188
+
189
+ export function makePlanRunService(db: SurrealDBService) {
190
+ const getPlanSpecByIdEffect = (planSpecId: RecordIdInput) =>
191
+ Effect.gen(function* () {
192
+ const planSpecRecordId = ensureRecordId(planSpecId, TABLES.PLAN_SPEC)
193
+ const spec = yield* tryPlanRunPromise(
194
+ `Failed to load plan spec ${recordIdToString(planSpecId, TABLES.PLAN_SPEC)}`,
195
+ () => db.findOne(TABLES.PLAN_SPEC, { id: planSpecRecordId }, PlanSpecSchema),
196
+ )
197
+
198
+ if (!spec) {
199
+ return yield* new PlanRunNotFoundError({
200
+ message: `Plan spec not found: ${recordIdToString(planSpecId, TABLES.PLAN_SPEC)}`,
201
+ entity: 'plan spec',
202
+ id: recordIdToString(planSpecId, TABLES.PLAN_SPEC),
203
+ })
204
+ }
205
+
206
+ return spec
207
+ })
208
+
209
+ const listPlanSpecsByThreadEffect = (threadId: RecordIdInput) =>
210
+ tryPlanRunPromise(`Failed to load plan specs for thread ${recordIdToString(threadId, TABLES.THREAD)}`, () =>
211
+ db.findMany(TABLES.PLAN_SPEC, { threadId: ensureRecordId(threadId, TABLES.THREAD) }, PlanSpecSchema, {
212
+ orderBy: 'createdAt',
213
+ orderDir: 'DESC',
214
+ }),
215
+ )
216
+
217
+ const listRunsBySpecEffect = (planSpecId: RecordIdInput) =>
218
+ tryPlanRunPromise(`Failed to load plan runs for spec ${recordIdToString(planSpecId, TABLES.PLAN_SPEC)}`, () =>
219
+ db.findMany(TABLES.PLAN_RUN, { planSpecId: ensureRecordId(planSpecId, TABLES.PLAN_SPEC) }, PlanRunSchema, {
220
+ orderBy: 'createdAt',
221
+ orderDir: 'DESC',
222
+ }),
223
+ )
224
+
225
+ const listNodeSpecsEffect = (planSpecId: RecordIdInput) =>
226
+ tryPlanRunPromise(`Failed to load plan node specs for spec ${recordIdToString(planSpecId, TABLES.PLAN_SPEC)}`, () =>
227
+ db.findMany(
228
+ TABLES.PLAN_NODE_SPEC,
229
+ { planSpecId: ensureRecordId(planSpecId, TABLES.PLAN_SPEC) },
230
+ PlanNodeSpecRecordSchema,
231
+ { orderBy: 'position', orderDir: 'ASC' },
232
+ ),
233
+ )
234
+
235
+ const getNodeSpecByNodeIdEffect = (planSpecId: RecordIdInput, nodeId: string) =>
236
+ Effect.gen(function* () {
237
+ const nodeSpec = yield* tryPlanRunPromise(`Failed to load plan node spec "${nodeId}"`, () =>
238
+ db.findOne(
239
+ TABLES.PLAN_NODE_SPEC,
240
+ { planSpecId: ensureRecordId(planSpecId, TABLES.PLAN_SPEC), nodeId },
241
+ PlanNodeSpecRecordSchema,
242
+ ),
243
+ )
244
+
245
+ if (!nodeSpec) {
246
+ return yield* new PlanRunNotFoundError({
247
+ message: `Plan node spec "${nodeId}" not found.`,
248
+ entity: 'plan node spec',
249
+ id: nodeId,
250
+ })
251
+ }
252
+
253
+ return nodeSpec
254
+ })
255
+
256
+ const getRunByIdEffect = (runId: RecordIdInput) =>
257
+ Effect.gen(function* () {
258
+ const runRecordId = ensureRecordId(runId, TABLES.PLAN_RUN)
259
+ const run = yield* tryPlanRunPromise(`Failed to load plan run ${recordIdToString(runId, TABLES.PLAN_RUN)}`, () =>
260
+ db.findOne(TABLES.PLAN_RUN, { id: runRecordId }, PlanRunSchema),
261
+ )
262
+
263
+ if (!run) {
264
+ return yield* new PlanRunNotFoundError({
265
+ message: `Plan run not found: ${recordIdToString(runId, TABLES.PLAN_RUN)}`,
266
+ entity: 'plan run',
267
+ id: recordIdToString(runId, TABLES.PLAN_RUN),
268
+ })
269
+ }
270
+
271
+ return run
272
+ })
273
+
274
+ const getActiveRunRecordsEffect = (threadId: RecordIdInput) =>
275
+ Effect.gen(function* () {
276
+ const runs = yield* tryPlanRunPromise(
277
+ `Failed to load active plan runs for thread ${recordIdToString(threadId, TABLES.THREAD)}`,
278
+ () =>
279
+ db.findMany(TABLES.PLAN_RUN, { threadId: ensureRecordId(threadId, TABLES.THREAD) }, PlanRunSchema, {
280
+ orderBy: 'updatedAt',
281
+ orderDir: 'DESC',
282
+ }),
283
+ )
284
+ return runs.filter((run) => ACTIVE_RUN_STATUSES.has(run.status))
285
+ })
286
+
287
+ const getActiveRunRecordEffect = (threadId: RecordIdInput) =>
288
+ Effect.map(getActiveRunRecordsEffect(threadId), (runs) => (runs.length > 0 ? runs[0] : null))
289
+
290
+ const getRunsCreatedInContextEffect = (params: {
291
+ organizationId: RecordIdInput
292
+ sourceThreadId?: RecordIdInput
293
+ createdByAgentId?: string
294
+ statuses?: ReadonlyArray<PlanRunRecord['status']>
295
+ }) =>
296
+ Effect.gen(function* () {
297
+ const filter: Record<string, unknown> = {
298
+ organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
299
+ }
300
+ if (params.sourceThreadId) {
301
+ filter.sourceThreadId = ensureRecordId(params.sourceThreadId, TABLES.THREAD)
302
+ }
303
+ if (params.createdByAgentId) {
304
+ filter.createdByAgentId = params.createdByAgentId
305
+ }
306
+
307
+ const statuses = params.statuses ? new Set(params.statuses) : ACTIVE_RUN_STATUSES
308
+ const runs = yield* tryPlanRunPromise(
309
+ `Failed to load execution plans in context ${recordIdToString(params.organizationId, TABLES.ORGANIZATION)}`,
310
+ () => db.findMany(TABLES.PLAN_RUN, filter, PlanRunSchema, { orderBy: 'updatedAt', orderDir: 'DESC' }),
311
+ )
312
+ return runs.filter((run) => statuses.has(run.status))
313
+ })
314
+
315
+ const listNodeRunsEffect = (runId: RecordIdInput) =>
316
+ tryPlanRunPromise(`Failed to load plan node runs for run ${recordIdToString(runId, TABLES.PLAN_RUN)}`, () =>
317
+ db.findMany(TABLES.PLAN_NODE_RUN, { runId: ensureRecordId(runId, TABLES.PLAN_RUN) }, PlanNodeRunSchema, {
318
+ orderBy: 'nodeId',
319
+ orderDir: 'ASC',
320
+ }),
321
+ )
322
+
323
+ const getNodeRunByNodeIdEffect = (runId: RecordIdInput, nodeId: string) =>
324
+ Effect.gen(function* () {
325
+ const nodeRun = yield* tryPlanRunPromise(`Failed to load plan node run "${nodeId}"`, () =>
326
+ db.findOne(TABLES.PLAN_NODE_RUN, { runId: ensureRecordId(runId, TABLES.PLAN_RUN), nodeId }, PlanNodeRunSchema),
327
+ )
328
+
329
+ if (!nodeRun) {
330
+ return yield* new PlanRunNotFoundError({
331
+ message: `Plan node run "${nodeId}" not found.`,
332
+ entity: 'plan node run',
333
+ id: nodeId,
334
+ })
335
+ }
336
+
337
+ return nodeRun
338
+ })
339
+
340
+ const listArtifactsEffect = (runId: RecordIdInput) =>
341
+ tryPlanRunPromise(`Failed to load plan artifacts for run ${recordIdToString(runId, TABLES.PLAN_RUN)}`, () =>
342
+ db.findMany(TABLES.PLAN_ARTIFACT, { runId: ensureRecordId(runId, TABLES.PLAN_RUN) }, PlanArtifactSchema, {
343
+ orderBy: 'createdAt',
344
+ orderDir: 'ASC',
345
+ }),
346
+ )
347
+
348
+ const listAttemptsEffect = (runId: RecordIdInput) =>
349
+ tryPlanRunPromise(`Failed to load plan attempts for run ${recordIdToString(runId, TABLES.PLAN_RUN)}`, () =>
350
+ db.findMany(TABLES.PLAN_NODE_ATTEMPT, { runId: ensureRecordId(runId, TABLES.PLAN_RUN) }, PlanNodeAttemptSchema, {
351
+ orderBy: 'createdAt',
352
+ orderDir: 'ASC',
353
+ }),
354
+ )
355
+
356
+ const listValidationIssuesEffect = (params: {
357
+ runId?: RecordIdInput
358
+ planSpecId?: RecordIdInput
359
+ attemptId?: RecordIdInput
360
+ }) =>
361
+ Effect.gen(function* () {
362
+ const filter: Record<string, unknown> = {}
363
+ if (params.runId) filter.runId = ensureRecordId(params.runId, TABLES.PLAN_RUN)
364
+ if (params.planSpecId) filter.planSpecId = ensureRecordId(params.planSpecId, TABLES.PLAN_SPEC)
365
+ if (params.attemptId) filter.attemptId = ensureRecordId(params.attemptId, TABLES.PLAN_NODE_ATTEMPT)
366
+
367
+ return yield* tryPlanRunPromise('Failed to load plan validation issues', () =>
368
+ db.findMany(TABLES.PLAN_VALIDATION_ISSUE, filter, PlanValidationIssueSchema, {
369
+ orderBy: 'createdAt',
370
+ orderDir: 'ASC',
371
+ }),
372
+ )
373
+ })
374
+
375
+ const listApprovalsEffect = (runId: RecordIdInput) =>
376
+ tryPlanRunPromise(`Failed to load plan approvals for run ${recordIdToString(runId, TABLES.PLAN_RUN)}`, () =>
377
+ db.findMany(TABLES.PLAN_APPROVAL, { runId: ensureRecordId(runId, TABLES.PLAN_RUN) }, PlanApprovalSchema, {
378
+ orderBy: 'createdAt',
379
+ orderDir: 'ASC',
380
+ }),
381
+ )
382
+
383
+ const getLatestCheckpointEffect = (runId: RecordIdInput) =>
384
+ Effect.gen(function* () {
385
+ const checkpoints = yield* tryPlanRunPromise(
386
+ `Failed to load latest checkpoint for run ${recordIdToString(runId, TABLES.PLAN_RUN)}`,
387
+ () =>
388
+ db.findMany(TABLES.PLAN_CHECKPOINT, { runId: ensureRecordId(runId, TABLES.PLAN_RUN) }, PlanCheckpointSchema, {
389
+ orderBy: 'sequence',
390
+ orderDir: 'DESC',
391
+ limit: 1,
392
+ }),
393
+ )
394
+ return checkpoints.at(0) ?? null
395
+ })
396
+
397
+ const getNextCheckpointSequenceEffect = (runId: RecordIdInput) =>
398
+ Effect.map(getLatestCheckpointEffect(runId), (latestCheckpoint) =>
399
+ latestCheckpoint ? latestCheckpoint.sequence + 1 : 1,
400
+ )
401
+
402
+ const listEventsEffect = (runId: RecordIdInput, limit = 20) =>
403
+ Effect.gen(function* () {
404
+ const events = yield* tryPlanRunPromise(
405
+ `Failed to load plan events for run ${recordIdToString(runId, TABLES.PLAN_RUN)}`,
406
+ () =>
407
+ db.findMany(TABLES.PLAN_EVENT, { runId: ensureRecordId(runId, TABLES.PLAN_RUN) }, PlanEventSchema, {
408
+ orderBy: 'createdAt',
409
+ orderDir: 'DESC',
410
+ limit,
411
+ }),
412
+ )
413
+ return [...events].reverse()
414
+ })
415
+
416
+ const collectLineageArtifactsEffect = (run: PlanRunRecord) =>
417
+ Effect.gen(function* () {
418
+ const collect = (
419
+ currentRunId: RecordId | null,
420
+ depth: number,
421
+ ): Effect.Effect<SerializablePlanArtifact[], PlanRunServiceError | PlanRunNotFoundError> =>
422
+ Effect.gen(function* () {
423
+ if (!currentRunId || depth >= 5) {
424
+ return []
425
+ }
426
+
427
+ const previousRun = yield* tryPlanRunPromise(
428
+ `Failed to load lineage run ${recordIdToString(currentRunId, TABLES.PLAN_RUN)}`,
429
+ () => db.findOne(TABLES.PLAN_RUN, { id: currentRunId }, PlanRunSchema),
430
+ )
431
+ if (!previousRun) {
432
+ return []
433
+ }
434
+
435
+ const [olderArtifacts, artifacts] = yield* Effect.all([
436
+ collect(
437
+ previousRun.replacedRunId ? ensureRecordId(previousRun.replacedRunId, TABLES.PLAN_RUN) : null,
438
+ depth + 1,
439
+ ),
440
+ listArtifactsEffect(previousRun.id),
441
+ ])
442
+
443
+ return [...olderArtifacts, ...artifacts.map(serializeArtifact)]
444
+ })
445
+
446
+ return yield* collect(run.replacedRunId ? ensureRecordId(run.replacedRunId, TABLES.PLAN_RUN) : null, 0)
447
+ })
448
+
449
+ const toSerializablePlanEffect = (
450
+ run: PlanRunRecord,
451
+ options?: {
452
+ includeArtifacts?: boolean
453
+ includeApprovals?: boolean
454
+ includeCheckpoints?: boolean
455
+ includeEvents?: boolean
456
+ includeValidationIssues?: boolean
457
+ slim?: boolean
458
+ },
459
+ ) =>
460
+ Effect.gen(function* () {
461
+ const slim = options?.slim === true
462
+ const spec = yield* getPlanSpecByIdEffect(run.planSpecId)
463
+ const [
464
+ nodeSpecs,
465
+ nodeRuns,
466
+ artifacts,
467
+ lineageArtifacts,
468
+ approvals,
469
+ validationIssues,
470
+ latestCheckpoint,
471
+ recentEvents,
472
+ ] = yield* Effect.all([
473
+ listNodeSpecsEffect(spec.id),
474
+ listNodeRunsEffect(run.id),
475
+ options?.includeArtifacts === false ? Effect.succeed<PlanArtifactRecord[]>([]) : listArtifactsEffect(run.id),
476
+ options?.includeArtifacts === false || slim
477
+ ? Effect.succeed<SerializablePlanArtifact[]>([])
478
+ : collectLineageArtifactsEffect(run),
479
+ options?.includeApprovals === false || slim
480
+ ? Effect.succeed<PlanApprovalRecord[]>([])
481
+ : listApprovalsEffect(run.id),
482
+ options?.includeValidationIssues === false || slim
483
+ ? Effect.succeed<PlanValidationIssueRecord[]>([])
484
+ : listValidationIssuesEffect({ runId: run.id, planSpecId: spec.id }),
485
+ options?.includeCheckpoints
486
+ ? getLatestCheckpointEffect(run.id)
487
+ : Effect.succeed<PlanCheckpointRecord | null>(null),
488
+ options?.includeEvents === false
489
+ ? Effect.succeed<PlanEventRecord[]>([])
490
+ : listEventsEffect(run.id, slim ? 5 : 20),
491
+ ])
492
+
493
+ const nodeRunsById = new Map(nodeRuns.map((nodeRun) => [nodeRun.nodeId, nodeRun]))
494
+ const activeNodeIds = new Set(run.currentNodeId ? [run.currentNodeId] : [])
495
+ const readyNodeIds = new Set(run.readyNodeIds)
496
+
497
+ const nodes: SerializablePlanNode[] = []
498
+ for (const nodeSpec of nodeSpecs) {
499
+ const nodeRun = nodeRunsById.get(nodeSpec.nodeId)
500
+ if (!nodeRun) {
501
+ return yield* new PlanRunSerializationError({
502
+ message: `Plan run ${recordIdToString(run.id, TABLES.PLAN_RUN)} is missing node run "${nodeSpec.nodeId}".`,
503
+ })
504
+ }
505
+
506
+ const isActiveOrReady = activeNodeIds.has(nodeSpec.nodeId) || readyNodeIds.has(nodeSpec.nodeId)
507
+
508
+ if (slim && !isActiveOrReady) {
509
+ nodes.push({
510
+ id: nodeSpec.nodeId,
511
+ type: nodeSpec.type,
512
+ label: nodeSpec.label,
513
+ owner: { executorType: nodeSpec.owner.executorType, ref: nodeSpec.owner.ref },
514
+ objective: nodeSpec.objective,
515
+ status: nodeRun.status,
516
+ upstreamNodeIds: [...nodeSpec.upstreamNodeIds],
517
+ downstreamNodeIds: [...nodeSpec.downstreamNodeIds],
518
+ ...(nodeRun.completedAt ? { completedAt: toOptionalIsoDateTimeString(nodeRun.completedAt) } : {}),
519
+ } as SerializablePlanNode)
520
+ continue
521
+ }
522
+
523
+ nodes.push({
524
+ id: nodeSpec.nodeId,
525
+ type: nodeSpec.type,
526
+ label: nodeSpec.label,
527
+ owner: nodeSpec.owner,
528
+ objective: nodeSpec.objective,
529
+ instructions: nodeSpec.instructions,
530
+ inputSchemaRef: nodeSpec.inputSchemaRef,
531
+ outputSchemaRef: nodeSpec.outputSchemaRef,
532
+ deliverables: [...nodeSpec.deliverables],
533
+ successCriteria: [...nodeSpec.successCriteria],
534
+ completionChecks: [...nodeSpec.completionChecks],
535
+ retryPolicy: { ...nodeSpec.retryPolicy, retryOn: [...nodeSpec.retryPolicy.retryOn] },
536
+ failurePolicy: [...nodeSpec.failurePolicy],
537
+ timeoutMs: nodeSpec.timeoutMs,
538
+ toolPolicy: { allow: [...nodeSpec.toolPolicy.allow], deny: [...nodeSpec.toolPolicy.deny] },
539
+ contextPolicy: {
540
+ retrievalScopes: [...nodeSpec.contextPolicy.retrievalScopes],
541
+ attachmentPolicy: nodeSpec.contextPolicy.attachmentPolicy,
542
+ webPolicy: nodeSpec.contextPolicy.webPolicy,
543
+ },
544
+ executionVisibility: nodeSpec.executionVisibility,
545
+ schedule: nodeSpec.schedule,
546
+ deadline: nodeSpec.deadline,
547
+ escalation: nodeSpec.escalation,
548
+ monitoringConfig: nodeSpec.monitoringConfig,
549
+ delayAfterPredecessorMs: nodeSpec.delayAfterPredecessorMs,
550
+ deliberationConfig: nodeSpec.deliberationConfig,
551
+ status: nodeRun.status,
552
+ attemptCount: nodeRun.attemptCount,
553
+ retryCount: nodeRun.retryCount,
554
+ resolvedInput: nodeRun.resolvedInput,
555
+ latestStructuredOutput: nodeRun.latestStructuredOutput,
556
+ latestNotes: nodeRun.latestNotes,
557
+ blockedReason: nodeRun.blockedReason,
558
+ failureClass: nodeRun.failureClass,
559
+ upstreamNodeIds: [...nodeSpec.upstreamNodeIds],
560
+ downstreamNodeIds: [...nodeSpec.downstreamNodeIds],
561
+ readyAt: toOptionalIsoDateTimeString(nodeRun.readyAt),
562
+ startedAt: toOptionalIsoDateTimeString(nodeRun.startedAt),
563
+ completedAt: toOptionalIsoDateTimeString(nodeRun.completedAt),
564
+ })
565
+ }
566
+
567
+ return {
568
+ specId: recordIdToString(spec.id, TABLES.PLAN_SPEC),
569
+ runId: recordIdToString(run.id, TABLES.PLAN_RUN),
570
+ threadId: recordIdToString(run.threadId, TABLES.THREAD),
571
+ sourceThreadId: run.sourceThreadId ? recordIdToString(run.sourceThreadId, TABLES.THREAD) : undefined,
572
+ organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
573
+ title: spec.title,
574
+ objective: spec.objective,
575
+ version: spec.version,
576
+ status: run.status,
577
+ leadAgentId: run.leadAgentId,
578
+ createdByAgentId: run.createdByAgentId,
579
+ defaultExecutionVisibility: spec.defaultExecutionVisibility,
580
+ executionMode: spec.executionMode,
581
+ schemaRegistry: slim ? {} : structuredClone(spec.schemaRegistry),
582
+ entryNodeIds: [...spec.entryNodeIds],
583
+ edges: [...spec.edges],
584
+ schedule: spec.schedule,
585
+ activeNodeIds: run.currentNodeId ? [run.currentNodeId] : [],
586
+ readyNodeIds: [...run.readyNodeIds],
587
+ waitingNodeId: run.waitingNodeId,
588
+ replacedRunId: run.replacedRunId ? recordIdToString(run.replacedRunId, TABLES.PLAN_RUN) : undefined,
589
+ failureCount: run.failureCount,
590
+ startedAt: toOptionalIsoDateTimeString(run.startedAt),
591
+ completedAt: toOptionalIsoDateTimeString(run.completedAt),
592
+ progress: buildProgress(nodeRuns),
593
+ nodes,
594
+ artifacts: artifacts.map(serializeArtifact),
595
+ lineageArtifacts,
596
+ validationIssues: validationIssues.map(serializeValidationIssue),
597
+ approvals: approvals.map(serializeApproval),
598
+ latestCheckpoint: latestCheckpoint ? serializeCheckpoint(latestCheckpoint) : null,
599
+ recentEvents: recentEvents.map(serializeEvent),
600
+ }
601
+ })
602
+
603
+ return {
604
+ getPlanSpecById: getPlanSpecByIdEffect,
605
+ listPlanSpecsByThread: listPlanSpecsByThreadEffect,
606
+ listRunsBySpec: listRunsBySpecEffect,
607
+ listNodeSpecs: listNodeSpecsEffect,
608
+ getNodeSpecByNodeId: getNodeSpecByNodeIdEffect,
609
+ getRunById: getRunByIdEffect,
610
+ getActiveRunRecord: getActiveRunRecordEffect,
611
+ getActiveRunRecords: getActiveRunRecordsEffect,
612
+ getRunsCreatedInContext: getRunsCreatedInContextEffect,
613
+ listNodeRuns: listNodeRunsEffect,
614
+ getNodeRunByNodeId: getNodeRunByNodeIdEffect,
615
+ listArtifacts: listArtifactsEffect,
616
+ listAttempts: listAttemptsEffect,
617
+ listValidationIssues: listValidationIssuesEffect,
618
+ listApprovals: listApprovalsEffect,
619
+ getLatestCheckpoint: getLatestCheckpointEffect,
620
+ getNextCheckpointSequence: getNextCheckpointSequenceEffect,
621
+ listEvents: listEventsEffect,
622
+ toSerializablePlan: toSerializablePlanEffect,
623
+ collectLineageArtifacts: collectLineageArtifactsEffect,
624
+ }
625
+ }
626
+
627
+ export class PlanRunServiceTag extends Context.Service<PlanRunServiceTag, ReturnType<typeof makePlanRunService>>()(
628
+ '@lota-sdk/core/PlanRunService',
629
+ ) {}
630
+
631
+ export const PlanRunServiceLive = Layer.effect(
632
+ PlanRunServiceTag,
633
+ Effect.gen(function* () {
634
+ const db = yield* DatabaseServiceTag
635
+ return makePlanRunService(db)
636
+ }),
637
+ )