@lota-sdk/core 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -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,83 +0,0 @@
1
- import { PlanApprovalSchema } from '@lota-sdk/shared'
2
- import type { PlanApprovalRecord, PlanApprovalStatus } 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 { databaseService } from '../db/service'
8
- import type { DatabaseTransaction } from '../db/service'
9
- import { TABLES } from '../db/tables'
10
-
11
- class PlanApprovalService {
12
- async createPendingApproval(params: {
13
- tx: DatabaseTransaction
14
- runId: RecordIdInput
15
- nodeRunId: RecordIdInput
16
- nodeId: string
17
- requestedBy: string
18
- presented: Record<string, unknown>
19
- }): Promise<PlanApprovalRecord> {
20
- const approvalId = new RecordId(TABLES.PLAN_APPROVAL, Bun.randomUUIDv7())
21
- const created = await params.tx
22
- .create(approvalId)
23
- .content({
24
- runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
25
- nodeRunId: ensureRecordId(params.nodeRunId, TABLES.PLAN_NODE_RUN),
26
- nodeId: params.nodeId,
27
- status: 'pending',
28
- requestedBy: params.requestedBy,
29
- presented: params.presented,
30
- requiredEdits: [],
31
- })
32
- .output('after')
33
-
34
- return PlanApprovalSchema.parse(created)
35
- }
36
-
37
- async getApprovalById(approvalId: RecordIdInput): Promise<PlanApprovalRecord | null> {
38
- return databaseService.findOne(
39
- TABLES.PLAN_APPROVAL,
40
- { id: ensureRecordId(approvalId, TABLES.PLAN_APPROVAL) },
41
- PlanApprovalSchema,
42
- )
43
- }
44
-
45
- async getPendingApprovalForNodeRun(nodeRunId: RecordIdInput): Promise<PlanApprovalRecord | null> {
46
- const approvals = await databaseService.findMany(
47
- TABLES.PLAN_APPROVAL,
48
- { nodeRunId: ensureRecordId(nodeRunId, TABLES.PLAN_NODE_RUN), status: 'pending' },
49
- PlanApprovalSchema,
50
- { orderBy: 'createdAt', orderDir: 'DESC', limit: 1 },
51
- )
52
-
53
- return approvals.at(0) ?? null
54
- }
55
-
56
- async updateApprovalResponse(params: {
57
- tx: DatabaseTransaction
58
- approval: PlanApprovalRecord
59
- status: PlanApprovalStatus
60
- response: Record<string, unknown>
61
- respondedBy: string
62
- approvalMessageId?: string
63
- comments?: string
64
- requiredEdits?: string[]
65
- }): Promise<PlanApprovalRecord> {
66
- const updated = await params.tx
67
- .update(ensureRecordId(params.approval.id, TABLES.PLAN_APPROVAL))
68
- .merge({
69
- status: params.status,
70
- response: params.response,
71
- respondedBy: params.respondedBy,
72
- ...(params.approvalMessageId ? { approvalMessageId: params.approvalMessageId } : {}),
73
- ...(params.comments ? { comments: params.comments } : {}),
74
- ...(params.requiredEdits ? { requiredEdits: params.requiredEdits } : {}),
75
- respondedAt: new Date(),
76
- })
77
- .output('after')
78
-
79
- return PlanApprovalSchema.parse(updated)
80
- }
81
- }
82
-
83
- export const planApprovalService = new PlanApprovalService()
@@ -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()