@lota-sdk/core 0.4.9 → 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 (182) hide show
  1. package/package.json +2 -2
  2. package/src/ai/embedding-cache.ts +3 -1
  3. package/src/ai-gateway/ai-gateway.ts +164 -82
  4. package/src/ai-gateway/index.ts +16 -1
  5. package/src/config/agent-defaults.ts +4 -107
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/config/background-processing.ts +1 -1
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +22 -25
  10. package/src/config/thread-defaults.ts +1 -10
  11. package/src/create-runtime.ts +145 -670
  12. package/src/db/base.service.ts +30 -38
  13. package/src/db/memory-query-builder.ts +2 -1
  14. package/src/db/memory-store.ts +29 -20
  15. package/src/db/memory.ts +188 -195
  16. package/src/db/service-normalization.ts +97 -64
  17. package/src/db/service.ts +496 -384
  18. package/src/db/startup.ts +30 -19
  19. package/src/effect/helpers.ts +30 -5
  20. package/src/effect/index.ts +7 -7
  21. package/src/effect/layers.ts +75 -72
  22. package/src/effect/services.ts +15 -11
  23. package/src/embeddings/provider.ts +65 -71
  24. package/src/index.ts +13 -12
  25. package/src/queues/autonomous-job.queue.ts +177 -143
  26. package/src/queues/context-compaction.queue.ts +41 -39
  27. package/src/queues/delayed-node-promotion.queue.ts +61 -42
  28. package/src/queues/document-processor.queue.ts +5 -3
  29. package/src/queues/index.ts +1 -0
  30. package/src/queues/memory-consolidation.queue.ts +79 -53
  31. package/src/queues/organization-learning.queue.ts +70 -33
  32. package/src/queues/plan-agent-heartbeat.queue.ts +111 -83
  33. package/src/queues/plan-scheduler.queue.ts +101 -97
  34. package/src/queues/post-chat-memory.queue.ts +56 -46
  35. package/src/queues/queue-factory.ts +146 -69
  36. package/src/queues/queues.service.ts +61 -0
  37. package/src/queues/title-generation.queue.ts +44 -44
  38. package/src/redis/connection.ts +181 -164
  39. package/src/redis/org-memory-lock.ts +24 -9
  40. package/src/redis/redis-lease-lock.ts +8 -1
  41. package/src/redis/stream-context.ts +17 -9
  42. package/src/runtime/agent-identity-overrides.ts +7 -3
  43. package/src/runtime/agent-runtime-policy.ts +10 -5
  44. package/src/runtime/agent-stream-helpers.ts +24 -15
  45. package/src/runtime/chat-run-orchestration.ts +1 -1
  46. package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
  47. package/src/runtime/context-compaction/context-compaction.ts +131 -85
  48. package/src/runtime/domain-layer.ts +203 -0
  49. package/src/runtime/execution-plan-visibility.ts +5 -2
  50. package/src/runtime/graph-designer.ts +0 -14
  51. package/src/runtime/helper-model.ts +8 -4
  52. package/src/runtime/index.ts +1 -1
  53. package/src/runtime/indexed-repositories-policy.ts +2 -6
  54. package/src/runtime/memory/memory-block.ts +19 -9
  55. package/src/runtime/memory/memory-pipeline.ts +53 -66
  56. package/src/runtime/memory/memory-scope.ts +33 -29
  57. package/src/runtime/plugin-resolution.ts +58 -62
  58. package/src/runtime/post-turn-side-effects.ts +139 -161
  59. package/src/runtime/retrieval-adapters.ts +4 -4
  60. package/src/runtime/runtime-config.ts +3 -9
  61. package/src/runtime/runtime-extensions.ts +0 -43
  62. package/src/runtime/runtime-lifecycle.ts +124 -0
  63. package/src/runtime/runtime-services.ts +455 -0
  64. package/src/runtime/runtime-worker-registry.ts +113 -30
  65. package/src/runtime/social-chat/social-chat-agent-runner.ts +13 -8
  66. package/src/runtime/social-chat/social-chat-history.ts +24 -13
  67. package/src/runtime/social-chat/social-chat.ts +420 -369
  68. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +64 -57
  69. package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
  70. package/src/runtime/thread-chat-helpers.ts +18 -9
  71. package/src/runtime/thread-turn-context.ts +28 -74
  72. package/src/runtime/turn-lifecycle.ts +6 -14
  73. package/src/services/agent-activity.service.ts +169 -176
  74. package/src/services/agent-executor.service.ts +207 -196
  75. package/src/services/artifact.service.ts +10 -5
  76. package/src/services/attachment.service.ts +16 -48
  77. package/src/services/autonomous-job.service.ts +81 -87
  78. package/src/services/background-work.service.ts +54 -0
  79. package/src/services/chat-run-registry.service.ts +3 -1
  80. package/src/services/context-compaction.service.ts +8 -10
  81. package/src/services/document-chunk.service.ts +8 -17
  82. package/src/services/execution-plan/execution-plan-graph.ts +122 -109
  83. package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
  84. package/src/services/execution-plan/execution-plan.service.ts +68 -51
  85. package/src/services/feedback-loop.service.ts +1 -1
  86. package/src/services/global-orchestrator.service.ts +49 -15
  87. package/src/services/graph-full-routing.ts +49 -37
  88. package/src/services/index.ts +1 -0
  89. package/src/services/institutional-memory.service.ts +8 -17
  90. package/src/services/learned-skill.service.ts +38 -35
  91. package/src/services/memory/memory-conversation.ts +10 -5
  92. package/src/services/memory/memory-errors.ts +27 -0
  93. package/src/services/memory/memory-org-memory.ts +14 -3
  94. package/src/services/memory/memory-preseeded.ts +10 -4
  95. package/src/services/memory/memory-utils.ts +2 -1
  96. package/src/services/memory/memory.service.ts +37 -52
  97. package/src/services/memory/rerank.service.ts +3 -11
  98. package/src/services/monitoring-window.service.ts +1 -1
  99. package/src/services/mutating-approval.service.ts +1 -1
  100. package/src/services/node-workspace.service.ts +2 -2
  101. package/src/services/notification.service.ts +16 -4
  102. package/src/services/organization-member.service.ts +1 -1
  103. package/src/services/organization.service.ts +34 -51
  104. package/src/services/ownership-dispatcher.service.ts +148 -95
  105. package/src/services/plan/plan-agent-heartbeat.service.ts +30 -16
  106. package/src/services/plan/plan-agent-query.service.ts +13 -9
  107. package/src/services/plan/plan-approval.service.ts +52 -48
  108. package/src/services/plan/plan-artifact.service.ts +2 -2
  109. package/src/services/plan/plan-builder.service.ts +2 -2
  110. package/src/services/plan/plan-checkpoint.service.ts +1 -1
  111. package/src/services/plan/plan-compiler.service.ts +1 -1
  112. package/src/services/plan/plan-completion-side-effects.ts +99 -113
  113. package/src/services/plan/plan-coordination.service.ts +1 -1
  114. package/src/services/plan/plan-cycle.service.ts +171 -202
  115. package/src/services/plan/plan-deadline.service.ts +304 -307
  116. package/src/services/plan/plan-event-delivery.service.ts +84 -72
  117. package/src/services/plan/plan-executor-context.ts +2 -0
  118. package/src/services/plan/plan-executor-graph.ts +375 -353
  119. package/src/services/plan/plan-executor-helpers.ts +60 -75
  120. package/src/services/plan/plan-executor.service.ts +494 -489
  121. package/src/services/plan/plan-run.service.ts +12 -19
  122. package/src/services/plan/plan-scheduler.service.ts +89 -82
  123. package/src/services/plan/plan-template.service.ts +1 -1
  124. package/src/services/plan/plan-transaction-events.ts +8 -5
  125. package/src/services/plan/plan-validator.service.ts +1 -1
  126. package/src/services/plan/plan-workspace.service.ts +17 -11
  127. package/src/services/plugin-executor.service.ts +26 -21
  128. package/src/services/quality-metrics.service.ts +1 -1
  129. package/src/services/queue-job.service.ts +8 -17
  130. package/src/services/recent-activity-title.service.ts +22 -10
  131. package/src/services/recent-activity.service.ts +1 -1
  132. package/src/services/skill-resolver.service.ts +1 -1
  133. package/src/services/social-chat-history.service.ts +37 -20
  134. package/src/services/system-executor.service.ts +25 -20
  135. package/src/services/thread/thread-bootstrap.ts +37 -19
  136. package/src/services/thread/thread-listing.ts +2 -1
  137. package/src/services/thread/thread-memory-block.ts +18 -5
  138. package/src/services/thread/thread-message.service.ts +30 -13
  139. package/src/services/thread/thread-title.service.ts +1 -1
  140. package/src/services/thread/thread-turn-execution.ts +87 -83
  141. package/src/services/thread/thread-turn-preparation.service.ts +65 -40
  142. package/src/services/thread/thread-turn-streaming.ts +32 -36
  143. package/src/services/thread/thread-turn.ts +43 -29
  144. package/src/services/thread/thread.service.ts +32 -8
  145. package/src/services/user.service.ts +1 -1
  146. package/src/services/write-intent-validator.service.ts +1 -1
  147. package/src/storage/attachment-storage.service.ts +7 -4
  148. package/src/storage/generated-document-storage.service.ts +1 -1
  149. package/src/system-agents/context-compaction.agent.ts +1 -1
  150. package/src/system-agents/helper-agent-options.ts +1 -1
  151. package/src/system-agents/memory-reranker.agent.ts +1 -1
  152. package/src/system-agents/memory.agent.ts +1 -1
  153. package/src/system-agents/recent-activity-title-refiner.agent.ts +9 -6
  154. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  155. package/src/system-agents/skill-extractor.agent.ts +1 -1
  156. package/src/system-agents/skill-manager.agent.ts +1 -1
  157. package/src/system-agents/thread-router.agent.ts +23 -20
  158. package/src/system-agents/title-generator.agent.ts +1 -1
  159. package/src/tools/execution-plan.tool.ts +36 -20
  160. package/src/tools/fetch-webpage.tool.ts +30 -22
  161. package/src/tools/firecrawl-client.ts +1 -6
  162. package/src/tools/plan-approval.tool.ts +9 -1
  163. package/src/tools/remember-memory.tool.ts +3 -6
  164. package/src/tools/research-topic.tool.ts +12 -3
  165. package/src/tools/search-web.tool.ts +26 -18
  166. package/src/tools/search.tool.ts +4 -5
  167. package/src/tools/team-think.tool.ts +139 -121
  168. package/src/utils/async.ts +15 -6
  169. package/src/utils/errors.ts +27 -15
  170. package/src/workers/bootstrap.ts +34 -58
  171. package/src/workers/memory-consolidation.worker.ts +4 -1
  172. package/src/workers/organization-learning.worker.ts +16 -3
  173. package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
  174. package/src/workers/regular-chat-memory-digest.runner.ts +46 -29
  175. package/src/workers/skill-extraction.runner.ts +13 -15
  176. package/src/workers/worker-utils.ts +14 -8
  177. package/src/config/search.ts +0 -3
  178. package/src/effect/awaitable-effect.ts +0 -87
  179. package/src/effect/runtime-ref.ts +0 -25
  180. package/src/effect/runtime.ts +0 -31
  181. package/src/redis/runtime-connection.ts +0 -10
  182. package/src/runtime/agent-types.ts +0 -1
