@lota-sdk/core 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -0,0 +1,182 @@
1
+ import type {
2
+ ExecutionMode,
3
+ PlanFailureClass,
4
+ PlanNodeResultSubmission,
5
+ PlanNodeRunRecord,
6
+ PlanNodeSpecRecord,
7
+ PlanRunRecord,
8
+ PlanSpecRecord,
9
+ } from '@lota-sdk/shared'
10
+ import { Effect } from 'effect'
11
+
12
+ import { serverLogger } from '../config/logger'
13
+ import { recordIdToString } from '../db/record-id'
14
+ import { TABLES } from '../db/tables'
15
+ import { BadRequestError } from '../effect/errors'
16
+ import { shouldPlanNodeUseVisibleTurn } from '../runtime/execution-plan-visibility'
17
+ import type { makePlanExecutorService } from './plan/plan-executor.service'
18
+ import type { makePlanRunService } from './plan/plan-run.service'
19
+
20
+ function classifyDispatchFailure(ownerType: string, error: unknown): PlanFailureClass {
21
+ const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase()
22
+ if (errorMessage.includes('timeout')) return 'timeout_exceeded'
23
+ if (ownerType === 'plugin' || ownerType === 'system') return 'external_system_unavailable'
24
+ return 'non_recoverable_logic_error'
25
+ }
26
+
27
+ function formatDispatchError(error: unknown): string {
28
+ return error instanceof Error ? error.message : String(error)
29
+ }
30
+
31
+ const STABLE_RUN_STATUSES = new Set(['pending-approval', 'awaiting-human', 'blocked', 'failed', 'completed', 'aborted'])
32
+
33
+ interface GraphFullRoutingDeps {
34
+ dispatchReadyNode(params: {
35
+ run: PlanRunRecord
36
+ nodeSpecRecord: PlanNodeSpecRecord
37
+ nodeRun: PlanNodeRunRecord
38
+ spec: PlanSpecRecord
39
+ executionModeOverride?: ExecutionMode
40
+ }): Effect.Effect<PlanNodeResultSubmission, unknown>
41
+ planExecutorService: ReturnType<typeof makePlanExecutorService>
42
+ planRunService: ReturnType<typeof makePlanRunService>
43
+ }
44
+
45
+ export function routeGraphFullEffect(params: { threadId: string; runId: string }, deps: GraphFullRoutingDeps) {
46
+ return Effect.gen(function* () {
47
+ const MAX_ROUNDS = 32
48
+ const STRUCTURAL_TYPES = new Set(['switch', 'join', 'deliberation-fork'])
49
+
50
+ let round = 0
51
+ for (; round < MAX_ROUNDS; round++) {
52
+ const run = yield* deps.planRunService.getRunById(params.runId)
53
+ if (STABLE_RUN_STATUSES.has(run.status)) break
54
+
55
+ const spec = yield* deps.planRunService.getPlanSpecById(run.planSpecId)
56
+ const nodeSpecs = yield* deps.planRunService.listNodeSpecs(spec.id)
57
+ const nodeRuns = yield* deps.planRunService.listNodeRuns(run.id)
58
+
59
+ const readyNodes = nodeRuns.filter((nr: PlanNodeRunRecord) => {
60
+ if (nr.status !== 'ready') return false
61
+ const ns = nodeSpecs.find((s: PlanNodeSpecRecord) => s.nodeId === nr.nodeId)
62
+ return ns && ns.owner.executorType !== 'user' && !STRUCTURAL_TYPES.has(ns.type)
63
+ })
64
+ if (readyNodes.length === 0) break
65
+
66
+ const silentNodes = readyNodes.filter((nr: PlanNodeRunRecord) => {
67
+ const ns = nodeSpecs.find((s: PlanNodeSpecRecord) => s.nodeId === nr.nodeId)
68
+ return ns && !shouldPlanNodeUseVisibleTurn(spec, ns)
69
+ })
70
+ const visibleNodes = readyNodes.filter((nr: PlanNodeRunRecord) => {
71
+ const ns = nodeSpecs.find((s: PlanNodeSpecRecord) => s.nodeId === nr.nodeId)
72
+ return ns && shouldPlanNodeUseVisibleTurn(spec, ns)
73
+ })
74
+
75
+ yield* Effect.forEach(
76
+ readyNodes,
77
+ (nodeRun) =>
78
+ Effect.tryPromise(() =>
79
+ deps.planExecutorService.transitionNodeToRunning({ runId: params.runId, nodeId: nodeRun.nodeId }),
80
+ ),
81
+ { concurrency: 'unbounded' },
82
+ )
83
+
84
+ if (visibleNodes.length > 0) {
85
+ const { enqueuePlanAgentHeartbeatWake } = yield* Effect.tryPromise(
86
+ () => import('../queues/plan-agent-heartbeat.queue'),
87
+ )
88
+ const updatedRunForWake = yield* deps.planRunService.getRunById(params.runId)
89
+ yield* Effect.forEach(
90
+ visibleNodes,
91
+ (nodeRun) =>
92
+ Effect.gen(function* () {
93
+ const ns = nodeSpecs.find((s: PlanNodeSpecRecord) => s.nodeId === nodeRun.nodeId)
94
+ if (!ns || ns.owner.executorType !== 'agent') {
95
+ return
96
+ }
97
+
98
+ yield* Effect.tryPromise(() =>
99
+ enqueuePlanAgentHeartbeatWake({
100
+ organizationId: recordIdToString(updatedRunForWake.organizationId, TABLES.ORGANIZATION),
101
+ threadId: recordIdToString(updatedRunForWake.threadId, TABLES.THREAD),
102
+ runId: recordIdToString(updatedRunForWake.id, TABLES.PLAN_RUN),
103
+ nodeId: nodeRun.nodeId,
104
+ agentId: ns.owner.ref,
105
+ reason: 'graph-full-visible',
106
+ }),
107
+ )
108
+ }),
109
+ { concurrency: 'unbounded' },
110
+ )
111
+ }
112
+
113
+ if (silentNodes.length === 0) break
114
+
115
+ const updatedRun = yield* deps.planRunService.getRunById(params.runId)
116
+
117
+ const results = yield* Effect.forEach(
118
+ silentNodes,
119
+ (nodeRun) =>
120
+ Effect.matchEffect(
121
+ Effect.gen(function* () {
122
+ const nodeSpecRecord = nodeSpecs.find((ns: PlanNodeSpecRecord) => ns.nodeId === nodeRun.nodeId)
123
+ if (!nodeSpecRecord) {
124
+ return yield* new BadRequestError({ message: `Node spec not found for node "${nodeRun.nodeId}".` })
125
+ }
126
+ const updatedNodeRun = yield* deps.planRunService.getNodeRunByNodeId(updatedRun.id, nodeRun.nodeId)
127
+ const result = yield* deps.dispatchReadyNode({
128
+ run: updatedRun,
129
+ nodeSpecRecord,
130
+ nodeRun: updatedNodeRun,
131
+ spec,
132
+ executionModeOverride: 'linear',
133
+ })
134
+ return { nodeId: nodeRun.nodeId, ownerRef: nodeSpecRecord.owner.ref, result }
135
+ }),
136
+ {
137
+ onFailure: (reason: unknown) => Effect.succeed({ status: 'rejected' as const, reason }),
138
+ onSuccess: (value) => Effect.succeed({ status: 'fulfilled' as const, value }),
139
+ },
140
+ ),
141
+ { concurrency: 'unbounded' },
142
+ )
143
+
144
+ const threadId = recordIdToString(updatedRun.threadId, TABLES.THREAD)
145
+ const runId = recordIdToString(updatedRun.id, TABLES.PLAN_RUN)
146
+
147
+ for (let i = 0; i < results.length; i++) {
148
+ const settled = results[i]
149
+ const nodeRun = silentNodes[i]
150
+ const nodeSpecRecord = nodeSpecs.find((ns) => ns.nodeId === nodeRun.nodeId)
151
+
152
+ if (settled.status === 'fulfilled') {
153
+ yield* Effect.tryPromise(() =>
154
+ deps.planExecutorService.submitNodeResult({
155
+ threadId,
156
+ runId,
157
+ nodeId: settled.value.nodeId,
158
+ emittedBy: settled.value.ownerRef,
159
+ result: settled.value.result,
160
+ }),
161
+ )
162
+ } else {
163
+ serverLogger.warn`routeGraphFull: dispatch failed for node "${nodeRun.nodeId}": ${settled.reason}`
164
+ yield* Effect.tryPromise(() =>
165
+ deps.planExecutorService.blockNodeOnDispatchFailure({
166
+ threadId,
167
+ runId,
168
+ nodeId: nodeRun.nodeId,
169
+ emittedBy: nodeSpecRecord?.owner.ref ?? 'unknown',
170
+ message: formatDispatchError(settled.reason),
171
+ failureClass: classifyDispatchFailure(nodeSpecRecord?.owner.executorType ?? 'agent', settled.reason),
172
+ }),
173
+ )
174
+ }
175
+ }
176
+ }
177
+
178
+ if (round === MAX_ROUNDS - 1) {
179
+ serverLogger.warn`graph-full execution reached max rounds (${MAX_ROUNDS}) for run ${params.runId} — possible non-converging graph`
180
+ }
181
+ })
182
+ }
@@ -1,31 +1,30 @@
1
- export * from './adaptive-playbook.service'
2
1
  export * from './agent-executor.service'
