@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
@@ -1,136 +0,0 @@
1
- import { serverLogger } from '../config/logger'
2
- import { ensureRecordId } from '../db/record-id'
3
- import { TABLES } from '../db/tables'
4
- import { getRedisConnection, withRedisLeaseLock } from '../redis'
5
- import { resolvePlanNodeExecutionVisibility } from '../runtime/execution-plan-visibility'
6
- import { planAgentQueryService } from './plan-agent-query.service'
7
- import { planExecutorService } from './plan-executor.service'
8
- import { planRunService } from './plan-run.service'
9
- import { threadService } from './thread.service'
10
-
11
- const PLAN_AGENT_HEARTBEAT_LOCK_TTL_MS = 300_000
12
- const PLAN_AGENT_HEARTBEAT_LOCK_REFRESH_MS = 20_000
13
-
14
- function buildHeartbeatLockKey(runId: string, nodeId: string): string {
15
- return `plan-agent-heartbeat:${runId}:${nodeId}`
16
- }
17
-
18
- function buildWakeDedupeKey(params: {
19
- organizationId: string
20
- threadId: string
21
- runId: string
22
- nodeId: string
23
- agentId: string
24
- }) {
25
- return `${params.organizationId}:${params.threadId}:${params.runId}:${params.nodeId}:${params.agentId}`
26
- }
27
-
28
- class PlanAgentHeartbeatService {
29
- async wakeNode(params: {
30
- organizationId: string
31
- threadId: string
32
- runId: string
33
- nodeId: string
34
- agentId: string
35
- reason: string
36
- }): Promise<boolean> {
37
- const threadRef = ensureRecordId(params.threadId, TABLES.THREAD)
38
- await threadService.clearStaleActiveRunIfMissingFromRegistry(threadRef)
39
- if (await threadService.getActiveRunId(threadRef)) {
40
- return false
41
- }
42
-
43
- return withRedisLeaseLock(
44
- {
45
- redis: getRedisConnection(),
46
- lockKey: buildHeartbeatLockKey(params.runId, params.nodeId),
47
- lockTtlMs: PLAN_AGENT_HEARTBEAT_LOCK_TTL_MS,
48
- refreshIntervalMs: PLAN_AGENT_HEARTBEAT_LOCK_REFRESH_MS,
49
- label: 'plan agent heartbeat',
50
- maxWaitMs: 5_000,
51
- logger: serverLogger,
52
- },
53
- async () => {
54
- await threadService.clearStaleActiveRunIfMissingFromRegistry(threadRef)
55
- if (await threadService.hasActiveRunLease(threadRef)) {
56
- return false
57
- }
58
- if (await threadService.getActiveRunId(threadRef)) {
59
- return false
60
- }
61
-
62
- const run = await planRunService.getRunById(params.runId)
63
- const spec = await planRunService.getPlanSpecById(run.planSpecId)
64
- const [nodeSpec, nodeRun] = await Promise.all([
65
- planRunService.getNodeSpecByNodeId(spec.id, params.nodeId),
66
- planRunService.getNodeRunByNodeId(run.id, params.nodeId),
67
- ])
68
-
69
- if (nodeSpec.owner.executorType !== 'agent' || nodeSpec.owner.ref !== params.agentId) {
70
- return false
71
- }
72
-
73
- const visibility = resolvePlanNodeExecutionVisibility(spec, nodeSpec)
74
- if (visibility !== 'visible') {
75
- return false
76
- }
77
-
78
- if (nodeRun.status !== 'running' && nodeRun.status !== 'ready') {
79
- return false
80
- }
81
-
82
- if (nodeRun.status === 'ready' && run.currentNodeId === params.nodeId) {
83
- await planExecutorService.transitionNodeToRunning({ runId: params.runId, nodeId: params.nodeId })
84
- }
85
-
86
- const { triggerPlanNodeTurn } = await import('./thread-turn')
87
-
88
- await triggerPlanNodeTurn({ runId: params.runId, nodeId: params.nodeId })
89
- return true
90
- },
91
- )
92
- }
93
-
94
- async sweep(params?: { organizationId?: string }): Promise<void> {
95
- const { enqueuePlanAgentHeartbeatWake } = await import('../queues/plan-agent-heartbeat.queue')
96
- const [actionable, recentlyUnblocked, approachingDeadlines] = await Promise.all([
97
- planAgentQueryService.getActionableNodesForAgent({ organizationId: params?.organizationId }),
98
- planAgentQueryService.getRecentlyUnblockedNodes({ organizationId: params?.organizationId }),
99
- planAgentQueryService.getApproachingDeadlines({ organizationId: params?.organizationId, withinMinutes: 60 }),
100
- ])
101
-
102
- const wakeTargets = new Map<
103
- string,
104
- { organizationId: string; threadId: string; runId: string; nodeId: string; agentId: string; reason: string }
105
- >()
106
-
107
- for (const node of actionable) {
108
- wakeTargets.set(buildWakeDedupeKey(node), { ...node, reason: 'heartbeat-actionable' })
109
- }
110
-
111
- for (const node of recentlyUnblocked) {
112
- wakeTargets.set(buildWakeDedupeKey(node), { ...node, reason: node.sourceEventType })
113
- }
114
-
115
- for (const node of approachingDeadlines) {
116
- if (!node.agentId || node.visibility !== 'visible') {
117
- continue
118
- }
119
- const wakeTarget = {
120
- organizationId: node.organizationId,
121
- threadId: node.threadId,
122
- runId: node.runId,
123
- nodeId: node.nodeId,
124
- agentId: node.agentId,
125
- reason: `deadline-${node.status}`,
126
- }
127
- wakeTargets.set(buildWakeDedupeKey(wakeTarget), wakeTarget)
128
- }
129
-
130
- for (const target of wakeTargets.values()) {
131
- await enqueuePlanAgentHeartbeatWake(target)
132
- }
133
- }
134
- }
135
-
136
- export const planAgentHeartbeatService = new PlanAgentHeartbeatService()
@@ -1,267 +0,0 @@
1
- import type { PlanExecutionVisibility, PlanNodeSpecRecord, PlanRunRecord, PlanSpecRecord } from '@lota-sdk/shared'
2
- import { PlanRunSchema } from '@lota-sdk/shared'
3
- import { BoundQuery } from 'surrealdb'
4
-
5
- import type { RecordIdInput } from '../db/record-id'
6
- import { ensureRecordId, recordIdToString } from '../db/record-id'
7
- import { databaseService } from '../db/service'
8
- import { TABLES } from '../db/tables'
9
- import { resolvePlanNodeExecutionVisibility } from '../runtime/execution-plan-visibility'
10
- import { planDeadlineService } from './plan-deadline.service'
11
- import { planRunService } from './plan-run.service'
12
-
13
- const ACTIVE_PLAN_RUN_STATUSES = ['running', 'awaiting-human', 'blocked'] as const
14
- const ACTIONABLE_NODE_STATUSES = new Set(['ready', 'running'])
15
- const DEADLINE_TRACKED_NODE_STATUSES = new Set(['ready', 'running', 'awaiting-human'])
16
-
17
- export interface ActionablePlanAgentNode {
18
- organizationId: string
19
- threadId: string
20
- runId: string
21
- nodeId: string
22
- agentId: string
23
- status: 'ready' | 'running'
24
- visibility: PlanExecutionVisibility
25
- }
26
-
27
- export interface ApproachingDeadlineNode {
28
- organizationId: string
29
- threadId: string
30
- runId: string
31
- nodeId: string
32
- agentId?: string
33
- visibility?: PlanExecutionVisibility
34
- dueAt?: string
35
- status: 'warning' | 'escalated' | 'missed'
36
- nextTriggerAt?: string | null
37
- }
38
-
39
- export interface RecentlyUnblockedNode {
40
- organizationId: string
41
- threadId: string
42
- runId: string
43
- nodeId: string
44
- agentId: string
45
- visibility: PlanExecutionVisibility
46
- unblockedAt: string
47
- sourceEventType: 'node-unblocked' | 'approval-resolved'
48
- }
49
-
50
- function isVisibleAgentNode(params: {
51
- nodeSpec: PlanNodeSpecRecord
52
- spec: PlanSpecRecord
53
- }): { agentId: string; visibility: PlanExecutionVisibility } | null {
54
- if (params.nodeSpec.owner.executorType !== 'agent') {
55
- return null
56
- }
57
-
58
- const visibility = resolvePlanNodeExecutionVisibility(params.spec, params.nodeSpec)
59
- const isVisible = visibility === 'visible'
60
- if (!isVisible) {
61
- return null
62
- }
63
-
64
- return { agentId: params.nodeSpec.owner.ref, visibility }
65
- }
66
-
67
- class PlanAgentQueryService {
68
- async getActionableNodesForAgent(params: {
69
- agentId?: string
70
- organizationId?: RecordIdInput
71
- }): Promise<ActionablePlanAgentNode[]> {
72
- const runs = await this.listActiveRuns(params.organizationId)
73
- const actionable: ActionablePlanAgentNode[] = []
74
-
75
- for (const run of runs) {
76
- const spec = await planRunService.getPlanSpecById(run.planSpecId)
77
-
78
- // Graph-full plans can have multiple visible agent nodes running in parallel
79
- if (spec.executionMode === 'graph-full') {
80
- const [nodeSpecs, nodeRuns] = await Promise.all([
81
- planRunService.listNodeSpecs(spec.id),
82
- planRunService.listNodeRuns(run.id),
83
- ])
84
- for (const nodeRun of nodeRuns) {
85
- if (!ACTIONABLE_NODE_STATUSES.has(nodeRun.status)) continue
86
- const nodeSpec = nodeSpecs.find((ns) => ns.nodeId === nodeRun.nodeId)
87
- if (!nodeSpec) continue
88
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
89
- if (!visibleTarget) continue
90
- if (params.agentId && params.agentId !== visibleTarget.agentId) continue
91
- actionable.push({
92
- organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
93
- threadId: recordIdToString(run.threadId, TABLES.THREAD),
94
- runId: recordIdToString(run.id, TABLES.PLAN_RUN),
95
- nodeId: nodeSpec.nodeId,
96
- agentId: visibleTarget.agentId,
97
- status: nodeRun.status as 'ready' | 'running',
98
- visibility: visibleTarget.visibility,
99
- })
100
- }
101
- continue
102
- }
103
-
104
- // Linear plans: only the current node is actionable
105
- const currentNodeId = run.currentNodeId
106
- if (!currentNodeId) {
107
- continue
108
- }
109
-
110
- const [nodeSpec, nodeRun] = await Promise.all([
111
- planRunService.getNodeSpecByNodeId(spec.id, currentNodeId),
112
- planRunService.getNodeRunByNodeId(run.id, currentNodeId),
113
- ])
114
- if (!ACTIONABLE_NODE_STATUSES.has(nodeRun.status)) {
115
- continue
116
- }
117
-
118
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
119
- if (!visibleTarget) {
120
- continue
121
- }
122
- if (params.agentId && params.agentId !== visibleTarget.agentId) {
123
- continue
124
- }
125
-
126
- actionable.push({
127
- organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
128
- threadId: recordIdToString(run.threadId, TABLES.THREAD),
129
- runId: recordIdToString(run.id, TABLES.PLAN_RUN),
130
- nodeId: nodeSpec.nodeId,
131
- agentId: visibleTarget.agentId,
132
- status: nodeRun.status as 'ready' | 'running',
133
- visibility: visibleTarget.visibility,
134
- })
135
- }
136
-
137
- return actionable
138
- }
139
-
140
- async getApproachingDeadlines(params?: {
141
- organizationId?: RecordIdInput
142
- withinMinutes?: number
143
- }): Promise<ApproachingDeadlineNode[]> {
144
- const now = new Date()
145
- const maxWindowMs = (params?.withinMinutes ?? 60) * 60_000
146
- const runs = await this.listActiveRuns(params?.organizationId)
147
- const matches: ApproachingDeadlineNode[] = []
148
-
149
- for (const run of runs) {
150
- const spec = await planRunService.getPlanSpecById(run.planSpecId)
151
- const [nodeSpecs, nodeRuns] = await Promise.all([
152
- planRunService.listNodeSpecs(spec.id),
153
- planRunService.listNodeRuns(run.id),
154
- ])
155
- const nodeRunsById = new Map(nodeRuns.map((nodeRun) => [nodeRun.nodeId, nodeRun]))
156
-
157
- for (const nodeSpec of nodeSpecs) {
158
- const nodeRun = nodeRunsById.get(nodeSpec.nodeId)
159
- if (!nodeRun || !nodeSpec.deadline || !DEADLINE_TRACKED_NODE_STATUSES.has(nodeRun.status)) {
160
- continue
161
- }
162
-
163
- const evaluation = planDeadlineService.evaluateDeadline({
164
- deadline: nodeSpec.deadline,
165
- nodeStartedAt: new Date(nodeRun.startedAt ?? nodeRun.createdAt),
166
- now,
167
- })
168
- if (evaluation.status === 'ok') {
169
- continue
170
- }
171
-
172
- const nextTriggerTime = evaluation.nextTriggerAt?.getTime()
173
- if (nextTriggerTime && nextTriggerTime - now.getTime() > maxWindowMs && evaluation.status !== 'missed') {
174
- continue
175
- }
176
-
177
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
178
- matches.push({
179
- organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
180
- threadId: recordIdToString(run.threadId, TABLES.THREAD),
181
- runId: recordIdToString(run.id, TABLES.PLAN_RUN),
182
- nodeId: nodeSpec.nodeId,
183
- ...(visibleTarget ? { agentId: visibleTarget.agentId, visibility: visibleTarget.visibility } : {}),
184
- ...(nodeSpec.deadline.dueAt ? { dueAt: nodeSpec.deadline.dueAt } : {}),
185
- status: evaluation.status,
186
- nextTriggerAt: evaluation.nextTriggerAt?.toISOString() ?? null,
187
- })
188
- }
189
- }
190
-
191
- return matches
192
- }
193
-
194
- async getRecentlyUnblockedNodes(params?: {
195
- organizationId?: RecordIdInput
196
- sinceMinutes?: number
197
- agentId?: string
198
- }): Promise<RecentlyUnblockedNode[]> {
199
- const since = new Date(Date.now() - (params?.sinceMinutes ?? 30) * 60_000)
200
- const runs = await this.listActiveRuns(params?.organizationId)
201
- const matches: RecentlyUnblockedNode[] = []
202
-
203
- for (const run of runs) {
204
- const spec = await planRunService.getPlanSpecById(run.planSpecId)
205
- const [nodeSpecs, events] = await Promise.all([
206
- planRunService.listNodeSpecs(spec.id),
207
- planRunService.listEvents(run.id, 200),
208
- ])
209
- const nodeSpecsById = new Map(nodeSpecs.map((nodeSpec) => [nodeSpec.nodeId, nodeSpec]))
210
-
211
- for (const event of events) {
212
- if (
213
- (event.eventType !== 'node-unblocked' && event.eventType !== 'approval-resolved') ||
214
- !event.nodeId ||
215
- new Date(event.createdAt).getTime() < since.getTime()
216
- ) {
217
- continue
218
- }
219
-
220
- const currentNodeId = run.currentNodeId ?? event.nodeId
221
- const nodeSpec = nodeSpecsById.get(currentNodeId)
222
- if (!nodeSpec) {
223
- continue
224
- }
225
-
226
- const visibleTarget = isVisibleAgentNode({ nodeSpec, spec })
227
- if (!visibleTarget) {
228
- continue
229
- }
230
- if (params?.agentId && params.agentId !== visibleTarget.agentId) {
231
- continue
232
- }
233
-
234
- matches.push({
235
- organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
236
- threadId: recordIdToString(run.threadId, TABLES.THREAD),
237
- runId: recordIdToString(run.id, TABLES.PLAN_RUN),
238
- nodeId: currentNodeId,
239
- agentId: visibleTarget.agentId,
240
- visibility: visibleTarget.visibility,
241
- unblockedAt: new Date(event.createdAt).toISOString(),
242
- sourceEventType: event.eventType,
243
- })
244
- }
245
- }
246
-
247
- return matches
248
- }
249
-
250
- private async listActiveRuns(organizationId?: RecordIdInput): Promise<PlanRunRecord[]> {
251
- const bindings = {
252
- statuses: [...ACTIVE_PLAN_RUN_STATUSES],
253
- ...(organizationId ? { organizationId: ensureRecordId(organizationId, TABLES.ORGANIZATION) } : {}),
254
- }
255
-
256
- const whereOrganization = organizationId ? ' AND organizationId = $organizationId' : ''
257
- return databaseService.queryMany(
258
- new BoundQuery(
259
- `SELECT * FROM ${TABLES.PLAN_RUN} WHERE status INSIDE $statuses${whereOrganization} ORDER BY updatedAt DESC`,
260
- bindings,
261
- ),
262
- PlanRunSchema,
263
- )
264
- }
265
- }
266
-
267
- export const planAgentQueryService = new PlanAgentQueryService()
@@ -1,50 +0,0 @@
1
- import { PlanArtifactSchema } from '@lota-sdk/shared'
2
- import type { PlanArtifactRecord, PlanArtifactSubmission } from '@lota-sdk/shared'
3
- import { RecordId } from 'surrealdb'
4
-
5
- import type { RecordIdInput } from '../db/record-id'
6
- import { ensureRecordId } from '../db/record-id'
7
- import type { DatabaseTransaction } from '../db/service'
8
- import { TABLES } from '../db/tables'
9
-
10
- class PlanArtifactService {
11
- async persistArtifacts(params: {
12
- tx: DatabaseTransaction
13
- runId: RecordIdInput
14
- attemptId: RecordIdInput
15
- nodeId: string
16
- artifacts: PlanArtifactSubmission[]
17
- }): Promise<PlanArtifactRecord[]> {
18
- const records: PlanArtifactRecord[] = []
19
-
20
- // Sequential: SurrealDB transactions require ordered operations
21
- for (const artifact of params.artifacts) {
22
- const artifactId = new RecordId(TABLES.PLAN_ARTIFACT, Bun.randomUUIDv7())
23
- const created = await params.tx
24
- .create(artifactId)
25
- .content({
26
- runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
27
- attemptId: ensureRecordId(params.attemptId, TABLES.PLAN_NODE_ATTEMPT),
28
- nodeId: params.nodeId,
29
- name: artifact.name,
30
- kind: artifact.kind,
31
- pointer: artifact.publishedArtifactId
32
- ? `venturos://artifact/${artifact.publishedArtifactId}`
33
- : `artifact://${params.nodeId}/${artifact.name}`,
34
- ...(artifact.description ? { description: artifact.description } : {}),
35
- ...(artifact.content ? { content: artifact.content } : {}),
36
- ...(artifact.payload ? { payload: artifact.payload } : {}),
37
- ...(artifact.publishedArtifactId
38
- ? { publishedArtifactId: ensureRecordId(artifact.publishedArtifactId, TABLES.ARTIFACT) }
39
- : {}),
40
- })
41
- .output('after')
42
-
43
- records.push(PlanArtifactSchema.parse(created))
44
- }
45
-
46
- return records
47
- }
48
- }
49
-
50
- export const planArtifactService = new PlanArtifactService()
@@ -1,67 +0,0 @@
1
- import type { PlanDraft } from '@lota-sdk/shared'
2
-
3
- import { isExecutableConditionExpression } from './plan-helpers'
4
-
5
- function buildImplicitLinearEdges(draft: PlanDraft) {
6
- if (draft.edges.length > 0 || draft.nodes.length <= 1) {
7
- return draft.edges
8
- }
9
-
10
- return draft.nodes
11
- .slice(0, -1)
12
- .map((node, index) => ({
13
- id: `edge_${node.id}_to_${draft.nodes[index + 1]?.id ?? index + 1}`,
14
- source: node.id,
15
- target: draft.nodes[index + 1].id,
16
- map: {},
17
- }))
18
- }
19
-
20
- class PlanBuilderService {
21
- structureDesign(draft: PlanDraft): PlanDraft {
22
- return {
23
- ...draft,
24
- executionMode: draft.executionMode ?? 'linear',
25
- edges: buildImplicitLinearEdges(draft),
26
- entryNodeIds: draft.entryNodeIds && draft.entryNodeIds.length > 0 ? draft.entryNodeIds : [draft.nodes[0].id],
27
- }
28
- }
29
-
30
- semanticCompletion(draft: PlanDraft): PlanDraft {
31
- return {
32
- ...draft,
33
- nodes: draft.nodes.map((node) => ({
34
- ...node,
35
- deliverables: [...node.deliverables],
36
- successCriteria: [...node.successCriteria],
37
- completionChecks: [...node.completionChecks],
38
- failurePolicy: [...node.failurePolicy],
39
- retryPolicy: { ...node.retryPolicy, retryOn: [...node.retryPolicy.retryOn] },
40
- toolPolicy: { allow: [...node.toolPolicy.allow], deny: [...node.toolPolicy.deny] },
41
- contextPolicy: {
42
- retrievalScopes: [...node.contextPolicy.retrievalScopes],
43
- attachmentPolicy: node.contextPolicy.attachmentPolicy,
44
- webPolicy: node.contextPolicy.webPolicy,
45
- },
46
- })),
47
- edges: draft.edges.map((edge) => {
48
- const normalizedWhen = edge.when?.trim()
49
- const { when: _when, ...edgeWithoutWhen } = edge
50
- return {
51
- ...edgeWithoutWhen,
52
- ...(normalizedWhen && isExecutableConditionExpression(normalizedWhen) ? { when: normalizedWhen } : {}),
53
- map: { ...edge.map },
54
- }
55
- }),
56
- schemas: structuredClone(draft.schemas),
57
- entryNodeIds: [...(draft.entryNodeIds ?? [])],
58
- }
59
- }
60
-
61
- prepareDraft(draft: PlanDraft): PlanDraft {
62
- const withStructure = this.structureDesign(draft)
63
- return this.semanticCompletion(withStructure)
64
- }
65
- }
66
-
67
- export const planBuilderService = new PlanBuilderService()
@@ -1,81 +0,0 @@
1
- import { PlanCheckpointSchema } from '@lota-sdk/shared'
2
- import type { PlanCheckpointRecord, PlanRunStatus } from '@lota-sdk/shared'
3
- import { RecordId } from 'surrealdb'
4
-
5
- import { serverLogger } from '../config/logger'
6
- import type { RecordIdInput } from '../db/record-id'
7
- import { ensureRecordId, recordIdToString } from '../db/record-id'
8
- import { databaseService } from '../db/service'
9
- import type { DatabaseTransaction } from '../db/service'
10
- import { TABLES } from '../db/tables'
11
- import { planWorkspaceService } from './plan-workspace.service'
12
-
13
- class PlanCheckpointService {
14
- async createCheckpoint(params: {
15
- tx: DatabaseTransaction
16
- runId: RecordIdInput
17
- sequence: number
18
- runStatus: PlanRunStatus
19
- readyNodeIds: string[]
20
- activeNodeIds: string[]
21
- artifactIds: RecordIdInput[]
22
- lastCompletedNodeIds: string[]
23
- snapshot: Record<string, unknown>
24
- includeWorkspace?: boolean
25
- }): Promise<PlanCheckpointRecord> {
26
- const snapshotData = { ...params.snapshot }
27
-
28
- if (params.includeWorkspace) {
29
- const runIdStr = recordIdToString(params.runId, TABLES.PLAN_RUN)
30
- const workspaceSnapshot = await planWorkspaceService
31
- .currentRead({ runId: runIdStr })
32
- .then((all) => {
33
- // Filter to entries at or before the checkpoint sequence
34
- const filtered: Record<string, unknown> = {}
35
- for (const [key, entry] of Object.entries(all)) {
36
- if (entry.writeSequence <= params.sequence) {
37
- filtered[key] = entry
38
- }
39
- }
40
- return filtered
41
- })
42
- .catch((error) => {
43
- serverLogger.warn`Workspace snapshot failed for run ${runIdStr}: ${error}`
44
- return {}
45
- })
46
- if (Object.keys(workspaceSnapshot).length > 0) {
47
- snapshotData.workspaceSnapshot = workspaceSnapshot
48
- }
49
- }
50
-
51
- const checkpointId = new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7())
52
- const created = await params.tx
53
- .create(checkpointId)
54
- .content({
55
- runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
56
- sequence: params.sequence,
57
- runStatus: params.runStatus,
58
- readyNodeIds: [...params.readyNodeIds],
59
- activeNodeIds: [...params.activeNodeIds],
60
- artifactIds: params.artifactIds.map((artifactId) => ensureRecordId(artifactId, TABLES.PLAN_ARTIFACT)),
61
- lastCompletedNodeIds: [...params.lastCompletedNodeIds],
62
- snapshot: snapshotData,
63
- })
64
- .output('after')
65
-
66
- return PlanCheckpointSchema.parse(created)
67
- }
68
-
69
- async loadLatestForRun(runId: RecordIdInput): Promise<PlanCheckpointRecord | null> {
70
- const checkpoints = await databaseService.findMany(
71
- TABLES.PLAN_CHECKPOINT,
72
- { runId: ensureRecordId(runId, TABLES.PLAN_RUN) },
73
- PlanCheckpointSchema,
74
- { orderBy: 'sequence', orderDir: 'DESC', limit: 1 },
75
- )
76
-
77
- return checkpoints.at(0) ?? null
78
- }
79
- }
80
-
81
- export const planCheckpointService = new PlanCheckpointService()
@@ -1,80 +0,0 @@
1
- import type { PlanNodeOwner, PlanNodeType } from '@lota-sdk/shared'
2
- import { PlanEventSchema } from '@lota-sdk/shared'
3
-
4
- import { aiLogger } from '../config/logger'
5
- import { ensureRecordId } from '../db/record-id'
6
- import { databaseService } from '../db/service'
7
- import { TABLES } from '../db/tables'
8
- import { feedbackLoopService } from './feedback-loop.service'
9
- import { institutionalMemoryService } from './institutional-memory.service'
10
- import { planEventDeliveryService } from './plan-event-delivery.service'
11
- import { planRunService } from './plan-run.service'
12
- import type { PlanValidationIssueInput } from './plan-validator.service'
13
- import { qualityMetricsService } from './quality-metrics.service'
14
-
15
- export async function runPlanNodeCompletionSideEffects(params: {
16
- runId: string
17
- organizationId: string
18
- nodeId: string
19
- nodeLabel: string
20
- nodeOwnerRef: string
21
- nodeOwnerType: PlanNodeOwner['executorType']
22
- nodeType: PlanNodeType
23
- nodeStartedAt?: string | Date | null
24
- nodeAttemptCount: number
25
- artifactCount: number
26
- validationIssues: PlanValidationIssueInput[]
27
- }): Promise<void> {
28
- const executionTimeMs = params.nodeStartedAt ? Date.now() - new Date(params.nodeStartedAt).getTime() : 0
29
- await qualityMetricsService.recordNodeMetrics({
30
- organizationId: params.organizationId,
31
- runId: params.runId,
32
- nodeId: params.nodeId,
33
- metrics: {
34
- executionTimeMs: Math.max(0, executionTimeMs),
35
- attemptCount: params.nodeAttemptCount,
36
- artifactCount: params.artifactCount,
37
- validationIssueCount: params.validationIssues.length,
38
- ownerRef: params.nodeOwnerRef,
39
- ownerType: params.nodeOwnerType,
40
- nodeType: params.nodeType,
41
- },
42
- })
43
- }
44
-
45
- async function runPlanCompletionSideEffects(params: { runId: string; organizationId: string }): Promise<void> {
46
- await qualityMetricsService.recordCycleMetrics({ organizationId: params.organizationId, runId: params.runId })
47
-
48
- const recommendations = await feedbackLoopService.analyzeOutcomes({
49
- runId: params.runId,
50
- organizationId: params.organizationId,
51
- })
52
- if (recommendations.length > 0) {
53
- const run = await planRunService.getRunById(params.runId)
54
- const specRecord = await planRunService.getPlanSpecById(run.planSpecId)
55
- const event = await databaseService.create(
56
- TABLES.PLAN_EVENT,
57
- {
58
- planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
59
- runId: ensureRecordId(run.id, TABLES.PLAN_RUN),
60
- eventType: 'feedback-analyzed',
61
- message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
62
- detail: { recommendations },
63
- emittedBy: 'system',
64
- },
65
- PlanEventSchema,
66
- )
67
- await planEventDeliveryService.dispatchEvent(event)
68
- }
69
-
70
- await institutionalMemoryService.extractPatterns({ organizationId: params.organizationId, runId: params.runId })
71
- }
72
-
73
- export async function runPlanCompletionSideEffectsSafely(params: {
74
- runId: string
75
- organizationId: string
76
- }): Promise<void> {
77
- await runPlanCompletionSideEffects(params).catch((error) => {
78
- aiLogger.warn`Plan completion side effects failed for run ${params.runId}: ${error instanceof Error ? error.message : String(error)}`
79
- })
80
- }