@@ -12,48 +12,53 @@ import { nowDate } from '../../utils/date-time'
12
12
  import type { HumanNodeResponsePayload } from './plan-executor-helpers'
13
13
 
14
14
  export function makePlanApprovalService(db: SurrealDBService) {
15
- const createPendingApprovalEffect = (params: {
15
+ const createPendingApprovalEffect = Effect.fn('PlanApproval.createPendingApproval')(function* (params: {
16
16
  tx: DatabaseTransaction
17
17
  runId: RecordIdInput
18
18
  nodeRunId: RecordIdInput
19
19
  nodeId: string
20
20
  requestedBy: string
21
21
  presented: Record<string, unknown>
22
- }) =>
23
- Effect.gen(function* () {
24
- const approvalId = new RecordId(TABLES.PLAN_APPROVAL, Bun.randomUUIDv7())
25
- const created = yield* params.tx
26
- .create(approvalId)
27
- .content({
28
- runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
29
- nodeRunId: ensureRecordId(params.nodeRunId, TABLES.PLAN_NODE_RUN),
30
- nodeId: params.nodeId,
31
- status: 'pending',
32
- requestedBy: params.requestedBy,
33
- presented: params.presented,
34
- requiredEdits: [],
35
- })
36
- .output('after')
22
+ }) {
23
+ const approvalId = new RecordId(TABLES.PLAN_APPROVAL, Bun.randomUUIDv7())
24
+ const created = yield* params.tx
25
+ .create(approvalId)
26
+ .content({
27
+ runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
28
+ nodeRunId: ensureRecordId(params.nodeRunId, TABLES.PLAN_NODE_RUN),
29
+ nodeId: params.nodeId,
30
+ status: 'pending',
31
+ requestedBy: params.requestedBy,
32
+ presented: params.presented,
33
+ requiredEdits: [],
34
+ })
35
+ .output('after')
37
36
 
38
- return PlanApprovalSchema.parse(created)
39
- })
37
+ return PlanApprovalSchema.parse(created)
38
+ })
40
39
 
41
- const getApprovalByIdEffect = (approvalId: RecordIdInput) =>
42
- db.findOne(TABLES.PLAN_APPROVAL, { id: ensureRecordId(approvalId, TABLES.PLAN_APPROVAL) }, PlanApprovalSchema)
40
+ const getApprovalByIdEffect = Effect.fn('PlanApproval.getApprovalById')(function* (approvalId: RecordIdInput) {
41
+ return yield* db.findOne(
42
+ TABLES.PLAN_APPROVAL,
43
+ { id: ensureRecordId(approvalId, TABLES.PLAN_APPROVAL) },
44
+ PlanApprovalSchema,
45
+ )
46
+ })
43
47
 
44
- const getPendingApprovalForNodeRunEffect = (nodeRunId: RecordIdInput) =>
45
- Effect.gen(function* () {
46
- const approvals = yield* db.findMany(
47
- TABLES.PLAN_APPROVAL,
48
- { nodeRunId: ensureRecordId(nodeRunId, TABLES.PLAN_NODE_RUN), status: 'pending' },
49
- PlanApprovalSchema,
50
- { orderBy: 'createdAt', orderDir: 'DESC', limit: 1 },
51
- )
48
+ const getPendingApprovalForNodeRunEffect = Effect.fn('PlanApproval.getPendingApprovalForNodeRun')(function* (
49
+ nodeRunId: RecordIdInput,
50
+ ) {
51
+ const approvals = yield* db.findMany(
52
+ TABLES.PLAN_APPROVAL,
53
+ { nodeRunId: ensureRecordId(nodeRunId, TABLES.PLAN_NODE_RUN), status: 'pending' },
54
+ PlanApprovalSchema,
55
+ { orderBy: 'createdAt', orderDir: 'DESC', limit: 1 },
56
+ )
52
57
 
53
- return approvals.at(0) ?? null
54
- })
58
+ return approvals.at(0) ?? null
59
+ })
55
60
 
56
- const updateApprovalResponseEffect = (params: {
61
+ const updateApprovalResponseEffect = Effect.fn('PlanApproval.updateApprovalResponse')(function* (params: {
57
62
  tx: DatabaseTransaction
58
63
  approval: PlanApprovalRecord
59
64
  status: PlanApprovalStatus
@@ -62,23 +67,22 @@ export function makePlanApprovalService(db: SurrealDBService) {
62
67
  approvalMessageId?: string
63
68
  comments?: string
64
69
  requiredEdits?: string[]
65
- }) =>
66
- Effect.gen(function* () {
67
- const updated = yield* params.tx
68
- .update(ensureRecordId(params.approval.id, TABLES.PLAN_APPROVAL))
69
- .merge({
70
- status: params.status,
71
- response: params.response,
72
- respondedBy: params.respondedBy,
73
- ...(params.approvalMessageId ? { approvalMessageId: params.approvalMessageId } : {}),
74
- ...(params.comments ? { comments: params.comments } : {}),
75
- ...(params.requiredEdits ? { requiredEdits: params.requiredEdits } : {}),
76
- respondedAt: nowDate(),
77
- })
78
- .output('after')
70
+ }) {
71
+ const updated = yield* params.tx
72
+ .update(ensureRecordId(params.approval.id, TABLES.PLAN_APPROVAL))
73
+ .merge({
74
+ status: params.status,
75
+ response: params.response,
76
+ respondedBy: params.respondedBy,
77
+ ...(params.approvalMessageId ? { approvalMessageId: params.approvalMessageId } : {}),
78
+ ...(params.comments ? { comments: params.comments } : {}),
79
+ ...(params.requiredEdits ? { requiredEdits: params.requiredEdits } : {}),
80
+ respondedAt: nowDate(),
81
+ })
82
+ .output('after')
79
83
 
80
- return PlanApprovalSchema.parse(updated)
81
- })
84
+ return PlanApprovalSchema.parse(updated)
85
+ })
82
86
 
83
87
  return {
84
88
  createPendingApproval: createPendingApprovalEffect,
@@ -91,7 +95,7 @@ export function makePlanApprovalService(db: SurrealDBService) {
91
95
  export class PlanApprovalServiceTag extends Context.Service<
92
96
  PlanApprovalServiceTag,
93
97
  ReturnType<typeof makePlanApprovalService>
94
- >()('PlanApprovalService') {}
98
+ >()('@lota-sdk/core/PlanApprovalService') {}
95
99
 
96
100
  export const PlanApprovalServiceLive = Layer.effect(
97
101
  PlanApprovalServiceTag,
@@ -55,6 +55,6 @@ export function makePlanArtifactService() {
55
55
  export class PlanArtifactServiceTag extends Context.Service<
56
56
  PlanArtifactServiceTag,
57
57
  ReturnType<typeof makePlanArtifactService>
58
- >()('PlanArtifactService') {}
58
+ >()('@lota-sdk/core/PlanArtifactService') {}
59
59
 
60
- export const PlanArtifactServiceLive = Layer.succeed(PlanArtifactServiceTag, makePlanArtifactService())
60
+ export const PlanArtifactServiceLive = Layer.sync(PlanArtifactServiceTag, () => makePlanArtifactService())
@@ -71,6 +71,6 @@ export function makePlanBuilderService() {
71
71
  export class PlanBuilderServiceTag extends Context.Service<
72
72
  PlanBuilderServiceTag,
73
73
  ReturnType<typeof makePlanBuilderService>
74
- >()('PlanBuilderService') {}
74
+ >()('@lota-sdk/core/PlanBuilderService') {}
75
75
 
76
- export const PlanBuilderServiceLive = Layer.succeed(PlanBuilderServiceTag, makePlanBuilderService())
76
+ export const PlanBuilderServiceLive = Layer.sync(PlanBuilderServiceTag, () => makePlanBuilderService())
@@ -91,7 +91,7 @@ export function makePlanCheckpointService(deps: PlanCheckpointDeps) {
91
91
  export class PlanCheckpointServiceTag extends Context.Service<
92
92
  PlanCheckpointServiceTag,
93
93
  ReturnType<typeof makePlanCheckpointService>
94
- >()('PlanCheckpointService') {}
94
+ >()('@lota-sdk/core/PlanCheckpointService') {}
95
95
 
96
96
  export const PlanCheckpointServiceLive = Layer.effect(
97
97
  PlanCheckpointServiceTag,
@@ -95,7 +95,7 @@ export function makePlanCompilerService(planValidatorService: ReturnType<typeof
95
95
  export class PlanCompilerServiceTag extends Context.Service<
96
96
  PlanCompilerServiceTag,
97
97
  ReturnType<typeof makePlanCompilerService>
98
- >()('PlanCompilerService') {}
98
+ >()('@lota-sdk/core/PlanCompilerService') {}
99
99
 
100
100
  export const PlanCompilerServiceLive = Layer.effect(
101
101
  PlanCompilerServiceTag,
@@ -6,7 +6,7 @@ import { aiLogger } from '../../config/logger'
6
6
  import { ensureRecordId } from '../../db/record-id'
7
7
  import type { SurrealDBService } from '../../db/service'
8
8
  import { TABLES } from '../../db/tables'
9
- import { runPromise } from '../../effect/runtime'
9
+ import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
10
10
  import { nowEpochMillis, unsafeDateFrom } from '../../utils/date-time'
11
11
  import type { makeFeedbackLoopService } from '../feedback-loop.service'
12
12
  import type { makeInstitutionalMemoryService } from '../institutional-memory.service'
@@ -33,25 +33,15 @@ class PlanCompletionSideEffectsError extends Schema.TaggedErrorClass<PlanComplet
33
33
  { message: Schema.String, cause: Schema.Defect },
34
34
  ) {}
35
35
 
36
+ const effectTryPlanCompletionPromise = makeEffectTryPromiseWithMessage(
37
+ (message, cause) => new PlanCompletionSideEffectsError({ message, cause }),
38
+ )
39
+
36
40
  function tryPlanCompletionPromise<A>(
37
41
  message: string,
38
- thunk: () => PromiseLike<A> | Effect.Effect<A, unknown>,
42
+ evaluate: () => PromiseLike<A> | Effect.Effect<A, unknown>,
39
43
  ): Effect.Effect<A, PlanCompletionSideEffectsError> {
40
- return Effect.suspend(() => {
41
- try {
42
- const value = thunk()
43
- if (Effect.isEffect(value)) {
44
- return value.pipe(Effect.mapError((cause) => new PlanCompletionSideEffectsError({ message, cause })))
45
- }
46
-
47
- return Effect.tryPromise({
48
- try: () => Promise.resolve(value),
49
- catch: (cause) => new PlanCompletionSideEffectsError({ message, cause }),
50
- })
51
- } catch (cause) {
52
- return Effect.fail(new PlanCompletionSideEffectsError({ message, cause }))
53
- }
54
- })
44
+ return effectTryPlanCompletionPromise(evaluate, message)
55
45
  }
56
46
 
57
47
  export function makePlanCompletionSideEffects({
@@ -62,114 +52,110 @@ export function makePlanCompletionSideEffects({
62
52
  planRunService,
63
53
  qualityMetricsService,
64
54
  }: PlanCompletionSideEffectsDeps) {
65
- function runPlanNodeCompletionSideEffects(params: {
66
- runId: string
67
- organizationId: string
68
- nodeId: string
69
- nodeLabel: string
70
- nodeOwnerRef: string
71
- nodeOwnerType: PlanNodeOwner['executorType']
72
- nodeType: PlanNodeType
73
- nodeStartedAt?: string | Date | null
74
- nodeAttemptCount: number
75
- artifactCount: number
76
- validationIssues: PlanValidationIssueInput[]
77
- }): Promise<void> {
78
- return runPromise(
79
- Effect.gen(function* () {
80
- const executionTimeMs = params.nodeStartedAt
81
- ? nowEpochMillis() - unsafeDateFrom(params.nodeStartedAt).getTime()
82
- : 0
83
- if (executionTimeMs > 0) {
84
- yield* Metric.update(
85
- Metric.withAttributes(planNodeExecutionDuration, { nodeType: params.nodeType }),
86
- executionTimeMs,
87
- )
88
- }
89
- yield* qualityMetricsService.recordNodeMetrics({
90
- organizationId: params.organizationId,
91
- runId: params.runId,
92
- nodeId: params.nodeId,
93
- metrics: {
94
- executionTimeMs: Math.max(0, executionTimeMs),
95
- attemptCount: params.nodeAttemptCount,
96
- artifactCount: params.artifactCount,
97
- validationIssueCount: params.validationIssues.length,
98
- ownerRef: params.nodeOwnerRef,
99
- ownerType: params.nodeOwnerType,
100
- nodeType: params.nodeType,
101
- },
102
- })
103
- }),
104
- )
105
- }
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
+ )
106
94
 
107
- function runPlanCompletionSideEffects(params: { runId: string; organizationId: string }): Promise<void> {
108
- return runPromise(
109
- Effect.gen(function* () {
110
- 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 })
111
98
 
112
- const recommendations = yield* feedbackLoopService
113
- .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)
114
126
  .pipe(
115
127
  Effect.mapError(
116
- (cause) =>
117
- new PlanCompletionSideEffectsError({ message: 'Failed to analyze plan feedback outcomes.', cause }),
118
- ),
119
- )
120
- if (recommendations.length > 0) {
121
- const run = yield* planRunService.getRunById(params.runId)
122
- const specRecord = yield* planRunService.getPlanSpecById(run.planSpecId)
123
- const event = yield* tryPlanCompletionPromise('Failed to create feedback analyzed plan event.', () =>
124
- databaseService.create(
125
- TABLES.PLAN_EVENT,
126
- {
127
- planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
128
- runId: ensureRecordId(run.id, TABLES.PLAN_RUN),
129
- eventType: 'feedback-analyzed',
130
- message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
131
- detail: { recommendations },
132
- emittedBy: 'system',
133
- },
134
- PlanEventSchema,
128
+ (error) =>
129
+ new PlanCompletionSideEffectsError({
130
+ message: 'Failed to dispatch feedback analyzed plan event.',
131
+ cause: error,
132
+ }),
135
133
  ),
136
134
  )
137
- yield* Effect.tryPromise({
138
- try: () => planEventDeliveryService.dispatchEvent(event),
139
- catch: (cause) =>
135
+ }
136
+
137
+ yield* institutionalMemoryService
138
+ .extractPatterns({ organizationId: params.organizationId, runId: params.runId })
139
+ .pipe(
140
+ Effect.mapError(
141
+ (cause) =>
140
142
  new PlanCompletionSideEffectsError({
141
- message: 'Failed to dispatch feedback analyzed plan event.',
143
+ message: 'Failed to extract institutional memory patterns.',
142
144
  cause,
143
145
  }),
144
- })
145
- }
146
-
147
- yield* institutionalMemoryService
148
- .extractPatterns({ organizationId: params.organizationId, runId: params.runId })
149
- .pipe(
150
- Effect.mapError(
151
- (cause) =>
152
- new PlanCompletionSideEffectsError({
153
- message: 'Failed to extract institutional memory patterns.',
154
- cause,
155
- }),
156
- ),
157
- )
158
- }),
159
- )
160
- }
146
+ ),
147
+ )
148
+ },
149
+ )
161
150
 
162
- function runPlanCompletionSideEffectsSafely(params: { runId: string; organizationId: string }): Promise<void> {
163
- return runPromise(
164
- Effect.catch(
165
- tryPlanCompletionPromise('Plan completion side effects failed.', () => runPlanCompletionSideEffects(params)),
166
- (error: unknown) =>
167
- Effect.sync(() => {
168
- aiLogger.warn`Plan completion side effects failed for run ${params.runId}: ${error instanceof Error ? error.message : String(error)}`
169
- }),
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
+ }),
170
157
  ),
171
158
  )
172
- }
173
159
 
174
160
  return { runPlanNodeCompletionSideEffects, runPlanCompletionSideEffectsSafely }
175
161
  }
@@ -170,7 +170,7 @@ export function makePlanCoordinationService(planRunService: ReturnType<typeof ma
170
170
  export class PlanCoordinationServiceTag extends Context.Service<
171
171
  PlanCoordinationServiceTag,
172
172
  ReturnType<typeof makePlanCoordinationService>
173
- >()('PlanCoordinationService') {}
173
+ >()('@lota-sdk/core/PlanCoordinationService') {}
174
174
 
175
175
  export const PlanCoordinationServiceLive = Layer.effect(
176
176
  PlanCoordinationServiceTag,