@lota-sdk/core 0.4.9 → 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.
- package/package.json +2 -2
- package/src/ai/embedding-cache.ts +3 -1
- package/src/ai-gateway/ai-gateway.ts +38 -10
- package/src/config/agent-defaults.ts +22 -9
- package/src/config/agent-types.ts +1 -1
- package/src/config/background-processing.ts +1 -1
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +20 -7
- package/src/config/thread-defaults.ts +12 -4
- package/src/create-runtime.ts +69 -656
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.ts +29 -20
- package/src/db/memory.ts +188 -195
- package/src/db/service-normalization.ts +97 -64
- package/src/db/service.ts +706 -538
- package/src/db/startup.ts +30 -19
- package/src/effect/awaitable-effect.ts +46 -37
- package/src/effect/helpers.ts +30 -5
- package/src/effect/index.ts +7 -5
- package/src/effect/layers.ts +82 -72
- package/src/effect/runtime.ts +18 -3
- package/src/effect/services.ts +15 -11
- package/src/embeddings/provider.ts +65 -66
- package/src/index.ts +13 -11
- package/src/queues/autonomous-job.queue.ts +59 -71
- package/src/queues/context-compaction.queue.ts +6 -18
- package/src/queues/delayed-node-promotion.queue.ts +9 -17
- package/src/queues/organization-learning.queue.ts +17 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +23 -20
- package/src/queues/plan-scheduler.queue.ts +6 -18
- package/src/queues/post-chat-memory.queue.ts +6 -18
- package/src/queues/queue-factory.ts +128 -50
- package/src/queues/title-generation.queue.ts +6 -17
- package/src/redis/connection.ts +181 -164
- package/src/redis/runtime-connection.ts +13 -3
- package/src/redis/stream-context.ts +17 -9
- package/src/runtime/agent-runtime-policy.ts +1 -1
- package/src/runtime/agent-stream-helpers.ts +15 -11
- package/src/runtime/chat-run-orchestration.ts +1 -1
- package/src/runtime/context-compaction/context-compaction-runtime.ts +1 -1
- package/src/runtime/context-compaction/context-compaction.ts +126 -82
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/graph-designer.ts +15 -7
- package/src/runtime/helper-model.ts +8 -4
- package/src/runtime/index.ts +0 -1
- package/src/runtime/memory/memory-block.ts +19 -9
- package/src/runtime/memory/memory-pipeline.ts +53 -66
- package/src/runtime/memory/memory-scope.ts +33 -29
- package/src/runtime/plugin-resolution.ts +33 -54
- package/src/runtime/post-turn-side-effects.ts +6 -26
- package/src/runtime/retrieval-adapters.ts +4 -4
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +3 -3
- package/src/runtime/runtime-extensions.ts +20 -9
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +7 -5
- package/src/runtime/social-chat/social-chat-history.ts +21 -12
- package/src/runtime/social-chat/social-chat.ts +401 -365
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +58 -52
- package/src/runtime/thread-turn-context.ts +21 -27
- package/src/services/agent-activity.service.ts +1 -1
- package/src/services/agent-executor.service.ts +179 -187
- package/src/services/artifact.service.ts +10 -5
- package/src/services/attachment.service.ts +35 -1
- package/src/services/autonomous-job.service.ts +58 -56
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +3 -1
- package/src/services/context-compaction.service.ts +1 -1
- package/src/services/document-chunk.service.ts +8 -17
- package/src/services/execution-plan/execution-plan-graph.ts +74 -52
- package/src/services/execution-plan/execution-plan.service.ts +1 -1
- package/src/services/feedback-loop.service.ts +1 -1
- package/src/services/global-orchestrator.service.ts +33 -10
- package/src/services/graph-full-routing.ts +44 -33
- package/src/services/index.ts +1 -0
- package/src/services/institutional-memory.service.ts +8 -17
- package/src/services/learned-skill.service.ts +38 -35
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +14 -3
- package/src/services/memory/memory-preseeded.ts +10 -4
- package/src/services/memory/memory-utils.ts +2 -1
- package/src/services/memory/memory.service.ts +26 -44
- package/src/services/memory/rerank.service.ts +3 -11
- package/src/services/monitoring-window.service.ts +1 -1
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/node-workspace.service.ts +2 -2
- package/src/services/notification.service.ts +16 -4
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +34 -51
- package/src/services/ownership-dispatcher.service.ts +132 -90
- package/src/services/plan/plan-agent-heartbeat.service.ts +1 -1
- package/src/services/plan/plan-agent-query.service.ts +1 -1
- package/src/services/plan/plan-approval.service.ts +52 -48
- package/src/services/plan/plan-artifact.service.ts +2 -2
- package/src/services/plan/plan-builder.service.ts +2 -2
- package/src/services/plan/plan-checkpoint.service.ts +1 -1
- package/src/services/plan/plan-compiler.service.ts +1 -1
- package/src/services/plan/plan-completion-side-effects.ts +18 -24
- package/src/services/plan/plan-coordination.service.ts +1 -1
- package/src/services/plan/plan-cycle.service.ts +171 -164
- package/src/services/plan/plan-deadline.service.ts +290 -304
- package/src/services/plan/plan-event-delivery.service.ts +44 -39
- package/src/services/plan/plan-executor-graph.ts +114 -67
- package/src/services/plan/plan-executor-helpers.ts +60 -75
- package/src/services/plan/plan-executor.service.ts +550 -467
- package/src/services/plan/plan-run.service.ts +12 -19
- package/src/services/plan/plan-scheduler.service.ts +27 -33
- package/src/services/plan/plan-template.service.ts +1 -1
- package/src/services/plan/plan-transaction-events.ts +8 -5
- package/src/services/plan/plan-validator.service.ts +1 -1
- package/src/services/plan/plan-workspace.service.ts +17 -11
- package/src/services/plugin-executor.service.ts +26 -21
- package/src/services/quality-metrics.service.ts +1 -1
- package/src/services/queue-job.service.ts +8 -17
- package/src/services/recent-activity-title.service.ts +17 -9
- package/src/services/recent-activity.service.ts +1 -1
- package/src/services/skill-resolver.service.ts +1 -1
- package/src/services/social-chat-history.service.ts +37 -20
- package/src/services/system-executor.service.ts +25 -20
- package/src/services/thread/thread-bootstrap.ts +26 -10
- package/src/services/thread/thread-listing.ts +2 -1
- package/src/services/thread/thread-memory-block.ts +18 -5
- package/src/services/thread/thread-message.service.ts +24 -8
- package/src/services/thread/thread-title.service.ts +1 -1
- package/src/services/thread/thread-turn-execution.ts +1 -1
- package/src/services/thread/thread-turn-preparation.service.ts +18 -16
- package/src/services/thread/thread-turn-streaming.ts +12 -11
- package/src/services/thread/thread-turn.ts +43 -10
- package/src/services/thread/thread.service.ts +11 -2
- package/src/services/user.service.ts +1 -1
- package/src/services/write-intent-validator.service.ts +1 -1
- package/src/storage/attachment-storage.service.ts +7 -4
- package/src/storage/generated-document-storage.service.ts +1 -1
- package/src/system-agents/context-compaction.agent.ts +1 -1
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/skill-extractor.agent.ts +1 -1
- package/src/system-agents/skill-manager.agent.ts +1 -1
- package/src/system-agents/title-generator.agent.ts +1 -1
- package/src/tools/execution-plan.tool.ts +28 -17
- package/src/tools/fetch-webpage.tool.ts +20 -13
- package/src/tools/firecrawl-client.ts +13 -3
- package/src/tools/plan-approval.tool.ts +9 -1
- package/src/tools/search-web.tool.ts +16 -9
- package/src/tools/team-think.tool.ts +2 -2
- package/src/utils/async.ts +15 -6
- package/src/utils/errors.ts +27 -15
- package/src/workers/bootstrap.ts +25 -48
- package/src/workers/organization-learning.worker.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +25 -15
- package/src/workers/worker-utils.ts +20 -2
- package/src/config/search.ts +0 -3
- package/src/runtime/agent-types.ts +0 -1
|
@@ -187,67 +187,72 @@ export function makePlanEventDeliveryService(deps: PlanEventDeliveryDeps) {
|
|
|
187
187
|
})
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
190
|
+
const dispatchEventEffect = Effect.fn('PlanEventDelivery.dispatchEvent')(function* (event: PlanEventRecord) {
|
|
191
|
+
const eventId = recordIdToString(event.id, TABLES.PLAN_EVENT)
|
|
192
|
+
const deliveredKey = buildDeliveredKey(eventId)
|
|
193
|
+
const exists = yield* Effect.tryPromise({
|
|
194
|
+
try: () => redis.getConnection().exists(deliveredKey),
|
|
195
|
+
catch: (error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error }),
|
|
196
|
+
})
|
|
197
|
+
if (exists) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
201
200
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
})
|
|
201
|
+
yield* deliverEvent(event)
|
|
202
|
+
yield* Effect.tryPromise({
|
|
203
|
+
try: () => redis.getConnection().set(deliveredKey, '1', 'PX', PLAN_EVENT_DELIVERED_TTL_MS),
|
|
204
|
+
catch: (error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error }),
|
|
207
205
|
})
|
|
208
|
-
}
|
|
206
|
+
})
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
208
|
+
const dispatchUndeliveredEventsEffect = Effect.fn('PlanEventDelivery.dispatchUndeliveredEvents')(function* (
|
|
209
|
+
runId: string,
|
|
210
|
+
options?: { limit?: number },
|
|
211
|
+
) {
|
|
212
|
+
const run = yield* planRunService
|
|
213
|
+
.getRunById(runId)
|
|
214
|
+
.pipe(Effect.mapError((error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error })))
|
|
215
|
+
const events = yield* planRunService
|
|
216
|
+
.listEvents(run.id, options?.limit ?? 200)
|
|
217
|
+
.pipe(Effect.mapError((error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error })))
|
|
218
218
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
219
|
+
for (const event of events) {
|
|
220
|
+
yield* dispatchEventEffect(event)
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const dispatchEventsEffect = Effect.fn('PlanEventDelivery.dispatchEvents')(function* (events: PlanEventRecord[]) {
|
|
225
|
+
const runIds = new Set(events.map((event) => recordIdToString(event.runId, TABLES.PLAN_RUN)))
|
|
226
|
+
for (const runId of runIds) {
|
|
227
|
+
yield* dispatchUndeliveredEventsEffect(runId)
|
|
228
|
+
}
|
|
229
|
+
})
|
|
224
230
|
|
|
225
231
|
return {
|
|
226
232
|
dispatchEvents(events: PlanEventRecord[]): Promise<void> {
|
|
227
|
-
return runPromise(
|
|
228
|
-
Effect.gen(function* () {
|
|
229
|
-
const runIds = new Set(events.map((event) => recordIdToString(event.runId, TABLES.PLAN_RUN)))
|
|
230
|
-
for (const runId of runIds) {
|
|
231
|
-
yield* dispatchUndeliveredEventsEffect(runId)
|
|
232
|
-
}
|
|
233
|
-
}),
|
|
234
|
-
)
|
|
233
|
+
return runPromise(dispatchEventsEffect(events))
|
|
235
234
|
},
|
|
236
235
|
|
|
236
|
+
dispatchEventsEffect,
|
|
237
|
+
|
|
237
238
|
dispatchEvent(event: PlanEventRecord): Promise<void> {
|
|
238
239
|
return runPromise(dispatchEventEffect(event))
|
|
239
240
|
},
|
|
240
241
|
|
|
242
|
+
dispatchEventEffect,
|
|
243
|
+
|
|
241
244
|
dispatchUndeliveredEvents(runId: string, options?: { limit?: number }): Promise<void> {
|
|
242
245
|
return runPromise(dispatchUndeliveredEventsEffect(runId, options))
|
|
243
246
|
},
|
|
247
|
+
|
|
248
|
+
dispatchUndeliveredEventsEffect,
|
|
244
249
|
}
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
export class PlanEventDeliveryServiceTag extends Context.Service<
|
|
248
253
|
PlanEventDeliveryServiceTag,
|
|
249
254
|
ReturnType<typeof makePlanEventDeliveryService>
|
|
250
|
-
>()('PlanEventDeliveryService') {}
|
|
255
|
+
>()('@lota-sdk/core/PlanEventDeliveryService') {}
|
|
251
256
|
|
|
252
257
|
export const PlanEventDeliveryServiceLive = Layer.effect(
|
|
253
258
|
PlanEventDeliveryServiceTag,
|
|
@@ -6,7 +6,8 @@ import type {
|
|
|
6
6
|
PlanSpecRecord,
|
|
7
7
|
} from '@lota-sdk/shared'
|
|
8
8
|
import { PlanNodeRunSchema } from '@lota-sdk/shared'
|
|
9
|
-
import { Effect } from 'effect'
|
|
9
|
+
import { Effect, Match, Schema } from 'effect'
|
|
10
|
+
import type { z } from 'zod'
|
|
10
11
|
|
|
11
12
|
import type { RecordIdInput } from '../../db/record-id'
|
|
12
13
|
import { ensureRecordId, recordIdToString } from '../../db/record-id'
|
|
@@ -29,6 +30,22 @@ import { emitEvent, replaceRun } from './plan-executor-persistence'
|
|
|
29
30
|
|
|
30
31
|
const delayedNodePromotionQueueModule = Effect.tryPromise(() => import('../../queues/delayed-node-promotion.queue'))
|
|
31
32
|
|
|
33
|
+
class PlanExecutorGraphError extends Schema.TaggedErrorClass<PlanExecutorGraphError>()('PlanExecutorGraphError', {
|
|
34
|
+
message: Schema.String,
|
|
35
|
+
cause: Schema.optional(Schema.Defect),
|
|
36
|
+
}) {}
|
|
37
|
+
|
|
38
|
+
function parseRowOrFail<T>(
|
|
39
|
+
schema: z.ZodType<T>,
|
|
40
|
+
value: unknown,
|
|
41
|
+
operation: string,
|
|
42
|
+
): Effect.Effect<T, PlanExecutorGraphError> {
|
|
43
|
+
return Effect.try({
|
|
44
|
+
try: () => schema.parse(value),
|
|
45
|
+
catch: (cause) => new PlanExecutorGraphError({ message: `Failed to parse ${operation} row`, cause }),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
export function syncRunGraph(
|
|
33
50
|
context: PlanExecutorContext,
|
|
34
51
|
params: {
|
|
@@ -150,14 +167,13 @@ export function syncRunGraph(
|
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
if (nodeSpec.upstreamNodeIds.length > 0 && activeIncomingEdges.length === 0) {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
170
|
+
const skippedNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
171
|
+
status: 'skipped',
|
|
172
|
+
completedAt: currentTime,
|
|
173
|
+
blockedReason: null,
|
|
174
|
+
failureClass: null,
|
|
175
|
+
})
|
|
176
|
+
const skippedNodeRun = yield* parseRowOrFail(PlanNodeRunSchema, skippedNodeRunRow, 'plan node run')
|
|
161
177
|
replaceNodeRun(skippedNodeRun)
|
|
162
178
|
yield* emitEvent({
|
|
163
179
|
tx: params.tx,
|
|
@@ -186,9 +202,12 @@ export function syncRunGraph(
|
|
|
186
202
|
const hasNonImmediateSchedule = nodeSchedule && nodeSchedule.type !== 'immediate'
|
|
187
203
|
|
|
188
204
|
if (hasNonImmediateSchedule) {
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
205
|
+
const scheduledNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
206
|
+
status: 'scheduled',
|
|
207
|
+
resolvedInput,
|
|
208
|
+
scheduledAt: currentTime,
|
|
209
|
+
})
|
|
210
|
+
const scheduledNodeRun = yield* parseRowOrFail(PlanNodeRunSchema, scheduledNodeRunRow, 'plan node run')
|
|
192
211
|
replaceNodeRun(scheduledNodeRun)
|
|
193
212
|
yield* planSchedulerService.createSchedule({
|
|
194
213
|
organizationId: currentRun.organizationId,
|
|
@@ -214,9 +233,12 @@ export function syncRunGraph(
|
|
|
214
233
|
} else if (nodeSpec.delayAfterPredecessorMs) {
|
|
215
234
|
const delayAfterPredecessorMs = nodeSpec.delayAfterPredecessorMs
|
|
216
235
|
const { enqueueDelayedNodePromotion } = yield* delayedNodePromotionQueueModule
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
236
|
+
const scheduledNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
237
|
+
status: 'scheduled',
|
|
238
|
+
resolvedInput,
|
|
239
|
+
scheduledAt: currentTime,
|
|
240
|
+
})
|
|
241
|
+
const scheduledNodeRun = yield* parseRowOrFail(PlanNodeRunSchema, scheduledNodeRunRow, 'plan node run')
|
|
220
242
|
replaceNodeRun(scheduledNodeRun)
|
|
221
243
|
yield* Effect.tryPromise(() =>
|
|
222
244
|
enqueueDelayedNodePromotion(
|
|
@@ -242,9 +264,12 @@ export function syncRunGraph(
|
|
|
242
264
|
})
|
|
243
265
|
changed = true
|
|
244
266
|
} else {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
267
|
+
const readyNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
268
|
+
status: 'ready',
|
|
269
|
+
resolvedInput,
|
|
270
|
+
readyAt: currentTime,
|
|
271
|
+
})
|
|
272
|
+
const readyNodeRun = yield* parseRowOrFail(PlanNodeRunSchema, readyNodeRunRow, 'plan node run')
|
|
248
273
|
replaceNodeRun(readyNodeRun)
|
|
249
274
|
yield* emitEvent({
|
|
250
275
|
tx: params.tx,
|
|
@@ -271,13 +296,12 @@ export function syncRunGraph(
|
|
|
271
296
|
const nodeRun = getNodeRunsById().get(nodeSpec.nodeId)
|
|
272
297
|
if (!nodeRun) continue
|
|
273
298
|
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
)
|
|
299
|
+
const completedNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
300
|
+
status: 'completed',
|
|
301
|
+
startedAt: nodeRun.startedAt ?? currentTime,
|
|
302
|
+
completedAt: currentTime,
|
|
303
|
+
})
|
|
304
|
+
const completedNodeRun = yield* parseRowOrFail(PlanNodeRunSchema, completedNodeRunRow, 'plan node run')
|
|
281
305
|
replaceNodeRun(completedNodeRun)
|
|
282
306
|
yield* emitEvent({
|
|
283
307
|
tx: params.tx,
|
|
@@ -318,8 +342,14 @@ export function syncRunGraph(
|
|
|
318
342
|
message: `Expected ready node run for "${nextHumanNodeSpec.nodeId}".`,
|
|
319
343
|
})
|
|
320
344
|
}
|
|
321
|
-
const
|
|
322
|
-
|
|
345
|
+
const awaitingHumanNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
346
|
+
status: 'awaiting-human',
|
|
347
|
+
startedAt: nodeRun.startedAt ?? currentTime,
|
|
348
|
+
})
|
|
349
|
+
const awaitingHumanNodeRun = yield* parseRowOrFail(
|
|
350
|
+
PlanNodeRunSchema,
|
|
351
|
+
awaitingHumanNodeRunRow,
|
|
352
|
+
'plan node run',
|
|
323
353
|
)
|
|
324
354
|
replaceNodeRun(awaitingHumanNodeRun)
|
|
325
355
|
|
|
@@ -377,9 +407,11 @@ export function syncRunGraph(
|
|
|
377
407
|
message: `Expected ready node run for "${nextActionNodeSpec.nodeId}".`,
|
|
378
408
|
})
|
|
379
409
|
}
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
410
|
+
const runningNodeRunRow = yield* updateNodeRun(nodeRun, {
|
|
411
|
+
status: 'running',
|
|
412
|
+
startedAt: nodeRun.startedAt ?? currentTime,
|
|
413
|
+
})
|
|
414
|
+
const runningNodeRun = yield* parseRowOrFail(PlanNodeRunSchema, runningNodeRunRow, 'plan node run')
|
|
383
415
|
replaceNodeRun(runningNodeRun)
|
|
384
416
|
|
|
385
417
|
currentRun = yield* replaceRun(params.tx, currentRun, {
|
|
@@ -418,44 +450,59 @@ export function syncRunGraph(
|
|
|
418
450
|
emittedBy: params.emittedBy,
|
|
419
451
|
capturedEvents: params.capturedEvents,
|
|
420
452
|
})
|
|
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
453
|
} else {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
454
|
+
const allTerminalSuccess = currentNodeRuns.every((nodeRun) => isSuccessfulTerminalStatus(nodeRun.status))
|
|
455
|
+
const runStatus: 'completed' | 'running' | 'blocked' = allTerminalSuccess
|
|
456
|
+
? 'completed'
|
|
457
|
+
: hasScheduledOrMonitoring
|
|
458
|
+
? 'running'
|
|
459
|
+
: 'blocked'
|
|
460
|
+
const readyIds = currentNodeRuns
|
|
461
|
+
.filter((candidate) => candidate.status === 'ready')
|
|
462
|
+
.map((candidate) => candidate.nodeId)
|
|
463
|
+
|
|
464
|
+
currentRun = yield* Match.value(runStatus).pipe(
|
|
465
|
+
Match.when('completed', () =>
|
|
466
|
+
replaceRun(params.tx, currentRun, {
|
|
467
|
+
status: 'completed',
|
|
468
|
+
currentNodeId: null,
|
|
469
|
+
waitingNodeId: null,
|
|
470
|
+
readyNodeIds: [],
|
|
471
|
+
completedAt: currentTime,
|
|
472
|
+
}),
|
|
473
|
+
),
|
|
474
|
+
Match.when('running', () =>
|
|
475
|
+
replaceRun(params.tx, currentRun, {
|
|
476
|
+
status: 'running',
|
|
477
|
+
currentNodeId: null,
|
|
478
|
+
waitingNodeId: null,
|
|
479
|
+
readyNodeIds: readyIds,
|
|
480
|
+
}),
|
|
481
|
+
),
|
|
482
|
+
Match.when('blocked', () =>
|
|
483
|
+
replaceRun(params.tx, currentRun, {
|
|
484
|
+
status: 'blocked',
|
|
485
|
+
currentNodeId: null,
|
|
486
|
+
waitingNodeId: null,
|
|
487
|
+
readyNodeIds: readyIds,
|
|
488
|
+
}),
|
|
489
|
+
),
|
|
490
|
+
Match.exhaustive,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
if (runStatus === 'completed') {
|
|
494
|
+
yield* emitEvent({
|
|
495
|
+
tx: params.tx,
|
|
496
|
+
run: currentRun,
|
|
497
|
+
spec: params.spec,
|
|
498
|
+
eventType: 'run-status-changed',
|
|
499
|
+
fromStatus: params.run.status,
|
|
500
|
+
toStatus: currentRun.status,
|
|
501
|
+
message: `Run "${params.spec.title}" completed.`,
|
|
502
|
+
emittedBy: params.emittedBy,
|
|
503
|
+
capturedEvents: params.capturedEvents,
|
|
504
|
+
})
|
|
505
|
+
}
|
|
459
506
|
}
|
|
460
507
|
}
|
|
461
508
|
} else {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isSafePath,
|
|
13
13
|
isSafePathOrRoot,
|
|
14
14
|
} from '@lota-sdk/shared'
|
|
15
|
-
import { Effect } from 'effect'
|
|
15
|
+
import { Effect, Schema } from 'effect'
|
|
16
16
|
|
|
17
17
|
import type { RecordIdInput } from '../../db/record-id'
|
|
18
18
|
import { ensureRecordId } from '../../db/record-id'
|
|
@@ -25,10 +25,11 @@ import { isExecutableConditionExpression, readPathValue } from './plan-helpers'
|
|
|
25
25
|
const SUCCESSFUL_TERMINAL_NODE_STATUSES = new Set(['completed', 'partial', 'skipped', 'scheduled', 'monitoring'])
|
|
26
26
|
const HUMAN_NODE_TYPE_SET = new Set<string>(HUMAN_NODE_TYPE_VALUES)
|
|
27
27
|
const STRUCTURAL_NODE_TYPE_SET = new Set<string>(STRUCTURAL_NODE_TYPE_VALUES)
|
|
28
|
+
const decodeJsonLiteralEffect = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)
|
|
28
29
|
|
|
29
30
|
function parseLiteralValue(raw: string): Effect.Effect<unknown> {
|
|
30
31
|
const trimmed = raw.trim()
|
|
31
|
-
if (!trimmed.length) return Effect.
|
|
32
|
+
if (!trimmed.length) return Effect.void
|
|
32
33
|
if (trimmed === 'true') return Effect.succeed(true)
|
|
33
34
|
if (trimmed === 'false') return Effect.succeed(false)
|
|
34
35
|
if (trimmed === 'null') return Effect.succeed(null)
|
|
@@ -41,9 +42,7 @@ function parseLiteralValue(raw: string): Effect.Effect<unknown> {
|
|
|
41
42
|
return Effect.succeed(trimmed.slice(1, -1))
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
return Effect.
|
|
45
|
-
Effect.catch(() => Effect.succeed(trimmed)),
|
|
46
|
-
)
|
|
45
|
+
return decodeJsonLiteralEffect(trimmed).pipe(Effect.catch(() => Effect.succeed(trimmed)))
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
function buildArtifactContext(artifacts: Array<{ name: string; kind: string; payload?: unknown }>) {
|
|
@@ -149,7 +148,53 @@ type PlanNodeRunUpdate = Omit<
|
|
|
149
148
|
completedAt?: string | Date | null
|
|
150
149
|
}
|
|
151
150
|
|
|
151
|
+
function patchField<K extends string, V>(
|
|
152
|
+
key: K,
|
|
153
|
+
patch: V | null | undefined,
|
|
154
|
+
existing: V | undefined,
|
|
155
|
+
): Partial<Record<K, V>> {
|
|
156
|
+
if (patch === null) return {}
|
|
157
|
+
if (patch !== undefined) return { [key]: patch } as Partial<Record<K, V>>
|
|
158
|
+
if (existing !== undefined) return { [key]: existing } as Partial<Record<K, V>>
|
|
159
|
+
return {}
|
|
160
|
+
}
|
|
161
|
+
|
|
152
162
|
export function toNodeRunData(nodeRun: PlanNodeRunRecord, patch: PlanNodeRunUpdate) {
|
|
163
|
+
const patchedAttemptId =
|
|
164
|
+
patch.latestAttemptId === null
|
|
165
|
+
? null
|
|
166
|
+
: patch.latestAttemptId !== undefined
|
|
167
|
+
? ensureRecordId(patch.latestAttemptId, TABLES.PLAN_NODE_ATTEMPT)
|
|
168
|
+
: undefined
|
|
169
|
+
const existingAttemptId =
|
|
170
|
+
nodeRun.latestAttemptId !== undefined
|
|
171
|
+
? ensureRecordId(nodeRun.latestAttemptId, TABLES.PLAN_NODE_ATTEMPT)
|
|
172
|
+
: undefined
|
|
173
|
+
|
|
174
|
+
const patchedScheduledAt =
|
|
175
|
+
patch.scheduledAt === null
|
|
176
|
+
? null
|
|
177
|
+
: patch.scheduledAt !== undefined
|
|
178
|
+
? toDatabaseDateTime(patch.scheduledAt)
|
|
179
|
+
: undefined
|
|
180
|
+
const existingScheduledAt = nodeRun.scheduledAt !== undefined ? toDatabaseDateTime(nodeRun.scheduledAt) : undefined
|
|
181
|
+
|
|
182
|
+
const patchedReadyAt =
|
|
183
|
+
patch.readyAt === null ? null : patch.readyAt !== undefined ? toDatabaseDateTime(patch.readyAt) : undefined
|
|
184
|
+
const existingReadyAt = nodeRun.readyAt !== undefined ? toDatabaseDateTime(nodeRun.readyAt) : undefined
|
|
185
|
+
|
|
186
|
+
const patchedStartedAt =
|
|
187
|
+
patch.startedAt === null ? null : patch.startedAt !== undefined ? toDatabaseDateTime(patch.startedAt) : undefined
|
|
188
|
+
const existingStartedAt = nodeRun.startedAt !== undefined ? toDatabaseDateTime(nodeRun.startedAt) : undefined
|
|
189
|
+
|
|
190
|
+
const patchedCompletedAt =
|
|
191
|
+
patch.completedAt === null
|
|
192
|
+
? null
|
|
193
|
+
: patch.completedAt !== undefined
|
|
194
|
+
? toDatabaseDateTime(patch.completedAt)
|
|
195
|
+
: undefined
|
|
196
|
+
const existingCompletedAt = nodeRun.completedAt !== undefined ? toDatabaseDateTime(nodeRun.completedAt) : undefined
|
|
197
|
+
|
|
153
198
|
return {
|
|
154
199
|
runId: ensureRecordId(nodeRun.runId, TABLES.PLAN_RUN),
|
|
155
200
|
planSpecId: ensureRecordId(nodeRun.planSpecId, TABLES.PLAN_SPEC),
|
|
@@ -157,76 +202,16 @@ export function toNodeRunData(nodeRun: PlanNodeRunRecord, patch: PlanNodeRunUpda
|
|
|
157
202
|
status: patch.status ?? nodeRun.status,
|
|
158
203
|
attemptCount: patch.attemptCount ?? nodeRun.attemptCount,
|
|
159
204
|
retryCount: patch.retryCount ?? nodeRun.retryCount,
|
|
160
|
-
...(patch.resolvedInput
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
...(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
? { latestStructuredOutput: patch.latestStructuredOutput }
|
|
171
|
-
: nodeRun.latestStructuredOutput
|
|
172
|
-
? { latestStructuredOutput: nodeRun.latestStructuredOutput }
|
|
173
|
-
: {}),
|
|
174
|
-
...(patch.latestNotes === null
|
|
175
|
-
? {}
|
|
176
|
-
: patch.latestNotes !== undefined
|
|
177
|
-
? { latestNotes: patch.latestNotes }
|
|
178
|
-
: nodeRun.latestNotes
|
|
179
|
-
? { latestNotes: nodeRun.latestNotes }
|
|
180
|
-
: {}),
|
|
181
|
-
...(patch.latestAttemptId === null
|
|
182
|
-
? {}
|
|
183
|
-
: patch.latestAttemptId !== undefined
|
|
184
|
-
? { latestAttemptId: ensureRecordId(patch.latestAttemptId, TABLES.PLAN_NODE_ATTEMPT) }
|
|
185
|
-
: nodeRun.latestAttemptId
|
|
186
|
-
? { latestAttemptId: ensureRecordId(nodeRun.latestAttemptId, TABLES.PLAN_NODE_ATTEMPT) }
|
|
187
|
-
: {}),
|
|
188
|
-
...(patch.blockedReason === null
|
|
189
|
-
? {}
|
|
190
|
-
: patch.blockedReason !== undefined
|
|
191
|
-
? { blockedReason: patch.blockedReason }
|
|
192
|
-
: nodeRun.blockedReason
|
|
193
|
-
? { blockedReason: nodeRun.blockedReason }
|
|
194
|
-
: {}),
|
|
195
|
-
...(patch.failureClass === null
|
|
196
|
-
? {}
|
|
197
|
-
: patch.failureClass !== undefined
|
|
198
|
-
? { failureClass: patch.failureClass }
|
|
199
|
-
: nodeRun.failureClass
|
|
200
|
-
? { failureClass: nodeRun.failureClass }
|
|
201
|
-
: {}),
|
|
202
|
-
...(patch.scheduledAt === null
|
|
203
|
-
? {}
|
|
204
|
-
: patch.scheduledAt !== undefined
|
|
205
|
-
? { scheduledAt: toDatabaseDateTime(patch.scheduledAt) }
|
|
206
|
-
: nodeRun.scheduledAt
|
|
207
|
-
? { scheduledAt: toDatabaseDateTime(nodeRun.scheduledAt) }
|
|
208
|
-
: {}),
|
|
209
|
-
...(patch.readyAt === null
|
|
210
|
-
? {}
|
|
211
|
-
: patch.readyAt !== undefined
|
|
212
|
-
? { readyAt: toDatabaseDateTime(patch.readyAt) }
|
|
213
|
-
: nodeRun.readyAt
|
|
214
|
-
? { readyAt: toDatabaseDateTime(nodeRun.readyAt) }
|
|
215
|
-
: {}),
|
|
216
|
-
...(patch.startedAt === null
|
|
217
|
-
? {}
|
|
218
|
-
: patch.startedAt !== undefined
|
|
219
|
-
? { startedAt: toDatabaseDateTime(patch.startedAt) }
|
|
220
|
-
: nodeRun.startedAt
|
|
221
|
-
? { startedAt: toDatabaseDateTime(nodeRun.startedAt) }
|
|
222
|
-
: {}),
|
|
223
|
-
...(patch.completedAt === null
|
|
224
|
-
? {}
|
|
225
|
-
: patch.completedAt !== undefined
|
|
226
|
-
? { completedAt: toDatabaseDateTime(patch.completedAt) }
|
|
227
|
-
: nodeRun.completedAt
|
|
228
|
-
? { completedAt: toDatabaseDateTime(nodeRun.completedAt) }
|
|
229
|
-
: {}),
|
|
205
|
+
...patchField('resolvedInput', patch.resolvedInput, nodeRun.resolvedInput ?? undefined),
|
|
206
|
+
...patchField('latestStructuredOutput', patch.latestStructuredOutput, nodeRun.latestStructuredOutput ?? undefined),
|
|
207
|
+
...patchField('latestNotes', patch.latestNotes, nodeRun.latestNotes ?? undefined),
|
|
208
|
+
...patchField('latestAttemptId', patchedAttemptId, existingAttemptId),
|
|
209
|
+
...patchField('blockedReason', patch.blockedReason, nodeRun.blockedReason ?? undefined),
|
|
210
|
+
...patchField('failureClass', patch.failureClass, nodeRun.failureClass ?? undefined),
|
|
211
|
+
...patchField('scheduledAt', patchedScheduledAt, existingScheduledAt),
|
|
212
|
+
...patchField('readyAt', patchedReadyAt, existingReadyAt),
|
|
213
|
+
...patchField('startedAt', patchedStartedAt, existingStartedAt),
|
|
214
|
+
...patchField('completedAt', patchedCompletedAt, existingCompletedAt),
|
|
230
215
|
}
|
|
231
216
|
}
|
|
232
217
|
|