@lota-sdk/core 0.4.8 → 0.4.10

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