3
2
  export * from './agent-activity.service'
4
3
  export * from './artifact.service'
5
- export * from './artifact-provenance.service'
6
4
  export * from './attachment.service'
7
5
  export * from './autonomous-job.service'
8
6
  export * from './document-chunk.service'
9
- export * from './execution-plan.service'
7
+ export * from './execution-plan/execution-plan.service'
10
8
  export * from './institutional-memory.service'
11
9
  export * from './feedback-loop.service'
12
10
  export * from './global-orchestrator.service'
13
- export * from './memory.service'
11
+ export * from './memory/memory.service'
14
12
  export * from './node-workspace.service'
15
13
  export * from './notification.service'
16
14
  export * from './ownership-dispatcher.service'
17
15
  export * from './organization-member.service'
18
16
  export * from './organization.service'
19
- export * from './plan-coordination.service'
20
- export * from './plan-cycle.service'
21
- export * from './plan-deadline.service'
22
- export * from './plan-agent-query.service'
23
- export * from './plan-agent-heartbeat.service'
24
- export * from './plan-event-delivery.service'
25
- export * from './plan-workspace.service'
26
- export * from './plan-run.service'
27
- export * from './plan-scheduler.service'
28
- export * from './plan-template.service'
17
+ export * from './memory/rerank.service'
18
+ export * from './plan/plan-coordination.service'
19
+ export * from './plan/plan-cycle.service'
20
+ export * from './plan/plan-deadline.service'
21
+ export * from './plan/plan-agent-query.service'
22
+ export * from './plan/plan-agent-heartbeat.service'
23
+ export * from './plan/plan-event-delivery.service'
24
+ export * from './plan/plan-workspace.service'
25
+ export * from './plan/plan-run.service'
26
+ export * from './plan/plan-scheduler.service'
27
+ export * from './plan/plan-template.service'
29
28
  export * from './quality-metrics.service'
