@lota-sdk/core 0.4.10 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/package.json +2 -2
  2. package/src/ai-gateway/ai-gateway.ts +149 -95
  3. package/src/ai-gateway/index.ts +16 -1
  4. package/src/config/agent-defaults.ts +4 -120
  5. package/src/config/logger.ts +18 -34
  6. package/src/config/thread-defaults.ts +1 -18
  7. package/src/create-runtime.ts +90 -28
  8. package/src/db/base.service.ts +30 -38
  9. package/src/db/service.ts +489 -545
  10. package/src/effect/index.ts +0 -2
  11. package/src/effect/layers.ts +6 -13
  12. package/src/embeddings/provider.ts +2 -7
  13. package/src/index.ts +4 -5
  14. package/src/queues/autonomous-job.queue.ts +159 -113
  15. package/src/queues/context-compaction.queue.ts +39 -25
  16. package/src/queues/delayed-node-promotion.queue.ts +56 -29
  17. package/src/queues/document-processor.queue.ts +5 -3
  18. package/src/queues/index.ts +1 -0
  19. package/src/queues/memory-consolidation.queue.ts +79 -53
  20. package/src/queues/organization-learning.queue.ts +63 -39
  21. package/src/queues/plan-agent-heartbeat.queue.ts +104 -79
  22. package/src/queues/plan-scheduler.queue.ts +100 -84
  23. package/src/queues/post-chat-memory.queue.ts +55 -33
  24. package/src/queues/queue-factory.ts +40 -41
  25. package/src/queues/queues.service.ts +61 -0
  26. package/src/queues/title-generation.queue.ts +42 -31
  27. package/src/redis/org-memory-lock.ts +24 -9
  28. package/src/redis/redis-lease-lock.ts +8 -1
  29. package/src/runtime/agent-identity-overrides.ts +7 -3
  30. package/src/runtime/agent-runtime-policy.ts +9 -4
  31. package/src/runtime/agent-stream-helpers.ts +9 -4
  32. package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
  33. package/src/runtime/context-compaction/context-compaction.ts +9 -7
  34. package/src/runtime/domain-layer.ts +15 -4
  35. package/src/runtime/execution-plan-visibility.ts +5 -2
  36. package/src/runtime/graph-designer.ts +0 -22
  37. package/src/runtime/index.ts +1 -0
  38. package/src/runtime/indexed-repositories-policy.ts +2 -6
  39. package/src/runtime/plugin-resolution.ts +29 -12
  40. package/src/runtime/post-turn-side-effects.ts +139 -141
  41. package/src/runtime/runtime-config.ts +0 -6
  42. package/src/runtime/runtime-extensions.ts +0 -54
  43. package/src/runtime/runtime-lifecycle.ts +4 -4
  44. package/src/runtime/runtime-services.ts +122 -53
  45. package/src/runtime/runtime-worker-registry.ts +113 -30
  46. package/src/runtime/social-chat/social-chat-agent-runner.ts +6 -3
  47. package/src/runtime/social-chat/social-chat-history.ts +3 -1
  48. package/src/runtime/social-chat/social-chat.ts +35 -20
  49. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +6 -5
  50. package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
  51. package/src/runtime/thread-chat-helpers.ts +18 -9
  52. package/src/runtime/thread-turn-context.ts +7 -47
  53. package/src/runtime/turn-lifecycle.ts +6 -14
  54. package/src/services/agent-activity.service.ts +168 -175
  55. package/src/services/agent-executor.service.ts +35 -16
  56. package/src/services/attachment.service.ts +4 -70
  57. package/src/services/autonomous-job.service.ts +53 -61
  58. package/src/services/context-compaction.service.ts +7 -9
  59. package/src/services/execution-plan/execution-plan-graph.ts +106 -115
  60. package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
  61. package/src/services/execution-plan/execution-plan.service.ts +67 -50
  62. package/src/services/global-orchestrator.service.ts +18 -7
  63. package/src/services/graph-full-routing.ts +7 -6
  64. package/src/services/memory/memory-conversation.ts +10 -5
  65. package/src/services/memory/memory.service.ts +11 -8
  66. package/src/services/ownership-dispatcher.service.ts +16 -5
  67. package/src/services/plan/plan-agent-heartbeat.service.ts +29 -15
  68. package/src/services/plan/plan-agent-query.service.ts +12 -8
  69. package/src/services/plan/plan-completion-side-effects.ts +93 -101
  70. package/src/services/plan/plan-cycle.service.ts +7 -45
  71. package/src/services/plan/plan-deadline.service.ts +28 -17
  72. package/src/services/plan/plan-event-delivery.service.ts +47 -40
  73. package/src/services/plan/plan-executor-context.ts +2 -0
  74. package/src/services/plan/plan-executor-graph.ts +366 -391
  75. package/src/services/plan/plan-executor.service.ts +13 -91
  76. package/src/services/plan/plan-scheduler.service.ts +62 -49
  77. package/src/services/plan/plan-transaction-events.ts +1 -1
  78. package/src/services/recent-activity-title.service.ts +6 -2
  79. package/src/services/thread/thread-bootstrap.ts +11 -9
  80. package/src/services/thread/thread-message.service.ts +6 -5
  81. package/src/services/thread/thread-turn-execution.ts +86 -82
  82. package/src/services/thread/thread-turn-preparation.service.ts +47 -24
  83. package/src/services/thread/thread-turn-streaming.ts +20 -25
  84. package/src/services/thread/thread-turn.ts +25 -44
  85. package/src/services/thread/thread.service.ts +21 -6
  86. package/src/system-agents/recent-activity-title-refiner.agent.ts +8 -5
  87. package/src/system-agents/thread-router.agent.ts +23 -20
  88. package/src/tools/execution-plan.tool.ts +8 -3
  89. package/src/tools/fetch-webpage.tool.ts +10 -9
  90. package/src/tools/firecrawl-client.ts +0 -15
  91. package/src/tools/remember-memory.tool.ts +3 -6
  92. package/src/tools/research-topic.tool.ts +12 -3
  93. package/src/tools/search-web.tool.ts +10 -9
  94. package/src/tools/search.tool.ts +4 -5
  95. package/src/tools/team-think.tool.ts +139 -121
  96. package/src/workers/bootstrap.ts +9 -10
  97. package/src/workers/memory-consolidation.worker.ts +4 -1
  98. package/src/workers/organization-learning.worker.ts +15 -2
  99. package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
  100. package/src/workers/regular-chat-memory-digest.runner.ts +21 -14
  101. package/src/workers/skill-extraction.runner.ts +13 -15
  102. package/src/workers/worker-utils.ts +6 -18
  103. package/src/effect/awaitable-effect.ts +0 -96
  104. package/src/effect/runtime-ref.ts +0 -25
  105. package/src/effect/runtime.ts +0 -46
  106. package/src/redis/runtime-connection.ts +0 -20
  107. package/src/runtime/runtime-accessors.ts +0 -92
  108. package/src/runtime/runtime-token.ts +0 -47
