@lota-sdk/core 0.4.8 → 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/thread-defaults.ts +33 -21
  9. package/src/create-runtime.ts +725 -387
  10. package/src/db/base.service.ts +52 -28
  11. package/src/db/cursor-pagination.ts +71 -30
  12. package/src/db/memory-store.helpers.ts +4 -7
  13. package/src/db/memory-store.ts +856 -598
  14. package/src/db/memory.ts +398 -275
  15. package/src/db/record-id.ts +32 -10
  16. package/src/db/schema-fingerprint.ts +30 -12
  17. package/src/db/service-normalization.ts +255 -0
  18. package/src/db/service.ts +726 -761
  19. package/src/db/startup.ts +140 -66
  20. package/src/db/transaction-conflict.ts +15 -0
  21. package/src/effect/awaitable-effect.ts +87 -0
  22. package/src/effect/errors.ts +121 -0
  23. package/src/effect/helpers.ts +98 -0
  24. package/src/effect/index.ts +22 -0
  25. package/src/effect/layers.ts +228 -0
  26. package/src/effect/runtime-ref.ts +25 -0
  27. package/src/effect/runtime.ts +31 -0
  28. package/src/effect/services.ts +57 -0
  29. package/src/effect/zod.ts +43 -0
  30. package/src/embeddings/provider.ts +122 -76
  31. package/src/index.ts +46 -1
  32. package/src/openrouter/direct-provider.ts +11 -35
  33. package/src/queues/autonomous-job.queue.ts +130 -74
  34. package/src/queues/context-compaction.queue.ts +60 -15
  35. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  36. package/src/queues/document-processor.queue.ts +52 -77
  37. package/src/queues/memory-consolidation.queue.ts +47 -32
  38. package/src/queues/organization-learning.queue.ts +13 -4
  39. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  40. package/src/queues/plan-scheduler.queue.ts +107 -31
  41. package/src/queues/post-chat-memory.queue.ts +66 -24
  42. package/src/queues/queue-factory.ts +142 -52
  43. package/src/queues/standalone-worker.ts +39 -0
  44. package/src/queues/title-generation.queue.ts +54 -9
  45. package/src/redis/connection.ts +84 -32
  46. package/src/redis/index.ts +6 -8
  47. package/src/redis/org-memory-lock.ts +60 -27
  48. package/src/redis/redis-lease-lock.ts +200 -121
  49. package/src/redis/runtime-connection.ts +10 -0
  50. package/src/redis/stream-context.ts +84 -46
  51. package/src/runtime/agent-identity-overrides.ts +2 -2
  52. package/src/runtime/agent-runtime-policy.ts +4 -1
  53. package/src/runtime/agent-stream-helpers.ts +20 -9
  54. package/src/runtime/chat-run-orchestration.ts +102 -19
  55. package/src/runtime/chat-run-registry.ts +36 -2
  56. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  57. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  58. package/src/runtime/execution-plan-visibility.ts +2 -2
  59. package/src/runtime/execution-plan.ts +42 -15
  60. package/src/runtime/graph-designer.ts +11 -7
  61. package/src/runtime/helper-model.ts +135 -48
  62. package/src/runtime/index.ts +7 -7
  63. package/src/runtime/indexed-repositories-policy.ts +3 -3
  64. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  65. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  66. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  67. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  68. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  69. package/src/runtime/plugin-resolution.ts +144 -24
  70. package/src/runtime/plugin-types.ts +9 -1
  71. package/src/runtime/post-turn-side-effects.ts +197 -130
  72. package/src/runtime/retrieval-adapters.ts +38 -4
  73. package/src/runtime/runtime-config.ts +150 -61
  74. package/src/runtime/runtime-extensions.ts +21 -34
  75. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  76. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  77. package/src/runtime/social-chat/social-chat.ts +594 -0
  78. package/src/runtime/specialist-runner.ts +36 -10
  79. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  80. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  81. package/src/runtime/thread-chat-helpers.ts +2 -2
  82. package/src/runtime/thread-plan-turn.ts +2 -1
  83. package/src/runtime/thread-turn-context.ts +172 -94
  84. package/src/runtime/turn-lifecycle.ts +93 -27
  85. package/src/services/agent-activity.service.ts +287 -203
  86. package/src/services/agent-executor.service.ts +329 -217
  87. package/src/services/artifact.service.ts +225 -148
  88. package/src/services/attachment.service.ts +137 -115
  89. package/src/services/autonomous-job.service.ts +888 -491
  90. package/src/services/chat-run-registry.service.ts +11 -1
  91. package/src/services/context-compaction.service.ts +136 -86
  92. package/src/services/document-chunk.service.ts +162 -90
  93. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  94. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  95. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  96. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  97. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  98. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  99. package/src/services/feedback-loop.service.ts +132 -76
  100. package/src/services/global-orchestrator.service.ts +80 -170
  101. package/src/services/graph-full-routing.ts +182 -0
  102. package/src/services/index.ts +18 -21
  103. package/src/services/institutional-memory.service.ts +220 -123
  104. package/src/services/learned-skill.service.ts +364 -259
  105. package/src/services/memory/memory-conversation.ts +95 -0
  106. package/src/services/memory/memory-org-memory.ts +39 -0
  107. package/src/services/memory/memory-preseeded.ts +80 -0
  108. package/src/services/memory/memory-rerank.ts +297 -0
  109. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  110. package/src/services/memory/memory.service.ts +692 -0
  111. package/src/services/memory/rerank.service.ts +209 -0
  112. package/src/services/monitoring-window.service.ts +92 -70
  113. package/src/services/mutating-approval.service.ts +62 -53
  114. package/src/services/node-workspace.service.ts +141 -98
  115. package/src/services/notification.service.ts +17 -16
  116. package/src/services/organization-member.service.ts +120 -66
  117. package/src/services/organization.service.ts +144 -51
  118. package/src/services/ownership-dispatcher.service.ts +415 -264
  119. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  120. package/src/services/plan/plan-agent-query.service.ts +322 -0
  121. package/src/services/plan/plan-approval.service.ts +102 -0
  122. package/src/services/plan/plan-artifact.service.ts +60 -0
  123. package/src/services/plan/plan-builder.service.ts +76 -0
  124. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  125. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  126. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  127. package/src/services/plan/plan-coordination.service.ts +181 -0
  128. package/src/services/plan/plan-cycle.service.ts +398 -0
  129. package/src/services/plan/plan-deadline.service.ts +547 -0
  130. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  131. package/src/services/plan/plan-executor-context.ts +35 -0
  132. package/src/services/plan/plan-executor-graph.ts +475 -0
  133. package/src/services/plan/plan-executor-helpers.ts +322 -0
  134. package/src/services/plan/plan-executor-persistence.ts +209 -0
  135. package/src/services/plan/plan-executor.service.ts +1654 -0
  136. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  137. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  138. package/src/services/plan/plan-run-serialization.ts +15 -0
  139. package/src/services/plan/plan-run.service.ts +644 -0
  140. package/src/services/plan/plan-scheduler.service.ts +385 -0
  141. package/src/services/plan/plan-template.service.ts +224 -0
  142. package/src/services/plan/plan-transaction-events.ts +33 -0
  143. package/src/services/plan/plan-validator.service.ts +907 -0
  144. package/src/services/plan/plan-workspace.service.ts +125 -0
  145. package/src/services/plugin-executor.service.ts +97 -68
  146. package/src/services/quality-metrics.service.ts +112 -94
  147. package/src/services/queue-job.service.ts +296 -230
  148. package/src/services/recent-activity-title.service.ts +65 -36
  149. package/src/services/recent-activity.service.ts +274 -259
  150. package/src/services/skill-resolver.service.ts +38 -12
  151. package/src/services/social-chat-history.service.ts +176 -125
  152. package/src/services/system-executor.service.ts +91 -61
  153. package/src/services/thread/thread-active-run.ts +203 -0
  154. package/src/services/thread/thread-bootstrap.ts +369 -0
  155. package/src/services/thread/thread-listing.ts +198 -0
  156. package/src/services/thread/thread-memory-block.ts +117 -0
  157. package/src/services/thread/thread-message.service.ts +363 -0
  158. package/src/services/thread/thread-record-store.ts +155 -0
  159. package/src/services/thread/thread-title.service.ts +74 -0
  160. package/src/services/thread/thread-turn-execution.ts +280 -0
  161. package/src/services/thread/thread-turn-message-context.ts +73 -0
  162. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  163. package/src/services/thread/thread-turn-streaming.ts +402 -0
  164. package/src/services/thread/thread-turn-tracing.ts +35 -0
  165. package/src/services/thread/thread-turn.ts +343 -0
  166. package/src/services/thread/thread.service.ts +335 -0
  167. package/src/services/user.service.ts +82 -32
  168. package/src/services/write-intent-validator.service.ts +63 -51
  169. package/src/storage/attachment-parser.ts +69 -27
  170. package/src/storage/attachment-storage.service.ts +331 -275
  171. package/src/storage/generated-document-storage.service.ts +66 -34
  172. package/src/system-agents/agent-result.ts +3 -1
  173. package/src/system-agents/context-compaction.agent.ts +2 -2
  174. package/src/system-agents/delegated-agent-factory.ts +159 -90
  175. package/src/system-agents/memory-reranker.agent.ts +2 -2
  176. package/src/system-agents/memory.agent.ts +2 -2
  177. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  178. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  179. package/src/system-agents/skill-extractor.agent.ts +2 -2
  180. package/src/system-agents/skill-manager.agent.ts +2 -2
  181. package/src/system-agents/thread-router.agent.ts +157 -113
  182. package/src/system-agents/title-generator.agent.ts +2 -2
  183. package/src/tools/execution-plan.tool.ts +220 -161
  184. package/src/tools/fetch-webpage.tool.ts +21 -17
  185. package/src/tools/firecrawl-client.ts +16 -6
  186. package/src/tools/index.ts +1 -0
  187. package/src/tools/memory-block.tool.ts +14 -6
  188. package/src/tools/plan-approval.tool.ts +49 -47
  189. package/src/tools/read-file-parts.tool.ts +44 -33
  190. package/src/tools/remember-memory.tool.ts +65 -45
  191. package/src/tools/search-web.tool.ts +26 -22
  192. package/src/tools/search.tool.ts +41 -29
  193. package/src/tools/team-think.tool.ts +124 -83
  194. package/src/tools/user-questions.tool.ts +4 -3
  195. package/src/tools/web-tool-shared.ts +6 -0
  196. package/src/utils/async.ts +17 -23
  197. package/src/utils/crypto.ts +21 -0
  198. package/src/utils/date-time.ts +40 -1
  199. package/src/utils/errors.ts +95 -16
  200. package/src/utils/hono-error-handler.ts +24 -39
  201. package/src/utils/index.ts +2 -1
  202. package/src/utils/null-proto-record.ts +41 -0
  203. package/src/utils/sse-keepalive.ts +124 -21
  204. package/src/workers/bootstrap.ts +186 -51
  205. package/src/workers/memory-consolidation.worker.ts +325 -237
  206. package/src/workers/organization-learning.worker.ts +50 -16
  207. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  208. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  209. package/src/workers/skill-extraction.runner.ts +176 -93
  210. package/src/workers/utils/file-section-chunker.ts +8 -10
  211. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  212. package/src/workers/utils/repomix-file-sections.ts +2 -2
  213. package/src/workers/utils/thread-message-query.ts +97 -38
  214. package/src/workers/worker-utils.ts +56 -31
  215. package/src/config/debug-logger.ts +0 -47
  216. package/src/redis/connection-accessor.ts +0 -26
  217. package/src/runtime/context-compaction-runtime.ts +0 -87
  218. package/src/runtime/social-chat-agent-runner.ts +0 -118
  219. package/src/runtime/social-chat.ts +0 -516
  220. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  221. package/src/services/adaptive-playbook.service.ts +0 -152
  222. package/src/services/artifact-provenance.service.ts +0 -172
  223. package/src/services/chat-attachments.service.ts +0 -17
  224. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  225. package/src/services/execution-plan.service.ts +0 -1118
  226. package/src/services/memory.service.ts +0 -914
  227. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  228. package/src/services/plan-agent-query.service.ts +0 -267
  229. package/src/services/plan-approval.service.ts +0 -83
  230. package/src/services/plan-artifact.service.ts +0 -50
  231. package/src/services/plan-builder.service.ts +0 -67
  232. package/src/services/plan-checkpoint.service.ts +0 -81
  233. package/src/services/plan-completion-side-effects.ts +0 -80
  234. package/src/services/plan-coordination.service.ts +0 -157
  235. package/src/services/plan-cycle.service.ts +0 -284
  236. package/src/services/plan-deadline.service.ts +0 -430
  237. package/src/services/plan-event-delivery.service.ts +0 -166
  238. package/src/services/plan-executor.service.ts +0 -1950
  239. package/src/services/plan-run.service.ts +0 -515
  240. package/src/services/plan-scheduler.service.ts +0 -240
  241. package/src/services/plan-template.service.ts +0 -177
  242. package/src/services/plan-validator.service.ts +0 -818
  243. package/src/services/plan-workspace.service.ts +0 -83
  244. package/src/services/rerank.service.ts +0 -156
  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,32 +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 './rerank.service'
20
- export * from './plan-coordination.service'
21
- export * from './plan-cycle.service'
22
- export * from './plan-deadline.service'
23
- export * from './plan-agent-query.service'
24
- export * from './plan-agent-heartbeat.service'
25
- export * from './plan-event-delivery.service'
26
- export * from './plan-workspace.service'
27
- export * from './plan-run.service'
28
- export * from './plan-scheduler.service'
29
- 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'
30
28
  export * from './quality-metrics.service'
31
29
  export * from './queue-job.service'
32
30
  export * from './monitoring-window.service'
@@ -37,10 +35,9 @@ export * from './skill-resolver.service'
37
35
  export * from './social-chat-history.service'
38
36
  export * from './system-executor.service'
39
37
  export * from './user.service'
40
- export * from './thread-message.service'
41
- export * from './thread.types'
42
- export * from './thread-title.service'
43
- export * from './thread-turn'
44
- export * from './thread-plan-registry.service'
45
- 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'
46
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
+ )