@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
@@ -3,12 +3,13 @@ import { PlanRunSchema } from '@lota-sdk/shared'
3
3
  import { Context, Schema, Effect, Layer } from 'effect'
4
4
  import { BoundQuery } from 'surrealdb'
5
5
 
6
+ import type { ResolvedAgentConfig } from '../../config/agent-defaults'
6
7
  import type { RecordIdInput } from '../../db/record-id'
7
8
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
8
9
  import type { SurrealDBService } from '../../db/service'
9
10
  import { TABLES } from '../../db/tables'
10
11
  import { effectTryPromise } from '../../effect/helpers'
11
- import { DatabaseServiceTag } from '../../effect/services'
12
+ import { AgentConfigServiceTag, DatabaseServiceTag } from '../../effect/services'
12
13
  import { resolvePlanNodeExecutionVisibility } from '../../runtime/execution-plan-visibility'
13
14
  import { nowDate, unsafeDateFrom } from '../../utils/date-time'
14
15
  import { evaluateDeadline } from './plan-deadline.service'
@@ -53,6 +54,7 @@ export interface RecentlyUnblockedNode {
53
54
  }
54
55
 
55
56
  function isVisibleAgentNode(params: {
57
+ agentConfig: ResolvedAgentConfig
56
58
  nodeSpec: PlanNodeSpecRecord
57
59
  spec: PlanSpecRecord
58
60
  }): { agentId: string; visibility: PlanExecutionVisibility } | null {
@@ -60,7 +62,7 @@ function isVisibleAgentNode(params: {
60
62
  return null
61
63
  }
62
64
 
63
- const visibility = resolvePlanNodeExecutionVisibility(params.spec, params.nodeSpec)
65
+ const visibility = resolvePlanNodeExecutionVisibility(params.agentConfig, params.spec, params.nodeSpec)
64
66
  const isVisible = visibility === 'visible'
65
67
  if (!isVisible) {
66
68
  return null
@@ -93,12 +95,13 @@ function queryServiceEffect<A, E>(
93
95
  }
94
96
 
95
97
  interface PlanAgentQueryDeps {
98
+ agentConfig: ResolvedAgentConfig
96
99
  db: SurrealDBService
97
100
  planRunService: ReturnType<typeof makePlanRunService>
98
101
  }
99
102
 
100
103
  export function makePlanAgentQueryService(deps: PlanAgentQueryDeps) {
101
- const { db, planRunService } = deps
104
+ const { agentConfig, db, planRunService } = deps
102
105
 
103
106
  function listActiveRunsEffect(organizationId?: RecordIdInput): Effect.Effect<PlanRunRecord[], PlanAgentQueryError> {
104
107
  const bindings = {
@@ -138,7 +141,7 @@ export function makePlanAgentQueryService(deps: PlanAgentQueryDeps) {
138
141
  if (!ACTIONABLE_NODE_STATUSES.has(nodeRun.status)) continue
139
142
  const nodeSpec = nodeSpecs.find((ns) => ns.nodeId === nodeRun.nodeId)
140
143
  if (!nodeSpec) continue
141
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
144
+ const visibleTarget = isVisibleAgentNode({ agentConfig, nodeSpec, spec })
142
145
  if (!visibleTarget) continue
143
146
  if (params.agentId && params.agentId !== visibleTarget.agentId) continue
144
147
  actionable.push({
@@ -167,7 +170,7 @@ export function makePlanAgentQueryService(deps: PlanAgentQueryDeps) {
167
170
  continue
168
171
  }
169
172
 
170
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
173
+ const visibleTarget = isVisibleAgentNode({ agentConfig, nodeSpec, spec })
171
174
  if (!visibleTarget) {
172
175
  continue
173
176
  }
@@ -226,7 +229,7 @@ export function makePlanAgentQueryService(deps: PlanAgentQueryDeps) {
226
229
  continue
227
230
  }
228
231
 
229
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
232
+ const visibleTarget = isVisibleAgentNode({ agentConfig, nodeSpec, spec })
230
233
  matches.push({
231
234
  organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
232
235
  threadId: recordIdToString(run.threadId, TABLES.THREAD),
@@ -276,7 +279,7 @@ export function makePlanAgentQueryService(deps: PlanAgentQueryDeps) {
276
279
  continue
277
280
  }
278
281
 
279
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
282
+ const visibleTarget = isVisibleAgentNode({ agentConfig, nodeSpec, spec })
280
283
  if (!visibleTarget) {
281
284
  continue
282
285
  }
@@ -315,8 +318,9 @@ export class PlanAgentQueryServiceTag extends Context.Service<
315
318
  export const PlanAgentQueryServiceLive = Layer.effect(
316
319
  PlanAgentQueryServiceTag,
317
320
  Effect.gen(function* () {
321
+ const agentConfig = yield* AgentConfigServiceTag
318
322
  const db = yield* DatabaseServiceTag
319
323
  const planRunService = yield* PlanRunServiceTag
320
- return makePlanAgentQueryService({ db, planRunService })
324
+ return makePlanAgentQueryService({ agentConfig, db, planRunService })
321
325
  }),
322
326
  )
@@ -7,7 +7,6 @@ import { ensureRecordId } from '../../db/record-id'
7
7
  import type { SurrealDBService } from '../../db/service'
8
8
  import { TABLES } from '../../db/tables'
9
9
  import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
10
- import { runPromise } from '../../effect/runtime'
11
10
  import { nowEpochMillis, unsafeDateFrom } from '../../utils/date-time'
12
11
  import type { makeFeedbackLoopService } from '../feedback-loop.service'
13
12
  import type { makeInstitutionalMemoryService } from '../institutional-memory.service'
@@ -53,117 +52,110 @@ export function makePlanCompletionSideEffects({
53
52
  planRunService,
54
53
  qualityMetricsService,
55
54
  }: PlanCompletionSideEffectsDeps) {
56
- function runPlanNodeCompletionSideEffects(params: {
57
- runId: string
58
- organizationId: string
59
- nodeId: string
60
- nodeLabel: string
61
- nodeOwnerRef: string
62
- nodeOwnerType: PlanNodeOwner['executorType']
63
- nodeType: PlanNodeType
64
- nodeStartedAt?: string | Date | null
65
- nodeAttemptCount: number
66
- artifactCount: number
67
- validationIssues: PlanValidationIssueInput[]
68
- }): Promise<void> {
69
- return runPromise(
70
- Effect.gen(function* () {
71
- const executionTimeMs = params.nodeStartedAt
72
- ? nowEpochMillis() - unsafeDateFrom(params.nodeStartedAt).getTime()
73
- : 0
74
- if (executionTimeMs > 0) {
75
- yield* Metric.update(
76
- Metric.withAttributes(planNodeExecutionDuration, { nodeType: params.nodeType }),
77
- executionTimeMs,
78
- )
79
- }
80
- yield* qualityMetricsService.recordNodeMetrics({
81
- organizationId: params.organizationId,
82
- runId: params.runId,
83
- nodeId: params.nodeId,
84
- metrics: {
85
- executionTimeMs: Math.max(0, executionTimeMs),
86
- attemptCount: params.nodeAttemptCount,
87
- artifactCount: params.artifactCount,
88
- validationIssueCount: params.validationIssues.length,
89
- ownerRef: params.nodeOwnerRef,
90
- ownerType: params.nodeOwnerType,
91
- nodeType: params.nodeType,
92
- },
93
- })
94
- }),
95
- )
96
- }
55
+ const runPlanNodeCompletionSideEffects = Effect.fn('PlanCompletionSideEffects.runPlanNodeCompletionSideEffects')(
56
+ function* (params: {
57
+ runId: string
58
+ organizationId: string
59
+ nodeId: string
60
+ nodeLabel: string
61
+ nodeOwnerRef: string
62
+ nodeOwnerType: PlanNodeOwner['executorType']
63
+ nodeType: PlanNodeType
64
+ nodeStartedAt?: string | Date | null
65
+ nodeAttemptCount: number
66
+ artifactCount: number
67
+ validationIssues: PlanValidationIssueInput[]
68
+ }) {
69
+ const executionTimeMs = params.nodeStartedAt
70
+ ? nowEpochMillis() - unsafeDateFrom(params.nodeStartedAt).getTime()
71
+ : 0
72
+ if (executionTimeMs > 0) {
73
+ yield* Metric.update(
74
+ Metric.withAttributes(planNodeExecutionDuration, { nodeType: params.nodeType }),
75
+ executionTimeMs,
76
+ )
77
+ }
78
+ yield* qualityMetricsService.recordNodeMetrics({
79
+ organizationId: params.organizationId,
80
+ runId: params.runId,
81
+ nodeId: params.nodeId,
82
+ metrics: {
83
+ executionTimeMs: Math.max(0, executionTimeMs),
84
+ attemptCount: params.nodeAttemptCount,
85
+ artifactCount: params.artifactCount,
86
+ validationIssueCount: params.validationIssues.length,
87
+ ownerRef: params.nodeOwnerRef,
88
+ ownerType: params.nodeOwnerType,
89
+ nodeType: params.nodeType,
90
+ },
91
+ })
92
+ },
93
+ )
97
94
 
98
- function runPlanCompletionSideEffects(params: { runId: string; organizationId: string }): Promise<void> {
99
- return runPromise(
100
- Effect.gen(function* () {
101
- yield* qualityMetricsService.recordCycleMetrics({ organizationId: params.organizationId, runId: params.runId })
95
+ const runPlanCompletionSideEffects = Effect.fn('PlanCompletionSideEffects.runPlanCompletionSideEffects')(
96
+ function* (params: { runId: string; organizationId: string }) {
97
+ yield* qualityMetricsService.recordCycleMetrics({ organizationId: params.organizationId, runId: params.runId })
102
98
 
103
- const recommendations = yield* feedbackLoopService
104
- .analyzeOutcomes({ runId: params.runId, organizationId: params.organizationId })
99
+ const recommendations = yield* feedbackLoopService
100
+ .analyzeOutcomes({ runId: params.runId, organizationId: params.organizationId })
101
+ .pipe(
102
+ Effect.mapError(
103
+ (cause) =>
104
+ new PlanCompletionSideEffectsError({ message: 'Failed to analyze plan feedback outcomes.', cause }),
105
+ ),
106
+ )
107
+ if (recommendations.length > 0) {
108
+ const run = yield* planRunService.getRunById(params.runId)
109
+ const specRecord = yield* planRunService.getPlanSpecById(run.planSpecId)
110
+ const event = yield* tryPlanCompletionPromise('Failed to create feedback analyzed plan event.', () =>
111
+ databaseService.create(
112
+ TABLES.PLAN_EVENT,
113
+ {
114
+ planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
115
+ runId: ensureRecordId(run.id, TABLES.PLAN_RUN),
116
+ eventType: 'feedback-analyzed',
117
+ message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
118
+ detail: { recommendations },
119
+ emittedBy: 'system',
120
+ },
121
+ PlanEventSchema,
122
+ ),
123
+ )
124
+ yield* planEventDeliveryService
125
+ .dispatchEvent(event)
105
126
  .pipe(
106
127
  Effect.mapError(
107
- (cause) =>
108
- new PlanCompletionSideEffectsError({ message: 'Failed to analyze plan feedback outcomes.', cause }),
109
- ),
110
- )
111
- if (recommendations.length > 0) {
112
- const run = yield* planRunService.getRunById(params.runId)
113
- const specRecord = yield* planRunService.getPlanSpecById(run.planSpecId)
114
- const event = yield* tryPlanCompletionPromise('Failed to create feedback analyzed plan event.', () =>
115
- databaseService.create(
116
- TABLES.PLAN_EVENT,
117
- {
118
- planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
119
- runId: ensureRecordId(run.id, TABLES.PLAN_RUN),
120
- eventType: 'feedback-analyzed',
121
- message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
122
- detail: { recommendations },
123
- emittedBy: 'system',
124
- },
125
- PlanEventSchema,
126
- ),
127
- )
128
- yield* planEventDeliveryService
129
- .dispatchEventEffect(event)
130
- .pipe(
131
- Effect.mapError(
132
- (error) =>
133
- new PlanCompletionSideEffectsError({
134
- message: 'Failed to dispatch feedback analyzed plan event.',
135
- cause: error,
136
- }),
137
- ),
138
- )
139
- }
140
-
141
- yield* institutionalMemoryService
142
- .extractPatterns({ organizationId: params.organizationId, runId: params.runId })
143
- .pipe(
144
- Effect.mapError(
145
- (cause) =>
128
+ (error) =>
146
129
  new PlanCompletionSideEffectsError({
147
- message: 'Failed to extract institutional memory patterns.',
148
- cause,
130
+ message: 'Failed to dispatch feedback analyzed plan event.',
131
+ cause: error,
149
132
  }),
150
133
  ),
151
134
  )
152
- }),
153
- )
154
- }
135
+ }
136
+
137
+ yield* institutionalMemoryService
138
+ .extractPatterns({ organizationId: params.organizationId, runId: params.runId })
139
+ .pipe(
140
+ Effect.mapError(
141
+ (cause) =>
142
+ new PlanCompletionSideEffectsError({
143
+ message: 'Failed to extract institutional memory patterns.',
144
+ cause,
145
+ }),
146
+ ),
147
+ )
148
+ },
149
+ )
155
150
 
156
- function runPlanCompletionSideEffectsSafely(params: { runId: string; organizationId: string }): Promise<void> {
157
- return runPromise(
158
- Effect.catch(
159
- tryPlanCompletionPromise('Plan completion side effects failed.', () => runPlanCompletionSideEffects(params)),
160
- (error: unknown) =>
161
- Effect.sync(() => {
162
- aiLogger.warn`Plan completion side effects failed for run ${params.runId}: ${error instanceof Error ? error.message : String(error)}`
163
- }),
151
+ const runPlanCompletionSideEffectsSafely = (params: { runId: string; organizationId: string }) =>
152
+ runPlanCompletionSideEffects(params).pipe(
153
+ Effect.catch((error) =>
154
+ Effect.sync(() => {
155
+ aiLogger.warn`Plan completion side effects failed for run ${params.runId}: ${error instanceof Error ? error.message : String(error)}`
156
+ }),
164
157
  ),
165
158
  )
166
- }
167
159
 
168
160
  return { runPlanNodeCompletionSideEffects, runPlanCompletionSideEffectsSafely }
169
161
  }
@@ -3,7 +3,6 @@ import type {
3
3
  CarryForwardPolicy,
4
4
  CycleSchedule,
5
5
  PlanArtifactRecord,
6
- PlanCycleRecord,
7
6
  PlanDraft,
8
7
  PlanRunStatus,
9
8
  PlanScheduleSpec,
@@ -15,7 +14,6 @@ import type { RecordIdInput } from '../../db/record-id'
15
14
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
16
15
  import type { SurrealDBService } from '../../db/service'
17
16
  import { TABLES } from '../../db/tables'
18
- import { runPromise } from '../../effect/runtime'
19
17
  import { DatabaseServiceTag } from '../../effect/services'
20
18
  import { nowDate, nowEpochMillis, unsafeDateFrom } from '../../utils/date-time'
21
19
  import type { makePlanSchedulerService } from './plan-scheduler.service'
@@ -336,24 +334,8 @@ export function makePlanCycleService(deps: PlanCycleDeps) {
336
334
 
337
335
  return {
338
336
  cycleScheduleToSpec,
339
- createCycle(params: {
340
- organizationId: RecordIdInput
341
- threadId: RecordIdInput
342
- templateId: RecordIdInput
343
- name: string
344
- schedule: CycleSchedule
345
- carryForwardPolicy?: CarryForwardPolicy
346
- leadAgentId: string
347
- }): Promise<PlanCycleRecord> {
348
- return runPromise(createCycleEffect(params))
349
- },
350
- createCycleEffect,
351
-
352
- advanceCycle(cycleId: RecordIdInput): Promise<void> {
353
- return runPromise(advanceCycleEffect(cycleId))
354
- },
355
- advanceCycleEffect,
356
-
337
+ createCycle: createCycleEffect,
338
+ advanceCycle: advanceCycleEffect,
357
339
  buildCarryForwardDraft(params: {
358
340
  template: PlanTemplateRecord
359
341
  previousRunArtifacts: PlanArtifactRecord[]
@@ -361,31 +343,11 @@ export function makePlanCycleService(deps: PlanCycleDeps) {
361
343
  }): PlanDraft {
362
344
  return buildCarryForwardDraft(params)
363
345
  },
364
-
365
- cancelCycle(cycleId: RecordIdInput): Promise<void> {
366
- return runPromise(cancelCycleEffect(cycleId))
367
- },
368
- cancelCycleEffect,
369
-
370
- pauseCycle(cycleId: RecordIdInput): Promise<void> {
371
- return runPromise(pauseCycleEffect(cycleId))
372
- },
373
- pauseCycleEffect,
374
-
375
- resumeCycle(cycleId: RecordIdInput): Promise<void> {
376
- return runPromise(resumeCycleEffect(cycleId))
377
- },
378
- resumeCycleEffect,
379
-
380
- listCycles(threadId: RecordIdInput): Promise<PlanCycleRecord[]> {
381
- return runPromise(listCyclesEffect(threadId))
382
- },
383
- listCyclesEffect,
384
-
385
- getCycle(cycleId: RecordIdInput): Promise<PlanCycleRecord | null> {
386
- return runPromise(getCycleEffect(cycleId))
387
- },
388
- getCycleEffect,
346
+ cancelCycle: cancelCycleEffect,
347
+ pauseCycle: pauseCycleEffect,
348
+ resumeCycle: resumeCycleEffect,
349
+ listCycles: listCyclesEffect,
350
+ getCycle: getCycleEffect,
389
351
  }
390
352
  }
391
353
 
@@ -17,6 +17,8 @@ import type { SurrealDBService } from '../../db/service'
17
17
  import { TABLES } from '../../db/tables'
18
18
  import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
19
19
  import { DatabaseServiceTag } from '../../effect/services'
20
+ import type { PlanSchedulerQueueRuntime } from '../../queues/plan-scheduler.queue'
21
+ import { LotaQueuesServiceTag } from '../../queues/queues.service'
20
22
  import { nowDate, unsafeDateFrom } from '../../utils/date-time'
21
23
  import type { makePlanEventDeliveryService } from './plan-event-delivery.service'
22
24
  import { PlanEventDeliveryServiceTag } from './plan-event-delivery.service'
@@ -86,6 +88,7 @@ interface PlanDeadlineDeps {
86
88
  planExecutorService: ReturnType<typeof makePlanExecutorService>
87
89
  planEventDeliveryService: ReturnType<typeof makePlanEventDeliveryService>
88
90
  planRunService: ReturnType<typeof makePlanRunService>
91
+ planSchedulerQueue: PlanSchedulerQueueRuntime
89
92
  }
90
93
 
91
94
  class PlanDeadlineError extends Schema.TaggedErrorClass<PlanDeadlineError>()('PlanDeadlineError', {
@@ -94,12 +97,12 @@ class PlanDeadlineError extends Schema.TaggedErrorClass<PlanDeadlineError>()('Pl
94
97
  }) {}
95
98
 
96
99
  export function makePlanDeadlineService(deps: PlanDeadlineDeps) {
97
- const { db, planEventDeliveryService, planExecutorService, planRunService } = deps
100
+ const { db, planEventDeliveryService, planExecutorService, planRunService, planSchedulerQueue } = deps
98
101
  const effectTryPromise = makeEffectTryPromiseWithMessage(
99
102
  (message, cause) => new PlanDeadlineError({ message, cause }),
100
103
  )
101
- const loadPlanSchedulerQueue = () =>
102
- effectTryPromise(() => import('../../queues/plan-scheduler.queue'), 'Failed to load plan scheduler queue module.')
104
+ const loadPlanSchedulerQueue = (): Effect.Effect<PlanSchedulerQueueRuntime, PlanDeadlineError> =>
105
+ Effect.succeed(planSchedulerQueue)
103
106
 
104
107
  function loadNodeSpec(nodeRun: PlanNodeRunRecord): Effect.Effect<PlanNodeSpecRecord | null, PlanDeadlineError> {
105
108
  return db
@@ -219,7 +222,7 @@ export function makePlanDeadlineService(deps: PlanDeadlineDeps) {
219
222
  'Failed to create deadline event.',
220
223
  )
221
224
  yield* planEventDeliveryService
222
- .dispatchEventEffect(PlanEventSchema.parse(event))
225
+ .dispatchEvent(PlanEventSchema.parse(event))
223
226
  .pipe(
224
227
  Effect.mapError(
225
228
  (error) => new PlanDeadlineError({ message: 'Failed to dispatch deadline event.', cause: error }),
@@ -253,18 +256,19 @@ export function makePlanDeadlineService(deps: PlanDeadlineDeps) {
253
256
  ),
254
257
  ])
255
258
  const blockNode = (message: string): Effect.Effect<void, PlanDeadlineError> =>
256
- effectTryPromise(
257
- () =>
258
- planExecutorService.blockNodeOnDispatchFailure({
259
- threadId: params.threadId,
260
- runId: runIdStr,
261
- nodeId: params.nodeId,
262
- emittedBy: params.emittedBy,
263
- message,
264
- failureClass: 'timeout_exceeded',
265
- }),
266
- `Failed to ${message.toLowerCase()}.`,
267
- )
259
+ planExecutorService
260
+ .blockNodeOnDispatchFailure({
261
+ threadId: params.threadId,
262
+ runId: runIdStr,
263
+ nodeId: params.nodeId,
264
+ emittedBy: params.emittedBy,
265
+ message,
266
+ failureClass: 'timeout_exceeded',
267
+ })
268
+ .pipe(
269
+ Effect.asVoid,
270
+ Effect.mapError((cause) => new PlanDeadlineError({ message: `Failed to ${message.toLowerCase()}.`, cause })),
271
+ )
268
272
 
269
273
  switch (params.action) {
270
274
  case 'notify':
@@ -528,6 +532,13 @@ export const PlanDeadlineServiceLive = Layer.effect(
528
532
  const planExecutorService = yield* PlanExecutorServiceTag
529
533
  const planEventDeliveryService = yield* PlanEventDeliveryServiceTag
530
534
  const planRunService = yield* PlanRunServiceTag
531
- return makePlanDeadlineService({ db, planExecutorService, planEventDeliveryService, planRunService })
535
+ const queues = yield* LotaQueuesServiceTag
536
+ return makePlanDeadlineService({
537
+ db,
538
+ planExecutorService,
539
+ planEventDeliveryService,
540
+ planRunService,
541
+ planSchedulerQueue: queues.planScheduler,
542
+ })
532
543
  }),
533
544
  )
@@ -1,15 +1,21 @@
1
1
  import type { PlanEventRecord } from '@lota-sdk/shared'
2
2
  import { Context, Schema, Effect, Layer } from 'effect'
3
3
 
4
+ import type { ResolvedAgentConfig } from '../../config/agent-defaults'
4
5
  import { ensureRecordId, recordIdToString } from '../../db/record-id'
5
6
  import type { SurrealDBService } from '../../db/service'
6
7
  import { TABLES } from '../../db/tables'
7
- import { runPromise } from '../../effect/runtime'
8
- import { DatabaseServiceTag, RedisServiceTag } from '../../effect/services'
8
+ import {
9
+ AgentConfigServiceTag,
10
+ DatabaseServiceTag,
11
+ RedisServiceTag,
12
+ RuntimeAdaptersServiceTag,
13
+ } from '../../effect/services'
14
+ import type { PlanAgentHeartbeatQueueRuntime } from '../../queues/plan-agent-heartbeat.queue'
15
+ import { LotaQueuesServiceTag } from '../../queues/queues.service'
9
16
  import type { RedisConnectionManager } from '../../redis/connection'
10
17
  import { resolvePlanNodeExecutionVisibility } from '../../runtime/execution-plan-visibility'
11
- import { getRuntimeAdapters } from '../../runtime/runtime-extensions'
12
- import type { LotaRuntimePlanEventEnvelope } from '../../runtime/runtime-extensions'
18
+ import type { LotaRuntimeAdapters, LotaRuntimePlanEventEnvelope } from '../../runtime/runtime-extensions'
13
19
  import { getErrorMessage } from '../../utils/errors'
14
20
  import { ThreadSchema } from '../thread/thread.types'
15
21
  import type { makeUserService } from '../user.service'
@@ -28,19 +34,22 @@ function buildDeliveredKey(eventId: string): string {
28
34
  return `plan-event:delivered:${eventId}`
29
35
  }
30
36
 
31
- function dispatchAdapterEvent(envelope: LotaRuntimePlanEventEnvelope) {
32
- const adapter = getRuntimeAdapters().planEventAdapter
33
- if (!adapter) {
34
- return Effect.void
35
- }
37
+ function dispatchAdapterEvent(runtimeAdapters: LotaRuntimeAdapters, envelope: LotaRuntimePlanEventEnvelope) {
38
+ return Effect.gen(function* () {
39
+ const adapter = runtimeAdapters.planEventAdapter
40
+ if (!adapter) {
41
+ return
42
+ }
36
43
 
37
- return Effect.tryPromise({
38
- try: () => adapter.onPlanEvent(envelope),
39
- catch: (error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error }),
44
+ yield* Effect.tryPromise({
45
+ try: () => adapter.onPlanEvent(envelope),
46
+ catch: (error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error }),
47
+ })
40
48
  })
41
49
  }
42
50
 
43
51
  function resolveWakeTarget(
52
+ agentConfig: ResolvedAgentConfig,
44
53
  envelope: LotaRuntimePlanEventEnvelope,
45
54
  nodeSpecsById: Map<string, NonNullable<LotaRuntimePlanEventEnvelope['nodeSpec']>>,
46
55
  ): { nodeId: string; agentId: string; reason: string } | null {
@@ -55,7 +64,7 @@ function resolveWakeTarget(
55
64
  return null
56
65
  }
57
66
 
58
- const isVisible = resolvePlanNodeExecutionVisibility(envelope.spec, effectiveNodeSpec) === 'visible'
67
+ const isVisible = resolvePlanNodeExecutionVisibility(agentConfig, envelope.spec, effectiveNodeSpec) === 'visible'
59
68
  if (!isVisible) {
60
69
  return null
61
70
  }
@@ -76,22 +85,20 @@ function resolveWakeTarget(
76
85
  }
77
86
 
78
87
  function enqueueWakeIfNeeded(
88
+ agentConfig: ResolvedAgentConfig,
79
89
  envelope: LotaRuntimePlanEventEnvelope,
80
90
  nodeSpecsById: Map<string, NonNullable<LotaRuntimePlanEventEnvelope['nodeSpec']>>,
91
+ planAgentHeartbeatQueue: PlanAgentHeartbeatQueueRuntime,
81
92
  ) {
82
93
  return Effect.gen(function* () {
83
- const wakeTarget = resolveWakeTarget(envelope, nodeSpecsById)
94
+ const wakeTarget = resolveWakeTarget(agentConfig, envelope, nodeSpecsById)
84
95
  if (!wakeTarget) {
85
96
  return
86
97
  }
87
98
 
88
- const { enqueuePlanAgentHeartbeatWake } = yield* Effect.tryPromise({
89
- try: () => import('../../queues/plan-agent-heartbeat.queue'),
90
- catch: (error) => new PlanEventDeliveryError({ message: getErrorMessage(error), cause: error }),
91
- })
92
99
  yield* Effect.tryPromise({
93
100
  try: () =>
94
- enqueuePlanAgentHeartbeatWake({
101
+ planAgentHeartbeatQueue.enqueuePlanAgentHeartbeatWake({
95
102
  organizationId: envelope.organizationId,
96
103
  threadId: envelope.threadId,
97
104
  runId: envelope.runId,
@@ -105,14 +112,17 @@ function enqueueWakeIfNeeded(
105
112
  }
106
113
 
107
114
  interface PlanEventDeliveryDeps {
115
+ agentConfig: ResolvedAgentConfig
108
116
  db: SurrealDBService
109
117
  redis: RedisConnectionManager
110
118
  planRunService: ReturnType<typeof makePlanRunService>
111
119
  userService: ReturnType<typeof makeUserService>
120
+ planAgentHeartbeatQueue: PlanAgentHeartbeatQueueRuntime
121
+ runtimeAdapters: LotaRuntimeAdapters
112
122
  }
113
123
 
114
124
  export function makePlanEventDeliveryService(deps: PlanEventDeliveryDeps) {
115
- const { db, redis, planRunService, userService } = deps
125
+ const { agentConfig, db, redis, planRunService, userService, planAgentHeartbeatQueue, runtimeAdapters } = deps
116
126
 
117
127
  function deliverEvent(event: PlanEventRecord) {
118
128
  return Effect.gen(function* () {
@@ -182,8 +192,8 @@ export function makePlanEventDeliveryService(deps: PlanEventDeliveryDeps) {
182
192
  ...(userName ? { userName } : {}),
183
193
  }
184
194
 
185
- yield* dispatchAdapterEvent(envelope)
186
- yield* enqueueWakeIfNeeded(envelope, nodeSpecsById)
195
+ yield* dispatchAdapterEvent(runtimeAdapters, envelope)
196
+ yield* enqueueWakeIfNeeded(agentConfig, envelope, nodeSpecsById, planAgentHeartbeatQueue)
187
197
  })
188
198
  }
189
199
 
@@ -229,23 +239,9 @@ export function makePlanEventDeliveryService(deps: PlanEventDeliveryDeps) {
229
239
  })
230
240
 
231
241
  return {
232
- dispatchEvents(events: PlanEventRecord[]): Promise<void> {
233
- return runPromise(dispatchEventsEffect(events))
234
- },
235
-
236
- dispatchEventsEffect,
237
-
238
- dispatchEvent(event: PlanEventRecord): Promise<void> {
239
- return runPromise(dispatchEventEffect(event))
240
- },
241
-
242
- dispatchEventEffect,
243
-
244
- dispatchUndeliveredEvents(runId: string, options?: { limit?: number }): Promise<void> {
245
- return runPromise(dispatchUndeliveredEventsEffect(runId, options))
246
- },
247
-
248
- dispatchUndeliveredEventsEffect,
242
+ dispatchEvents: dispatchEventsEffect,
243
+ dispatchEvent: dispatchEventEffect,
244
+ dispatchUndeliveredEvents: dispatchUndeliveredEventsEffect,
249
245
  }
250
246
  }
251
247
 
@@ -257,10 +253,21 @@ export class PlanEventDeliveryServiceTag extends Context.Service<
257
253
  export const PlanEventDeliveryServiceLive = Layer.effect(
258
254
  PlanEventDeliveryServiceTag,
259
255
  Effect.gen(function* () {
256
+ const agentConfig = yield* AgentConfigServiceTag
260
257
  const db = yield* DatabaseServiceTag
261
258
  const redis = yield* RedisServiceTag
262
259
  const planRunService = yield* PlanRunServiceTag
263
260
  const userService = yield* UserServiceTag
264
- return makePlanEventDeliveryService({ db, redis, planRunService, userService })
261
+ const queues = yield* LotaQueuesServiceTag
262
+ const runtimeAdapters = yield* RuntimeAdaptersServiceTag
263
+ return makePlanEventDeliveryService({
264
+ agentConfig,
265
+ db,
266
+ redis,
267
+ planRunService,
268
+ userService,
269
+ planAgentHeartbeatQueue: queues.planAgentHeartbeat,
270
+ runtimeAdapters,
271
+ })
265
272
  }),
266
273
  )
@@ -1,6 +1,7 @@
1
1
  import type { Context } from 'effect'
2
2
 
3
3
  import type { SurrealDBService } from '../../db/service'
4
+ import type { DelayedNodePromotionQueueRuntime } from '../../queues/delayed-node-promotion.queue'
4
5
  import type { makeGeneratedDocumentStorageService } from '../../storage/generated-document-storage.service'
5
6
  import type { makeArtifactService } from '../artifact.service'
6
7
  import type { FeedbackLoopServiceTag } from '../feedback-loop.service'
@@ -32,4 +33,5 @@ export interface PlanExecutorContext {
32
33
  planValidatorService: ReturnType<typeof makePlanValidatorService>
33
34
  qualityMetricsService: Context.Service.Shape<typeof QualityMetricsServiceTag>
34
35
  planCompletionSideEffects: ReturnType<typeof makePlanCompletionSideEffects>
36
+ delayedNodePromotionQueue: DelayedNodePromotionQueueRuntime
35
37
  }