@@ -32,7 +32,8 @@ import type { SurrealDBService } from '../db/service'
32
32
  import { TABLES } from '../db/tables'
33
33
  import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
34
34
  import { DatabaseServiceTag, RuntimeConfigServiceTag } from '../effect/services'
35
- import type { AutonomousJobQueuePayload } from '../queues/autonomous-job.queue'
35
+ import type { AutonomousJobQueuePayload, AutonomousJobQueueRuntime } from '../queues/autonomous-job.queue'
36
+ import { LotaQueuesServiceTag } from '../queues/queues.service'
36
37
  import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
37
38
  import { extractMessageText } from '../runtime/thread-chat-helpers'
38
39
  import { buildAutonomousAtJobId, encodeBullmqId } from '../utils/autonomous-job-ids'
@@ -49,12 +50,8 @@ import { ExecutionPlanServiceTag } from './execution-plan/execution-plan.service
49
50
  import type { makeQueueJobService } from './queue-job.service'
50
51
  import { QueueJobServiceTag } from './queue-job.service'
51
52
  import type { PreparedThreadTurnResult } from './thread/thread-turn'
52
- import type { ThreadTurnParams } from './thread/thread-turn-preparation.service'
53
53
  import type { makeThreadService } from './thread/thread.service'
54
54
  import { ThreadServiceTag } from './thread/thread.service'
55
- // runThreadTurnInBackground is imported dynamically below
56
-
57
- type ThreadTurnModule = { runThreadTurnInBackground(params: ThreadTurnParams): Promise<PreparedThreadTurnResult> }
58
55
 
59
56
  const AUTONOMOUS_JOB_QUEUE_NAME = 'autonomous-job'
60
57
 