30
29
  export * from './queue-job.service'
31
30
  export * from './monitoring-window.service'
@@ -36,10 +35,9 @@ export * from './skill-resolver.service'
36
35
  export * from './social-chat-history.service'
37
36
  export * from './system-executor.service'
38
37
  export * from './user.service'
39
- export * from './thread-message.service'
40
- export * from './thread.types'
41
- export * from './thread-title.service'
42
- export * from './thread-turn'
43
- export * from './thread-plan-registry.service'
44
- export * from './thread.service'
38
+ export * from './thread/thread-message.service'
39
+ export * from './thread/thread.types'
40
+ export * from './thread/thread-title.service'
41
+ export * from './thread/thread-turn'
42
+ export * from './thread/thread.service'
45
43
  export * from './write-intent-validator.service'
@@ -1,145 +1,242 @@
1
- import type { InstitutionalMemory, InstitutionalMemoryType } from '@lota-sdk/shared'
1
+ import type {
2
+ InstitutionalMemory,
3
+ InstitutionalMemoryType,
4
+ PlanNodeRunRecord,
5
+ PlanNodeSpecRecord,
6
+ } from '@lota-sdk/shared'
2
7
  import { InstitutionalMemorySchema } from '@lota-sdk/shared'
8
+ import { Context, Schema, Effect, Layer } from 'effect'
3
9
  import { BoundQuery } from 'surrealdb'
