@lota-sdk/core 0.4.7 → 0.4.9

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 (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -0,0 +1,475 @@
1
+ import type {
2
+ PlanEventRecord,
3
+ PlanNodeRunRecord,
4
+ PlanNodeSpecRecord,
5
+ PlanRunRecord,
6
+ PlanSpecRecord,
7
+ } from '@lota-sdk/shared'
8
+ import { PlanNodeRunSchema } from '@lota-sdk/shared'
9
+ import { Effect } from 'effect'
10
+
11
+ import type { RecordIdInput } from '../../db/record-id'
12
+ import { ensureRecordId, recordIdToString } from '../../db/record-id'
13
+ import type { DatabaseTransaction } from '../../db/service'
14
+ import { TABLES } from '../../db/tables'
15
+ import { NotFoundError } from '../../effect/errors'
16
+ import { runPromise } from '../../effect/runtime'
17
+ import { nowDate } from '../../utils/date-time'
18
+ import type { PlanExecutorContext } from './plan-executor-context'
19
+ import {
20
+ buildNodeContext,
21
+ buildResolvedInput,
22
+ evaluateCondition,
23
+ isHumanNodeType,
24
+ isStructuralNodeType,
25
+ isSuccessfulTerminalStatus,
26
+ toNodeRunData,
27
+ } from './plan-executor-helpers'
28
+ import { emitEvent, replaceRun } from './plan-executor-persistence'
29
+
30
+ const delayedNodePromotionQueueModule = Effect.tryPromise(() => import('../../queues/delayed-node-promotion.queue'))
31
+
32
+ export function syncRunGraph(
33
+ context: PlanExecutorContext,
34
+ params: {
35
+ tx: DatabaseTransaction
36
+ run: PlanRunRecord
37
+ spec: PlanSpecRecord
38
+ nodeSpecs: PlanNodeSpecRecord[]
39
+ nodeRuns: PlanNodeRunRecord[]
40
+ artifacts: Array<{
41
+ id: RecordIdInput
42
+ nodeId: string
43
+ name: string
44
+ kind: string
45
+ pointer: string
46
+ schemaRef?: string
47
+ payload?: unknown
48
+ }>
49
+ emittedBy: string
50
+ capturedEvents?: PlanEventRecord[]
51
+ },
52
+ ): Promise<{
53
+ run: PlanRunRecord
54
+ nodeRuns: PlanNodeRunRecord[]
55
+ artifacts: Array<{
56
+ id: RecordIdInput
57
+ nodeId: string
58
+ name: string
59
+ kind: string
60
+ pointer: string
61
+ schemaRef?: string
62
+ payload?: unknown
63
+ }>
64
+ }> {
65
+ return runPromise(
66
+ Effect.gen(function* () {
67
+ const { planApprovalService, planCoordinationService, planSchedulerService } = context
68
+ const currentTime = nowDate()
69
+ let currentRun = params.run
70
+ let currentNodeRuns = [...params.nodeRuns]
71
+ const currentArtifacts = [...params.artifacts]
72
+ const sortedNodeSpecs = [...params.nodeSpecs].sort((left, right) => left.position - right.position)
73
+ const dependencies = params.spec.dependencies
74
+
75
+ const updateNodeRun = (nodeRun: PlanNodeRunRecord, patch: Parameters<typeof toNodeRunData>[1]) =>
76
+ params.tx
77
+ .update(ensureRecordId(nodeRun.id, TABLES.PLAN_NODE_RUN))
78
+ .content(toNodeRunData(nodeRun, patch))
79
+ .output('after')
80
+
81
+ const replaceNodeRun = (nextNodeRun: PlanNodeRunRecord) => {
82
+ currentNodeRuns = currentNodeRuns.map((candidate) =>
83
+ candidate.nodeId === nextNodeRun.nodeId ? nextNodeRun : candidate,
84
+ )
85
+ }
86
+
87
+ const getNodeRunsById = () => new Map(currentNodeRuns.map((nodeRun) => [nodeRun.nodeId, nodeRun]))
88
+ const getArtifactsByNodeId = () =>
89
+ currentArtifacts.reduce((groups, artifact) => {
90
+ const list = groups.get(artifact.nodeId) ?? []
91
+ list.push(artifact)
92
+ groups.set(artifact.nodeId, list)
93
+ return groups
94
+ }, new Map<string, typeof currentArtifacts>())
95
+
96
+ if (dependencies && dependencies.length > 0) {
97
+ const { unresolved } = yield* planCoordinationService.resolveDependencies({
98
+ dependencies,
99
+ threadId: recordIdToString(params.spec.threadId, TABLES.THREAD),
100
+ })
101
+ if (unresolved.length > 0) {
102
+ currentRun = yield* replaceRun(params.tx, currentRun, { status: 'blocked', readyNodeIds: [] })
103
+ yield* emitEvent({
104
+ tx: params.tx,
105
+ run: currentRun,
106
+ spec: params.spec,
107
+ eventType: 'run-status-changed',
108
+ fromStatus: params.run.status,
109
+ toStatus: currentRun.status,
110
+ message: `Run blocked: unresolved cross-plan dependencies (${unresolved.map((d) => d.sourcePlanSpecId).join(', ')}).`,
111
+ emittedBy: params.emittedBy,
112
+ capturedEvents: params.capturedEvents,
113
+ })
114
+ return { run: currentRun, nodeRuns: currentNodeRuns, artifacts: currentArtifacts }
115
+ }
116
+ }
117
+
118
+ let changed = true
119
+ while (changed) {
120
+ changed = false
121
+ const nodeRunsById = getNodeRunsById()
122
+ const artifactsByNodeId = getArtifactsByNodeId()
123
+
124
+ for (const nodeSpec of sortedNodeSpecs) {
125
+ const nodeRun = nodeRunsById.get(nodeSpec.nodeId)
126
+ if (!nodeRun || nodeRun.status !== 'pending') continue
127
+
128
+ const upstreamRuns = nodeSpec.upstreamNodeIds
129
+ .map((nodeId) => nodeRunsById.get(nodeId))
130
+ .filter(Boolean) as PlanNodeRunRecord[]
131
+ if (
132
+ nodeSpec.upstreamNodeIds.length > 0 &&
133
+ !upstreamRuns.every((upstreamRun) => isSuccessfulTerminalStatus(upstreamRun.status))
134
+ ) {
135
+ continue
136
+ }
137
+
138
+ const activeIncomingEdges: typeof params.spec.edges = []
139
+ for (const edge of params.spec.edges) {
140
+ if (edge.target !== nodeSpec.nodeId) continue
141
+ const sourceRun = nodeRunsById.get(edge.source)
142
+ if (!sourceRun) continue
143
+ const context = buildNodeContext({
144
+ nodeRun: sourceRun,
145
+ artifacts: artifactsByNodeId.get(edge.source) ?? [],
146
+ })
147
+ if (yield* evaluateCondition(edge.when, context)) {
148
+ activeIncomingEdges.push(edge)
149
+ }
150
+ }
151
+
152
+ if (nodeSpec.upstreamNodeIds.length > 0 && activeIncomingEdges.length === 0) {
153
+ const skippedNodeRun = PlanNodeRunSchema.parse(
154
+ yield* updateNodeRun(nodeRun, {
155
+ status: 'skipped',
156
+ completedAt: currentTime,
157
+ blockedReason: null,
158
+ failureClass: null,
159
+ }),
160
+ )
161
+ replaceNodeRun(skippedNodeRun)
162
+ yield* emitEvent({
163
+ tx: params.tx,
164
+ run: currentRun,
165
+ spec: params.spec,
166
+ nodeId: skippedNodeRun.nodeId,
167
+ eventType: 'node-skipped',
168
+ fromStatus: nodeRun.status,
169
+ toStatus: skippedNodeRun.status,
170
+ message: `Node "${nodeSpec.label}" was skipped because no inbound branch was activated.`,
171
+ emittedBy: params.emittedBy,
172
+ capturedEvents: params.capturedEvents,
173
+ })
174
+ changed = true
175
+ continue
176
+ }
177
+
178
+ const resolvedInput = yield* buildResolvedInput({
179
+ spec: params.spec,
180
+ nodeSpec,
181
+ nodeRunsById,
182
+ artifactsByNodeId,
183
+ })
184
+
185
+ const nodeSchedule = nodeSpec.schedule
186
+ const hasNonImmediateSchedule = nodeSchedule && nodeSchedule.type !== 'immediate'
187
+
188
+ if (hasNonImmediateSchedule) {
189
+ const scheduledNodeRun = PlanNodeRunSchema.parse(
190
+ yield* updateNodeRun(nodeRun, { status: 'scheduled', resolvedInput, scheduledAt: currentTime }),
191
+ )
192
+ replaceNodeRun(scheduledNodeRun)
193
+ yield* planSchedulerService.createSchedule({
194
+ organizationId: currentRun.organizationId,
195
+ threadId: currentRun.threadId,
196
+ planSpecId: params.spec.id,
197
+ runId: currentRun.id,
198
+ nodeId: nodeSpec.nodeId,
199
+ scheduleSpec: nodeSchedule,
200
+ })
201
+ yield* emitEvent({
202
+ tx: params.tx,
203
+ run: currentRun,
204
+ spec: params.spec,
205
+ nodeId: scheduledNodeRun.nodeId,
206
+ eventType: 'node-scheduled',
207
+ fromStatus: nodeRun.status,
208
+ toStatus: scheduledNodeRun.status,
209
+ message: `Node "${nodeSpec.label}" is scheduled (${nodeSchedule.type}).`,
210
+ emittedBy: params.emittedBy,
211
+ capturedEvents: params.capturedEvents,
212
+ })
213
+ changed = true
214
+ } else if (nodeSpec.delayAfterPredecessorMs) {
215
+ const delayAfterPredecessorMs = nodeSpec.delayAfterPredecessorMs
216
+ const { enqueueDelayedNodePromotion } = yield* delayedNodePromotionQueueModule
217
+ const scheduledNodeRun = PlanNodeRunSchema.parse(
218
+ yield* updateNodeRun(nodeRun, { status: 'scheduled', resolvedInput, scheduledAt: currentTime }),
219
+ )
220
+ replaceNodeRun(scheduledNodeRun)
221
+ yield* Effect.tryPromise(() =>
222
+ enqueueDelayedNodePromotion(
223
+ {
224
+ runId: recordIdToString(currentRun.id, TABLES.PLAN_RUN),
225
+ nodeId: nodeSpec.nodeId,
226
+ emittedBy: params.emittedBy,
227
+ },
228
+ delayAfterPredecessorMs,
229
+ ),
230
+ )
231
+ yield* emitEvent({
232
+ tx: params.tx,
233
+ run: currentRun,
234
+ spec: params.spec,
235
+ nodeId: scheduledNodeRun.nodeId,
236
+ eventType: 'node-scheduled',
237
+ fromStatus: nodeRun.status,
238
+ toStatus: scheduledNodeRun.status,
239
+ message: `Node "${nodeSpec.label}" is delayed by ${delayAfterPredecessorMs}ms after predecessor.`,
240
+ emittedBy: params.emittedBy,
241
+ capturedEvents: params.capturedEvents,
242
+ })
243
+ changed = true
244
+ } else {
245
+ const readyNodeRun = PlanNodeRunSchema.parse(
246
+ yield* updateNodeRun(nodeRun, { status: 'ready', resolvedInput, readyAt: currentTime }),
247
+ )
248
+ replaceNodeRun(readyNodeRun)
249
+ yield* emitEvent({
250
+ tx: params.tx,
251
+ run: currentRun,
252
+ spec: params.spec,
253
+ nodeId: readyNodeRun.nodeId,
254
+ eventType: 'node-ready',
255
+ fromStatus: nodeRun.status,
256
+ toStatus: readyNodeRun.status,
257
+ message: `Node "${nodeSpec.label}" is ready to execute.`,
258
+ emittedBy: params.emittedBy,
259
+ capturedEvents: params.capturedEvents,
260
+ })
261
+ changed = true
262
+ }
263
+ }
264
+
265
+ const readyStructuralNodes = sortedNodeSpecs.filter((nodeSpec) => {
266
+ const nodeRun = getNodeRunsById().get(nodeSpec.nodeId)
267
+ return nodeRun?.status === 'ready' && isStructuralNodeType(nodeSpec.type)
268
+ })
269
+
270
+ for (const nodeSpec of readyStructuralNodes) {
271
+ const nodeRun = getNodeRunsById().get(nodeSpec.nodeId)
272
+ if (!nodeRun) continue
273
+
274
+ const completedNodeRun = PlanNodeRunSchema.parse(
275
+ yield* updateNodeRun(nodeRun, {
276
+ status: 'completed',
277
+ startedAt: nodeRun.startedAt ?? currentTime,
278
+ completedAt: currentTime,
279
+ }),
280
+ )
281
+ replaceNodeRun(completedNodeRun)
282
+ yield* emitEvent({
283
+ tx: params.tx,
284
+ run: currentRun,
285
+ spec: params.spec,
286
+ nodeId: completedNodeRun.nodeId,
287
+ eventType: 'node-auto-completed',
288
+ fromStatus: nodeRun.status,
289
+ toStatus: completedNodeRun.status,
290
+ message: `Structural node "${nodeSpec.label}" auto-completed.`,
291
+ emittedBy: params.emittedBy,
292
+ capturedEvents: params.capturedEvents,
293
+ })
294
+ changed = true
295
+ }
296
+ }
297
+
298
+ const nodeRunsById = getNodeRunsById()
299
+ const activeRunningNode = currentNodeRuns.find((nodeRun) => nodeRun.status === 'running')
300
+ const activeHumanNode = currentNodeRuns.find((nodeRun) => nodeRun.status === 'awaiting-human')
301
+ const activeMonitoringNode = currentNodeRuns.find((nodeRun) => nodeRun.status === 'monitoring')
302
+ const hasScheduledOrMonitoring = currentNodeRuns.some(
303
+ (nodeRun) => nodeRun.status === 'scheduled' || nodeRun.status === 'monitoring',
304
+ )
305
+
306
+ if (!activeRunningNode && !activeHumanNode && !activeMonitoringNode) {
307
+ const nextHumanNodeSpec = sortedNodeSpecs.find((nodeSpec) => {
308
+ const nodeRun = nodeRunsById.get(nodeSpec.nodeId)
309
+ return nodeRun?.status === 'ready' && isHumanNodeType(nodeSpec.type)
310
+ })
311
+
312
+ if (nextHumanNodeSpec) {
313
+ const nodeRun = nodeRunsById.get(nextHumanNodeSpec.nodeId)
314
+ if (!nodeRun) {
315
+ return yield* new NotFoundError({
316
+ resource: 'plan node run',
317
+ id: nextHumanNodeSpec.nodeId,
318
+ message: `Expected ready node run for "${nextHumanNodeSpec.nodeId}".`,
319
+ })
320
+ }
321
+ const awaitingHumanNodeRun = PlanNodeRunSchema.parse(
322
+ yield* updateNodeRun(nodeRun, { status: 'awaiting-human', startedAt: nodeRun.startedAt ?? currentTime }),
323
+ )
324
+ replaceNodeRun(awaitingHumanNodeRun)
325
+
326
+ const approval = yield* planApprovalService.createPendingApproval({
327
+ tx: params.tx,
328
+ runId: currentRun.id,
329
+ nodeRunId: awaitingHumanNodeRun.id,
330
+ nodeId: awaitingHumanNodeRun.nodeId,
331
+ requestedBy: params.emittedBy,
332
+ presented: {
333
+ nodeId: nextHumanNodeSpec.nodeId,
334
+ label: nextHumanNodeSpec.label,
335
+ objective: nextHumanNodeSpec.objective,
336
+ instructions: nextHumanNodeSpec.instructions,
337
+ deliverables: nextHumanNodeSpec.deliverables,
338
+ successCriteria: nextHumanNodeSpec.successCriteria,
339
+ resolvedInput: awaitingHumanNodeRun.resolvedInput ?? {},
340
+ },
341
+ })
342
+
343
+ currentRun = yield* replaceRun(params.tx, currentRun, {
344
+ status: 'awaiting-human',
345
+ currentNodeId: awaitingHumanNodeRun.nodeId,
346
+ waitingNodeId: awaitingHumanNodeRun.nodeId,
347
+ readyNodeIds: currentNodeRuns
348
+ .filter((candidate) => candidate.status === 'ready' && candidate.nodeId !== awaitingHumanNodeRun.nodeId)
349
+ .map((candidate) => candidate.nodeId),
350
+ })
351
+
352
+ yield* emitEvent({
353
+ tx: params.tx,
354
+ run: currentRun,
355
+ spec: params.spec,
356
+ nodeId: awaitingHumanNodeRun.nodeId,
357
+ approvalId: approval.id,
358
+ eventType: 'approval-requested',
359
+ fromStatus: params.run.status,
360
+ toStatus: currentRun.status,
361
+ message: `Node "${nextHumanNodeSpec.label}" is awaiting human input.`,
362
+ emittedBy: params.emittedBy,
363
+ capturedEvents: params.capturedEvents,
364
+ })
365
+ } else {
366
+ const nextActionNodeSpec = sortedNodeSpecs.find((nodeSpec) => {
367
+ const nodeRun = nodeRunsById.get(nodeSpec.nodeId)
368
+ return nodeRun?.status === 'ready' && !isStructuralNodeType(nodeSpec.type)
369
+ })
370
+
371
+ if (nextActionNodeSpec) {
372
+ const nodeRun = nodeRunsById.get(nextActionNodeSpec.nodeId)
373
+ if (!nodeRun) {
374
+ return yield* new NotFoundError({
375
+ resource: 'plan node run',
376
+ id: nextActionNodeSpec.nodeId,
377
+ message: `Expected ready node run for "${nextActionNodeSpec.nodeId}".`,
378
+ })
379
+ }
380
+ const runningNodeRun = PlanNodeRunSchema.parse(
381
+ yield* updateNodeRun(nodeRun, { status: 'running', startedAt: nodeRun.startedAt ?? currentTime }),
382
+ )
383
+ replaceNodeRun(runningNodeRun)
384
+
385
+ currentRun = yield* replaceRun(params.tx, currentRun, {
386
+ status: 'running',
387
+ currentNodeId: runningNodeRun.nodeId,
388
+ waitingNodeId: null,
389
+ readyNodeIds: currentNodeRuns
390
+ .filter((candidate) => candidate.status === 'ready' && candidate.nodeId !== runningNodeRun.nodeId)
391
+ .map((candidate) => candidate.nodeId),
392
+ })
393
+
394
+ yield* emitEvent({
395
+ tx: params.tx,
396
+ run: currentRun,
397
+ spec: params.spec,
398
+ nodeId: runningNodeRun.nodeId,
399
+ eventType: 'node-running',
400
+ fromStatus: nodeRun.status,
401
+ toStatus: runningNodeRun.status,
402
+ message: `Node "${nextActionNodeSpec.label}" is now running.`,
403
+ emittedBy: params.emittedBy,
404
+ capturedEvents: params.capturedEvents,
405
+ })
406
+ yield* emitEvent({
407
+ tx: params.tx,
408
+ run: currentRun,
409
+ spec: params.spec,
410
+ nodeId: runningNodeRun.nodeId,
411
+ eventType: 'ownership-transition',
412
+ message: `Execution ownership transitioned to "${nextActionNodeSpec.label}".`,
413
+ detail: {
414
+ owner: nextActionNodeSpec.owner,
415
+ fromNodeId: params.run.currentNodeId ?? null,
416
+ toNodeId: runningNodeRun.nodeId,
417
+ },
418
+ emittedBy: params.emittedBy,
419
+ capturedEvents: params.capturedEvents,
420
+ })
421
+ } else if (currentNodeRuns.every((nodeRun) => isSuccessfulTerminalStatus(nodeRun.status))) {
422
+ currentRun = yield* replaceRun(params.tx, currentRun, {
423
+ status: 'completed',
424
+ currentNodeId: null,
425
+ waitingNodeId: null,
426
+ readyNodeIds: [],
427
+ completedAt: currentTime,
428
+ })
429
+
430
+ yield* emitEvent({
431
+ tx: params.tx,
432
+ run: currentRun,
433
+ spec: params.spec,
434
+ eventType: 'run-status-changed',
435
+ fromStatus: params.run.status,
436
+ toStatus: currentRun.status,
437
+ message: `Run "${params.spec.title}" completed.`,
438
+ emittedBy: params.emittedBy,
439
+ capturedEvents: params.capturedEvents,
440
+ })
441
+ } else if (hasScheduledOrMonitoring) {
442
+ currentRun = yield* replaceRun(params.tx, currentRun, {
443
+ status: 'running',
444
+ currentNodeId: null,
445
+ waitingNodeId: null,
446
+ readyNodeIds: currentNodeRuns
447
+ .filter((candidate) => candidate.status === 'ready')
448
+ .map((candidate) => candidate.nodeId),
449
+ })
450
+ } else {
451
+ currentRun = yield* replaceRun(params.tx, currentRun, {
452
+ status: 'blocked',
453
+ currentNodeId: null,
454
+ waitingNodeId: null,
455
+ readyNodeIds: currentNodeRuns
456
+ .filter((candidate) => candidate.status === 'ready')
457
+ .map((candidate) => candidate.nodeId),
458
+ })
459
+ }
460
+ }
461
+ } else {
462
+ currentRun = yield* replaceRun(params.tx, currentRun, {
463
+ status: activeHumanNode ? 'awaiting-human' : 'running',
464
+ currentNodeId: activeHumanNode?.nodeId ?? activeMonitoringNode?.nodeId ?? activeRunningNode?.nodeId ?? null,
465
+ waitingNodeId: activeHumanNode?.nodeId ?? null,
466
+ readyNodeIds: currentNodeRuns
467
+ .filter((candidate) => candidate.status === 'ready')
468
+ .map((candidate) => candidate.nodeId),
469
+ })
470
+ }
471
+
472
+ return { run: currentRun, nodeRuns: currentNodeRuns, artifacts: currentArtifacts }
473
+ }),
474
+ )
475
+ }