@@ -312,27 +309,22 @@ function getRunRowEffect(
312
309
  })
313
310
  }
314
311
 
315
- function unscheduleRowEffect(row: AutonomousJobRow): Effect.Effect<void, AutonomousJobServiceError> {
312
+ function unscheduleRowEffect(
313
+ deps: AutonomousJobDeps,
314
+ row: AutonomousJobRow,
315
+ ): Effect.Effect<void, AutonomousJobServiceError> {
316
316
  if (row.schedule.kind === 'at') {
317
317
  return Effect.gen(function* () {
318
- const { removeAutonomousAtJob } = yield* effectTryPromise(
319
- () => import('../queues/autonomous-job.queue'),
320
- 'Failed to load autonomous job queue helpers.',
321
- )
322
318
  yield* effectTryPromise(
323
- () => removeAutonomousAtJob(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
319
+ () => deps.autonomousJobQueue.removeAutonomousAtJob(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
324
320
  'Failed to remove autonomous at-job.',
325
321
  )
326
322
  })
327
323
  }
328
324
 
329
325
  return Effect.gen(function* () {
330
- const { removeAutonomousJobScheduler } = yield* effectTryPromise(
331
- () => import('../queues/autonomous-job.queue'),
332
- 'Failed to load autonomous job queue helpers.',
333
- )
334
326
  yield* effectTryPromise(
335
- () => removeAutonomousJobScheduler(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
327
+ () => deps.autonomousJobQueue.removeAutonomousJobScheduler(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
336
328
  'Failed to remove autonomous job scheduler.',
337
329
  )
338
330
  })
@@ -386,14 +378,9 @@ function scheduleRowEffect(
386
378
  })
387
379
  : createRunRowEffect(deps, { autonomousJobId: row.id, threadId: row.threadId })
388
380
 
389
- const { enqueueAutonomousJobRun } = yield* effectTryPromise(
390
- () => import('../queues/autonomous-job.queue'),
391
- 'Failed to load autonomous job queue helpers.',
392
- )
393
-
394
381
  const enqueueResult = yield* effectTryPromise(
395
382
  () =>
396
- enqueueAutonomousJobRun({
383
+ deps.autonomousJobQueue.enqueueAutonomousJobRun({
397
384
  payload: {
398
385
  autonomousJobId: jobId,
399
386
  autonomousJobRunId: recordIdToString(queuedRun.id, TABLES.AUTONOMOUS_JOB_RUN),
@@ -428,13 +415,9 @@ function scheduleRowEffect(
428
415
  } else {
429
416
  const recurringSchedule = row.schedule
430
417
  return Effect.gen(function* () {
431
- const { upsertAutonomousJobScheduler } = yield* effectTryPromise(
432
- () => import('../queues/autonomous-job.queue'),
433
- 'Failed to load autonomous job queue helpers.',
434
- )
435
-
436
418
  yield* effectTryPromise(
437
- () => upsertAutonomousJobScheduler({ autonomousJobId: jobId, schedule: recurringSchedule }),
419
+ () =>
420
+ deps.autonomousJobQueue.upsertAutonomousJobScheduler({ autonomousJobId: jobId, schedule: recurringSchedule }),
438
421
  'Failed to upsert autonomous job scheduler.',
439
422
  )
440
423
 
@@ -564,7 +547,7 @@ function updateEffect(
564
547
  return Effect.gen(function* () {
565
548
  const parsed = UpdateAutonomousJobInputSchema.parse(input)
566
549
  const existing = yield* getRowEffect(deps, jobId)
567
- yield* unscheduleRowEffect(existing)
550
+ yield* unscheduleRowEffect(deps, existing)
568
551
  const nextTitle = parsed.title
569
552
  if (typeof nextTitle === 'string' && compactWhitespace(nextTitle) !== compactWhitespace(existing.title)) {
570
553
  const title = nextTitle
@@ -616,7 +599,7 @@ function pauseEffect(
616
599
  ): Effect.Effect<AutonomousJob, AutonomousJobServiceError> {
617
600
  return Effect.gen(function* () {
618
601
  const row = yield* getRowEffect(deps, jobId)
619
- yield* unscheduleRowEffect(row)
602
+ yield* unscheduleRowEffect(deps, row)
620
603
  yield* effectTryPromise(
621
604
  () =>
622
605
  deps.db.update(
@@ -667,14 +650,10 @@ function runNowEffect(
667
650
  return Effect.gen(function* () {
668
651
  const row = yield* getRowEffect(deps, jobId)
669
652
  const queuedRun = yield* createRunRowEffect(deps, { autonomousJobId: row.id, threadId: row.threadId })
670
- const { enqueueAutonomousJobRun } = yield* effectTryPromise(
671
- () => import('../queues/autonomous-job.queue'),
672
- 'Failed to load autonomous job queue helpers.',
673
- )
674
653
 
675
654
  const enqueueResult = yield* effectTryPromise(
676
655
  () =>
677
- enqueueAutonomousJobRun({
656
+ deps.autonomousJobQueue.enqueueAutonomousJobRun({
678
657
  payload: {
679
658
  autonomousJobId: recordIdToString(row.id, TABLES.AUTONOMOUS_JOB),
680
659
  autonomousJobRunId: recordIdToString(queuedRun.id, TABLES.AUTONOMOUS_JOB_RUN),
@@ -710,7 +689,7 @@ function cancelEffect(
710
689
  ): Effect.Effect<AutonomousJob, AutonomousJobServiceError> {
711
690
  return Effect.gen(function* () {
712
691
  const row = yield* getRowEffect(deps, jobId)
713
- yield* unscheduleRowEffect(row)
692
+ yield* unscheduleRowEffect(deps, row)
714
693
  yield* effectTryPromise(
715
694
  () =>
716
695
  deps.db.update(
@@ -923,7 +902,7 @@ function failQueuedRunEffect(
923
902
  )
924
903
 
925
904
  if (autoPause || terminalOneShot) {
926
- yield* unscheduleRowEffect(currentJobRow)
905
+ yield* unscheduleRowEffect(deps, currentJobRow)
927
906
  }
928
907
 
929
908
  yield* effectTryPromise(
@@ -967,7 +946,7 @@ function failQueuedRunEffect(
967
946
  function executeQueuedRunEffect(
968
947
  deps: AutonomousJobDeps,
969
948
  job: Job<AutonomousJobQueuePayload>,
970
- ): Effect.Effect<{ status: string; summary?: string }, AutonomousJobServiceError> {
949
+ ): Effect.Effect<{ status: string; summary?: string }, AutonomousJobServiceError, unknown> {
971
950
  return Effect.gen(function* () {
972
951
  const autonomousJobRowRef = yield* Ref.make<AutonomousJobRow | null>(null)
973
952
  const runRowRef = yield* Ref.make<AutonomousJobRunRow | null>(null)
@@ -987,30 +966,34 @@ function executeQueuedRunEffect(
987
966
  yield* Ref.set(runRowRef, currentRunRow)
988
967
  const inputMessage = buildSyntheticUserMessage(currentJobRow.prompt)
989
968
 
990
- const [thread, turnModule] = yield* Effect.all([
991
- deps.thread
992
- .getThread(currentJobRow.threadId)
993
- .pipe(
994
- Effect.mapError(
995
- (cause) => new AutonomousJobServiceError({ message: 'Failed to load autonomous job thread.', cause }),
996
- ),
969
+ const thread = yield* deps.thread
970
+ .getThread(currentJobRow.threadId)
971
+ .pipe(
972
+ Effect.mapError(
973
+ (cause) => new AutonomousJobServiceError({ message: 'Failed to load autonomous job thread.', cause }),
997
974
  ),
998
- effectTryPromise<ThreadTurnModule>(() => import('./thread/thread-turn'), 'Failed to load thread turn runtime.'),
999
- ])
975
+ )
1000
976
 
1001
- const turnResult = yield* effectTryPromise(
1002
- () =>
1003
- turnModule.runThreadTurnInBackground({
1004
- thread,
1005
- threadRef: ensureRecordId(currentJobRow.threadId, TABLES.THREAD),
1006
- orgRef: ensureRecordId(currentJobRow.organizationId, TABLES.ORGANIZATION),
1007
- userRef: ensureRecordId(currentJobRow.ownerUserId, TABLES.USER),
1008
- userName: currentJobRow.ownerUserName,
1009
- agentIdOverride: currentJobRow.agentId,
1010
- inputMessage,
1011
- }),
1012
- 'Failed to run autonomous job thread turn.',
1013
- )
977
+ const turnModule = yield* Effect.tryPromise({
978
+ try: () => import('./thread/thread-turn'),
979
+ catch: (cause) => new AutonomousJobServiceError({ message: 'Failed to load thread turn runtime.', cause }),
980
+ })
981
+
982
+ const turnResult = yield* turnModule
983
+ .runThreadTurnInBackground({
984
+ thread,
985
+ threadRef: ensureRecordId(currentJobRow.threadId, TABLES.THREAD),
986
+ orgRef: ensureRecordId(currentJobRow.organizationId, TABLES.ORGANIZATION),
987
+ userRef: ensureRecordId(currentJobRow.ownerUserId, TABLES.USER),
988
+ userName: currentJobRow.ownerUserName,
989
+ agentIdOverride: currentJobRow.agentId,
990
+ inputMessage,
991
+ })
992
+ .pipe(
993
+ Effect.mapError(
994
+ (cause) => new AutonomousJobServiceError({ message: 'Failed to run autonomous job thread turn.', cause }),
995
+ ),
996
+ )
1014
997
 
1015
998
  const activePlan = yield* deps.executionPlan
1016
999
  .getActivePlanForThread(currentJobRow.threadId)
@@ -1061,6 +1044,7 @@ interface AutonomousJobDeps {
1061
1044
  executionPlan: ReturnType<typeof makeExecutionPlanService>
1062
1045
  queueJob: ReturnType<typeof makeQueueJobService>
1063
1046
  thread: ReturnType<typeof makeThreadService>
1047
+ autonomousJobQueue: AutonomousJobQueueRuntime
1064
1048
  }
1065
1049
 
1066
1050
  export function makeAutonomousJobService(deps: AutonomousJobDeps) {
@@ -1080,6 +1064,14 @@ export const AutonomousJobServiceLive = Layer.effect(
1080
1064
  const executionPlan = yield* ExecutionPlanServiceTag
1081
1065
  const queueJob = yield* QueueJobServiceTag
1082
1066
  const thread = yield* ThreadServiceTag
1083
- return makeAutonomousJobService({ db, config, executionPlan, queueJob, thread })
1067
+ const queues = yield* LotaQueuesServiceTag
1068
+ return makeAutonomousJobService({
1069
+ db,
1070
+ config,
1071
+ executionPlan,
1072
+ queueJob,
1073
+ thread,
1074
+ autonomousJobQueue: queues.autonomousJob,
1075
+ })
1084
1076
  }),
1085
1077
  )
@@ -77,14 +77,12 @@ export function makeContextCompactionService(deps: ContextCompactionDeps) {
77
77
  typeof thread.lastCompactedMessageId === 'string' ? thread.lastCompactedMessageId : undefined,
78
78
  )
79
79
 
80
- const result = yield* Effect.tryPromise(() =>
81
- contextCompactionRuntime.compactHistory({
82
- summaryText: typeof thread.compactionSummary === 'string' ? thread.compactionSummary : '',
83
- liveMessages,
84
- tailMessageCount: THREAD_RAW_TAIL_MESSAGES,
85
- contextSize: params.contextSize,
86
- }),
87
- )
80
+ const result = yield* contextCompactionRuntime.compactHistory({
81
+ summaryText: typeof thread.compactionSummary === 'string' ? thread.compactionSummary : '',
82
+ liveMessages,
83
+ tailMessageCount: THREAD_RAW_TAIL_MESSAGES,
84
+ contextSize: params.contextSize,
85
+ })
88
86
 
89
87
  if (!result.compacted || !result.lastCompactedMessageId) {
90
88
  return { compacted: false }
@@ -122,7 +120,7 @@ export function makeContextCompactionService(deps: ContextCompactionDeps) {
122
120
  })
123
121
 
124
122
  const compactMemoryBlockEffect = (params: { previousSummary: string; newEntriesText: string }) =>
125
- Effect.tryPromise(() => contextCompactionRuntime.compactMemoryBlockSummary(params))
123
+ contextCompactionRuntime.compactMemoryBlockSummary(params)
126
124
 
127
125
  return {
128
126
  createSummaryMessage(summaryText: string) {
@@ -15,7 +15,6 @@ import { ensureRecordId, recordIdToString } from '../../db/record-id'
15
15
  import type { DatabaseTransaction } from '../../db/service'
16
16
  import { TABLES } from '../../db/tables'
17
17
  import { ServiceError } from '../../effect/errors'
18
- import { runPromise } from '../../effect/runtime'
19
18
  import { nowDate } from '../../utils/date-time'
20
19
  import type { CompiledPlanNode } from '../plan/plan-compiler.service'
21
20
  import type { syncRunGraph } from '../plan/plan-executor-graph'
@@ -33,10 +32,10 @@ function parseRowOrFail<TSchema extends z.ZodTypeAny>(
33
32
  }
34
33
 
35
34
  type SyncRunGraphParams = Parameters<typeof syncRunGraph>[1]
36
- type SyncRunGraphResult = Awaited<ReturnType<typeof syncRunGraph>>
35
+ type SyncRunGraphResult = ReturnType<typeof syncRunGraph>
37
36
 
38
37
  interface ExecutionPlanGraphExecutor {
39
- syncRunGraph(params: SyncRunGraphParams): Promise<SyncRunGraphResult>
38
+ syncRunGraph(params: SyncRunGraphParams): SyncRunGraphResult
40
39
  }
41
40
 
42
41
  function buildNodeSpecCreateContent(compiledNode: CompiledPlanNode, planSpecId: RecordIdInput) {
@@ -148,131 +147,123 @@ export function createInitializedRunGraph(params: {
148
147
  runPatch?: { replacedRunId?: RecordIdInput }
149
148
  planExecutor: ExecutionPlanGraphExecutor
150
149
  }) {
151
- return runPromise(
152
- Effect.gen(function* () {
153
- const nodeSpecs = yield* createNodeSpecs(params.tx, params.spec.id, params.nodes)
154
- const runRaw = yield* params.tx
155
- .create(ensureRecordId(params.runId, TABLES.PLAN_RUN))
156
- .content({
157
- planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
158
- organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
159
- threadId: ensureRecordId(params.threadId, TABLES.THREAD),
160
- ...(params.sourceThreadId ? { sourceThreadId: ensureRecordId(params.sourceThreadId, TABLES.THREAD) } : {}),
161
- leadAgentId: params.leadAgentId,
162
- ...(params.createdByAgentId ? { createdByAgentId: params.createdByAgentId } : {}),
163
- status: params.requireApproval ? 'pending-approval' : 'running',
164
- readyNodeIds: [],
165
- failureCount: 0,
166
- ...(params.runPatch?.replacedRunId
167
- ? { replacedRunId: ensureRecordId(params.runPatch.replacedRunId, TABLES.PLAN_RUN) }
168
- : {}),
169
- ...(params.requireApproval ? {} : { startedAt: nowDate() }),
150
+ return Effect.gen(function* () {
151
+ const nodeSpecs = yield* createNodeSpecs(params.tx, params.spec.id, params.nodes)
152
+ const runRaw = yield* params.tx
153
+ .create(ensureRecordId(params.runId, TABLES.PLAN_RUN))
154
+ .content({
155
+ planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
156
+ organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
157
+ threadId: ensureRecordId(params.threadId, TABLES.THREAD),
158
+ ...(params.sourceThreadId ? { sourceThreadId: ensureRecordId(params.sourceThreadId, TABLES.THREAD) } : {}),
159
+ leadAgentId: params.leadAgentId,
160
+ ...(params.createdByAgentId ? { createdByAgentId: params.createdByAgentId } : {}),
161
+ status: params.requireApproval ? 'pending-approval' : 'running',
162
+ readyNodeIds: [],
163
+ failureCount: 0,
164
+ ...(params.runPatch?.replacedRunId
165
+ ? { replacedRunId: ensureRecordId(params.runPatch.replacedRunId, TABLES.PLAN_RUN) }
166
+ : {}),
167
+ ...(params.requireApproval ? {} : { startedAt: nowDate() }),
168
+ })
169
+ .output('after')
170
+ const run = yield* parseRowOrFail(PlanRunSchema, runRaw, 'createInitializedRunGraph:run')
171
+
172
+ const nodeRuns = yield* createNodeRuns(params.tx, run.id, params.spec.id, nodeSpecs)
173
+ const synced = params.requireApproval
174
+ ? { run, nodeRuns }
175
+ : yield* params.planExecutor.syncRunGraph({
176
+ tx: params.tx,
177
+ run,
178
+ spec: params.spec,
179
+ nodeSpecs,
180
+ nodeRuns,
181
+ artifacts: [],
182
+ emittedBy: params.leadAgentId,
183
+ capturedEvents: params.emittedEvents,
170
184
  })
171
- .output('after')
172
- const run = yield* parseRowOrFail(PlanRunSchema, runRaw, 'createInitializedRunGraph:run')
173
185
 
174
- const nodeRuns = yield* createNodeRuns(params.tx, run.id, params.spec.id, nodeSpecs)
175
- const synced = params.requireApproval
176
- ? { run, nodeRuns }
177
- : yield* Effect.tryPromise(() =>
178
- params.planExecutor.syncRunGraph({
179
- tx: params.tx,
180
- run,
181
- spec: params.spec,
182
- nodeSpecs,
183
- nodeRuns,
184
- artifacts: [],
185
- emittedBy: params.leadAgentId,
186
- capturedEvents: params.emittedEvents,
187
- }),
188
- )
186
+ const event = yield* params.tx
187
+ .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
188
+ .content({
189
+ planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
190
+ runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
191
+ eventType: params.createdEventType,
192
+ message: params.createdEventMessage,
193
+ emittedBy: params.leadAgentId,
194
+ detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
195
+ })
196
+ .output('after')
197
+ params.emittedEvents.push(yield* parseRowOrFail(PlanEventSchema, event, 'createInitializedRunGraph:createdEvent'))
189
198
 
190
- const event = yield* params.tx
199
+ if (params.requireApproval) {
200
+ const pendingApprovalEvent = yield* params.tx
191
201
  .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
192
202
  .content({
193
203
  planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
194
204
  runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
195
- eventType: params.createdEventType,
196
- message: params.createdEventMessage,
205
+ eventType: 'plan-pending-approval',
206
+ toStatus: synced.run.status,
207
+ message: `Execution plan "${params.spec.title}" is pending approval.`,
197
208
  emittedBy: params.leadAgentId,
198
209
  detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
199
210
  })
200
211
  .output('after')
201
- params.emittedEvents.push(yield* parseRowOrFail(PlanEventSchema, event, 'createInitializedRunGraph:createdEvent'))
202
-
203
- if (params.requireApproval) {
204
- const pendingApprovalEvent = yield* params.tx
205
- .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
206
- .content({
207
- planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
208
- runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
209
- eventType: 'plan-pending-approval',
210
- toStatus: synced.run.status,
211
- message: `Execution plan "${params.spec.title}" is pending approval.`,
212
- emittedBy: params.leadAgentId,
213
- detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
214
- })
215
- .output('after')
216
- params.emittedEvents.push(
217
- yield* parseRowOrFail(
218
- PlanEventSchema,
219
- pendingApprovalEvent,
220
- 'createInitializedRunGraph:pendingApprovalEvent',
221
- ),
222
- )
223
- }
224
-
225
- const checkpointRaw = yield* params.tx
226
- .create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
227
- .content({
228
- runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
229
- sequence: 1,
230
- runStatus: synced.run.status,
231
- readyNodeIds: [...synced.run.readyNodeIds],
232
- activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
233
- artifactIds: [],
234
- lastCompletedNodeIds: synced.nodeRuns
235
- .filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
236
- .map((nodeRun) => nodeRun.nodeId),
237
- snapshot: {
238
- reason: params.checkpointReason,
239
- currentNodeId: synced.run.currentNodeId,
240
- waitingNodeId: synced.run.waitingNodeId,
241
- readyNodeIds: synced.run.readyNodeIds,
242
- },
243
- })
244
- .output('after')
245
- const checkpoint = yield* parseRowOrFail(
246
- PlanCheckpointSchema,
247
- checkpointRaw,
248
- 'createInitializedRunGraph:checkpoint',
212
+ params.emittedEvents.push(
213
+ yield* parseRowOrFail(PlanEventSchema, pendingApprovalEvent, 'createInitializedRunGraph:pendingApprovalEvent'),
249
214
  )
215
+ }
250
216
 
251
- const updatedRunRaw = yield* params.tx
252
- .update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
253
- .content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
254
- .output('after')
255
- const updatedRun = yield* parseRowOrFail(PlanRunSchema, updatedRunRaw, 'createInitializedRunGraph:updatedRun')
217
+ const checkpointRaw = yield* params.tx
218
+ .create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
219
+ .content({
220
+ runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
221
+ sequence: 1,
222
+ runStatus: synced.run.status,
223
+ readyNodeIds: [...synced.run.readyNodeIds],
224
+ activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
225
+ artifactIds: [],
226
+ lastCompletedNodeIds: synced.nodeRuns
227
+ .filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
228
+ .map((nodeRun) => nodeRun.nodeId),
229
+ snapshot: {
230
+ reason: params.checkpointReason,
231
+ currentNodeId: synced.run.currentNodeId,
232
+ waitingNodeId: synced.run.waitingNodeId,
233
+ readyNodeIds: synced.run.readyNodeIds,
234
+ },
235
+ })
236
+ .output('after')
237
+ const checkpoint = yield* parseRowOrFail(
238
+ PlanCheckpointSchema,
239
+ checkpointRaw,
240
+ 'createInitializedRunGraph:checkpoint',
241
+ )
256
242
 
257
- const checkpointEvent = yield* params.tx
258
- .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
259
- .content({
260
- planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
261
- runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
262
- eventType: 'checkpoint-saved',
263
- message: 'Saved checkpoint 1.',
264
- emittedBy: 'system',
265
- detail: {
266
- checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT),
267
- reason: params.checkpointReason,
268
- },
269
- })
270
- .output('after')
271
- params.emittedEvents.push(
272
- yield* parseRowOrFail(PlanEventSchema, checkpointEvent, 'createInitializedRunGraph:checkpointEvent'),
273
- )
243
+ const updatedRunRaw = yield* params.tx
244
+ .update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
245
+ .content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
246
+ .output('after')
247
+ const updatedRun = yield* parseRowOrFail(PlanRunSchema, updatedRunRaw, 'createInitializedRunGraph:updatedRun')
248
+
249
+ const checkpointEvent = yield* params.tx
250
+ .create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
251
+ .content({
252
+ planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
253
+ runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
254
+ eventType: 'checkpoint-saved',
255
+ message: 'Saved checkpoint 1.',
256
+ emittedBy: 'system',
257
+ detail: {
258
+ checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT),
259
+ reason: params.checkpointReason,
260
+ },
261
+ })
262
+ .output('after')
263
+ params.emittedEvents.push(
264
+ yield* parseRowOrFail(PlanEventSchema, checkpointEvent, 'createInitializedRunGraph:checkpointEvent'),
265
+ )
274
266
 
275
- return updatedRun
276
- }),
277
- )
267
+ return updatedRun
268
+ })
278
269
  }
@@ -1,4 +1,3 @@
1
- import type { PlanRunRecord } from '@lota-sdk/shared'
2
1
  import { PlanRunSchema } from '@lota-sdk/shared'
3
2
  import { Schema, Effect } from 'effect'
4
3
 
@@ -6,7 +5,6 @@ import type { RecordIdInput } from '../../db/record-id'
6
5
  import { ensureRecordId } from '../../db/record-id'
7
6
  import type { SurrealDBService } from '../../db/service'
8
7
  import { TABLES } from '../../db/tables'
9
- import { runPromise } from '../../effect/runtime'
10
8
  import { toDatabaseDateTime } from '../../utils/date-time'
11
9
  import { toRunData } from '../plan/plan-run-data'
12
10
  import type { makePlanRunService } from '../plan/plan-run.service'
@@ -21,7 +19,7 @@ function toPlanScheduleAttachError(operation: string, cause: unknown) {
21
19
  return new PlanScheduleAttachError({ cause: `${operation}: ${detail}` })
22
20
  }
23
21
 
24
- function attachPlanScheduleIfNeededEffect(params: {
22
+ export function attachPlanScheduleIfNeeded(params: {
25
23
  db: SurrealDBService
26
24
  planRunService: ReturnType<typeof makePlanRunService>
27
25
  planSchedulerService: ReturnType<typeof makePlanSchedulerService>
@@ -70,15 +68,3 @@ function attachPlanScheduleIfNeededEffect(params: {
70
68
  return updatedRun ?? run
71
69
  })
72
70
  }
73
-
74
- export function attachPlanScheduleIfNeeded(params: {
75
- db: SurrealDBService
76
- planRunService: ReturnType<typeof makePlanRunService>
77
- planSchedulerService: ReturnType<typeof makePlanSchedulerService>
78
- organizationId: RecordIdInput
79
- threadId: RecordIdInput
80
- runId: RecordIdInput
81
- planSpecId: RecordIdInput
82
- }): Promise<PlanRunRecord> {
83
- return runPromise(attachPlanScheduleIfNeededEffect(params))
84
- }