4
10
 
5
11
  import { ensureRecordId } from '../db/record-id'
6
- import { databaseService } from '../db/service'
12
+ import type { SurrealDBService } from '../db/service'
7
13
  import { TABLES } from '../db/tables'
14
+ import { DatabaseServiceTag } from '../effect/services'
15
+ import { unsafeDateFrom } from '../utils/date-time'
16
+ import type { makePlanRunService } from './plan/plan-run.service'
17
+ import { PlanRunServiceTag } from './plan/plan-run.service'
18
+
19
+ interface InstitutionalMemoryDeps {
20
+ db: SurrealDBService
21
+ planRunService: ReturnType<typeof makePlanRunService>
22
+ }
8
23
 
9
- class InstitutionalMemoryService {
10
- async extractPatterns(params: { organizationId: string; runId: string }): Promise<InstitutionalMemory[]> {
11
- const { planRunService } = await import('./plan-run.service')
12
-
13
- const run = await planRunService.getRunById(params.runId)
14
- const nodeRuns = await planRunService.listNodeRuns(run.id)
15
- const nodeSpecs = await planRunService.listNodeSpecs(run.planSpecId)
16
- const nodeSpecsByNodeId = new Map(nodeSpecs.map((n) => [n.nodeId, n]))
17
-
18
- const patterns: InstitutionalMemory[] = []
19
- const orgRef = ensureRecordId(params.organizationId, TABLES.ORGANIZATION)
20
- const ownerRef = (nodeId: string): string => nodeSpecsByNodeId.get(nodeId)?.owner.ref ?? 'unknown'
21
-
22
- const completedNodes = nodeRuns.filter((nr) => nr.status === 'completed' && nr.startedAt && nr.completedAt)
23
- if (completedNodes.length > 0) {
24
- const timings = completedNodes.map((nr) => ({
25
- nodeId: nr.nodeId,
26
- owner: ownerRef(nr.nodeId),
27
- durationMs: new Date(nr.completedAt ?? 0).getTime() - new Date(nr.startedAt ?? 0).getTime(),
28
- }))
29
-
30
- const record = await this.persistPattern({
31
- organizationId: orgRef,
32
- type: 'timing-pattern',
33
- pattern: { runId: params.runId, timings },
34
- confidence: 0.7,
35
- sampleCount: completedNodes.length,
36
- })
37
- patterns.push(record)
38
- }
39
-
40
- const failedNodes = nodeRuns.filter((nr) => nr.status === 'failed')
41
- if (failedNodes.length > 0) {
42
- const failures = failedNodes.map((nr) => ({
43
- nodeId: nr.nodeId,
44
- owner: ownerRef(nr.nodeId),
45
- attemptCount: nr.attemptCount,
46
- }))
47
-
48
- const record = await this.persistPattern({
49
- organizationId: orgRef,
50
- type: 'failure-pattern',
51
- pattern: { runId: params.runId, failures },
52
- confidence: 0.6,
53
- sampleCount: failedNodes.length,
24
+ class InstitutionalMemoryServiceError extends Schema.TaggedErrorClass<InstitutionalMemoryServiceError>()(
25
+ 'InstitutionalMemoryServiceError',
26
+ { message: Schema.String, cause: Schema.Defect },
27
+ ) {}
28
+
29
+ function tryInstitutionalMemoryPromise<A>(
30
+ message: string,
31
+ thunk: () => PromiseLike<A> | Effect.Effect<A, unknown>,
32
+ ): Effect.Effect<A, InstitutionalMemoryServiceError> {
33
+ return Effect.suspend(() => {
34
+ try {
35
+ const value = thunk()
36
+ if (Effect.isEffect(value)) {
37
+ return value.pipe(Effect.mapError((cause) => new InstitutionalMemoryServiceError({ message, cause })))
38
+ }
39
+
40
+ return Effect.tryPromise({
41
+ try: () => Promise.resolve(value),
42
+ catch: (cause) => new InstitutionalMemoryServiceError({ message, cause }),
54
43
  })
55
- patterns.push(record)
56
- }
57
-
58
- const agentCompletions = new Map<string, number>()
59
- for (const nr of completedNodes) {
60
- const owner = ownerRef(nr.nodeId)
61
- agentCompletions.set(owner, (agentCompletions.get(owner) ?? 0) + 1)
62
- }
63
- if (agentCompletions.size > 0) {
64
- const record = await this.persistPattern({
65
- organizationId: orgRef,
66
- type: 'agent-affinity',
67
- pattern: { runId: params.runId, completions: Object.fromEntries(agentCompletions) },
68
- confidence: 0.5,
69
- sampleCount: completedNodes.length,
70
- })
71
- patterns.push(record)
44
+ } catch (cause) {
45
+ return Effect.fail(new InstitutionalMemoryServiceError({ message, cause }))
72
46
  }
47
+ })
48
+ }
73
49
 
74
- return patterns
75
- }
50
+ export function makeInstitutionalMemoryService(deps: InstitutionalMemoryDeps) {
51
+ const { db, planRunService } = deps
76
52
 
77
- async queryRelevant(params: {
78
- organizationId: string
79
- objective: string
80
- limit?: number
81
- }): Promise<InstitutionalMemory[]> {
82
- const orgRef = ensureRecordId(params.organizationId, TABLES.ORGANIZATION)
83
- const limit = params.limit ?? 10
84
-
85
- // Fetch a broader set, then rank by relevance to the objective.
86
- // SurrealDB does not support full-text scoring on FLEXIBLE object fields,
87
- // so we over-fetch and rank in application code.
88
- const fetchLimit = Math.max(limit * 3, 30)
89
- const records = await databaseService.queryMany(
90
- new BoundQuery(
91
- `SELECT * FROM ${TABLES.INSTITUTIONAL_MEMORY}
92
- WHERE organizationId = $orgId
93
- ORDER BY confidence DESC, createdAt DESC
94
- LIMIT $fetchLimit`,
95
- { orgId: orgRef, fetchLimit },
53
+ const persistPatternEffect = (params: {
54
+ organizationId: ReturnType<typeof ensureRecordId>
55
+ type: InstitutionalMemoryType
56
+ pattern: Record<string, unknown>
57
+ confidence: number
58
+ sampleCount: number
59
+ }) =>
60
+ tryInstitutionalMemoryPromise('Failed to persist institutional memory.', () =>
61
+ db.create(
62
+ TABLES.INSTITUTIONAL_MEMORY,
63
+ {
64
+ organizationId: params.organizationId,
65
+ type: params.type,
66
+ pattern: params.pattern,
67
+ confidence: params.confidence,
68
+ sampleCount: params.sampleCount,
69
+ },
70
+ InstitutionalMemorySchema,
96
71
  ),
97
- InstitutionalMemorySchema,
98
72
  )
99
73
 
100
- // Score each record by how many objective keywords appear in its pattern JSON
101
- const objectiveTerms = params.objective
102
- .toLowerCase()
103
- .split(/\s+/)
104
- .filter((t) => t.length > 2)
74
+ const extractPatternsEffect = (params: { organizationId: string; runId: string }) =>
75
+ Effect.gen(function* () {
76
+ const run = yield* planRunService
77
+ .getRunById(params.runId)
78
+ .pipe(
79
+ Effect.mapError(
80
+ (cause) => new InstitutionalMemoryServiceError({ message: `Failed to load run ${params.runId}.`, cause }),
81
+ ),
82
+ )
83
+
84
+ const [nodeRuns, nodeSpecs] = yield* Effect.all([
85
+ planRunService
86
+ .listNodeRuns(run.id)
87
+ .pipe(
88
+ Effect.mapError(
89
+ (cause) =>
90
+ new InstitutionalMemoryServiceError({
91
+ message: `Failed to load node runs for run ${params.runId}.`,
92
+ cause,
93
+ }),
94
+ ),
95
+ ),
96
+ planRunService
97
+ .listNodeSpecs(run.planSpecId)
98
+ .pipe(
99
+ Effect.mapError(
100
+ (cause) =>
101
+ new InstitutionalMemoryServiceError({
102
+ message: `Failed to load node specs for run ${params.runId}.`,
103
+ cause,
104
+ }),
105
+ ),
106
+ ),
107
+ ])
108
+
109
+ const nodeSpecsByNodeId = new Map<string, PlanNodeSpecRecord>(
110
+ nodeSpecs.map((nodeSpec) => [nodeSpec.nodeId, nodeSpec]),
111
+ )
112
+ const orgRef = ensureRecordId(params.organizationId, TABLES.ORGANIZATION)
113
+ const ownerRef = (nodeId: string): string => nodeSpecsByNodeId.get(nodeId)?.owner.ref ?? 'unknown'
114
+ const completedNodes = nodeRuns.filter(
115
+ (nodeRun: PlanNodeRunRecord) => nodeRun.status === 'completed' && nodeRun.startedAt && nodeRun.completedAt,
116
+ )
117
+ const failedNodes = nodeRuns.filter((nodeRun: PlanNodeRunRecord) => nodeRun.status === 'failed')
118
+ const agentCompletions = new Map<string, number>()
119
+
120
+ for (const nodeRun of completedNodes) {
121
+ const owner = ownerRef(nodeRun.nodeId)
122
+ agentCompletions.set(owner, (agentCompletions.get(owner) ?? 0) + 1)
123
+ }
124
+
125
+ const persistEffects: Array<Effect.Effect<InstitutionalMemory, InstitutionalMemoryServiceError, never>> = []
126
+
127
+ if (completedNodes.length > 0) {
128
+ const timings = completedNodes.map((nodeRun: PlanNodeRunRecord) => ({
129
+ nodeId: nodeRun.nodeId,
130
+ owner: ownerRef(nodeRun.nodeId),
131
+ durationMs:
132
+ unsafeDateFrom(nodeRun.completedAt ?? 0).getTime() - unsafeDateFrom(nodeRun.startedAt ?? 0).getTime(),
133
+ }))
134
+
135
+ persistEffects.push(
136
+ persistPatternEffect({
137
+ organizationId: orgRef,
138
+ type: 'timing-pattern',
139
+ pattern: { runId: params.runId, timings },
140
+ confidence: 0.7,
141
+ sampleCount: completedNodes.length,
142
+ }),
143
+ )
144
+ }
145
+
146
+ if (failedNodes.length > 0) {
147
+ const failures = failedNodes.map((nodeRun: PlanNodeRunRecord) => ({
148
+ nodeId: nodeRun.nodeId,
149
+ owner: ownerRef(nodeRun.nodeId),
150
+ attemptCount: nodeRun.attemptCount,
151
+ }))
152
+
153
+ persistEffects.push(
154
+ persistPatternEffect({
155
+ organizationId: orgRef,
156
+ type: 'failure-pattern',
157
+ pattern: { runId: params.runId, failures },
158
+ confidence: 0.6,
159
+ sampleCount: failedNodes.length,
160
+ }),
161
+ )
162
+ }
163
+
164
+ if (agentCompletions.size > 0) {
165
+ persistEffects.push(
166
+ persistPatternEffect({
167
+ organizationId: orgRef,
168
+ type: 'agent-affinity',
169
+ pattern: { runId: params.runId, completions: Object.fromEntries(agentCompletions) },
170
+ confidence: 0.5,
171
+ sampleCount: completedNodes.length,
172
+ }),
173
+ )
174
+ }
175
+
176
+ return yield* Effect.all(persistEffects)
177
+ })
105
178
 
106
- if (objectiveTerms.length === 0) return records.slice(0, limit)
179
+ const queryRelevantEffect = (params: { organizationId: string; objective: string; limit?: number }) =>
180
+ Effect.gen(function* () {
181
+ const orgRef = ensureRecordId(params.organizationId, TABLES.ORGANIZATION)
182
+ const limit = params.limit ?? 10
183
+ const fetchLimit = Math.max(limit * 3, 30)
184
+ const records = yield* tryInstitutionalMemoryPromise(
185
+ `Failed to query institutional memory for organization ${params.organizationId}.`,
186
+ () =>
187
+ db.queryMany(
188
+ new BoundQuery(
189
+ `SELECT * FROM ${TABLES.INSTITUTIONAL_MEMORY}
190
+ WHERE organizationId = $orgId
191
+ ORDER BY confidence DESC, createdAt DESC
192
+ LIMIT $fetchLimit`,
193
+ { orgId: orgRef, fetchLimit },
194
+ ),
195
+ InstitutionalMemorySchema,
196
+ ),
197
+ )
198
+
199
+ const objectiveTerms = params.objective
200
+ .toLowerCase()
201
+ .split(/\s+/)
202
+ .filter((term) => term.length > 2)
203
+
204
+ if (objectiveTerms.length === 0) return records.slice(0, limit)
205
+
206
+ const scored = records.map((record) => {
207
+ const patternStr = JSON.stringify(record.pattern).toLowerCase()
208
+ const matchCount = objectiveTerms.filter((term) => patternStr.includes(term)).length
209
+ return { record, relevance: matchCount / objectiveTerms.length }
210
+ })
107
211
 
108
- const scored = records.map((record) => {
109
- const patternStr = JSON.stringify(record.pattern).toLowerCase()
110
- const matchCount = objectiveTerms.filter((term) => patternStr.includes(term)).length
111
- return { record, relevance: matchCount / objectiveTerms.length }
112
- })
212
+ scored.sort((a, b) => {
213
+ if (b.relevance !== a.relevance) return b.relevance - a.relevance
214
+ if (b.record.confidence !== a.record.confidence) return b.record.confidence - a.record.confidence
215
+ return 0
216
+ })
113
217
 
114
- scored.sort((a, b) => {
115
- // Primary: relevance score, secondary: confidence, tertiary: recency
116
- if (b.relevance !== a.relevance) return b.relevance - a.relevance
117
- if (b.record.confidence !== a.record.confidence) return b.record.confidence - a.record.confidence
118
- return 0
218
+ return scored.slice(0, limit).map((item) => item.record)
119
219
  })
120
220
 
121
- return scored.slice(0, limit).map((s) => s.record)
221
+ const service = {
222
+ extractPatterns: (params: { organizationId: string; runId: string }) => extractPatternsEffect(params),
223
+ queryRelevant: (params: { organizationId: string; objective: string; limit?: number }) =>
224
+ queryRelevantEffect(params),
122
225
  }
123
226
 
124
- private async persistPattern(params: {
125
- organizationId: ReturnType<typeof ensureRecordId>
126
- type: InstitutionalMemoryType
127
- pattern: Record<string, unknown>
128
- confidence: number
129
- sampleCount: number
130
- }): Promise<InstitutionalMemory> {
131
- return databaseService.create(
132
- TABLES.INSTITUTIONAL_MEMORY,
133
- {
134
- organizationId: params.organizationId,
135
- type: params.type,
136
- pattern: params.pattern,
137
- confidence: params.confidence,
138
- sampleCount: params.sampleCount,
139
- },
140
- InstitutionalMemorySchema,
141
- )
142
- }
227
+ return service
143
228
  }
144
229
 
145
- export const institutionalMemoryService = new InstitutionalMemoryService()
230
+ export class InstitutionalMemoryServiceTag extends Context.Service<
231
+ InstitutionalMemoryServiceTag,
232
+ ReturnType<typeof makeInstitutionalMemoryService>
233
+ >()('InstitutionalMemoryService') {}
234
+
235
+ export const InstitutionalMemoryServiceLive = Layer.effect(
236
+ InstitutionalMemoryServiceTag,
237
+ Effect.gen(function* () {
238
+ const db = yield* DatabaseServiceTag
239
+ const planRunService = yield* PlanRunServiceTag
240
+ return makeInstitutionalMemoryService({ db, planRunService })
241
+ }),
242
+ )