@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,157 +0,0 @@
1
- import type { PlanArtifactRecord, PlanDependency } from '@lota-sdk/shared'
2
-
3
- import { serverLogger } from '../config/logger'
4
- import { recordIdToString } from '../db/record-id'
5
- import { TABLES } from '../db/tables'
6
- import type { PlanValidationIssueInput } from './plan-validator.service'
7
-
8
- export interface DependencyResolutionResult {
9
- resolved: Map<string, PlanArtifactRecord>
10
- unresolved: PlanDependency[]
11
- notifications: Array<{ dependency: PlanDependency; reason: string }>
12
- }
13
-
14
- class PlanCoordinationService {
15
- /**
16
- * Resolve cross-plan artifact dependencies.
17
- * For each dependency:
18
- * 1. Find the source plan by spec id in the thread
19
- * 2. Find the artifact by (nodeId, artifactName) in that plan's run
20
- * 3. Check staleness if maxStalenessMs set
21
- * 4. Based on triggerMode:
22
- * 'block' -> unresolved if missing/stale
23
- * 'notify' -> proceed but record notification
24
- * 'best-effort' -> proceed regardless, no notification
25
- */
26
- async resolveDependencies(params: {
27
- dependencies: PlanDependency[]
28
- threadId: string
29
- }): Promise<DependencyResolutionResult> {
30
- const { planRunService } = await import('./plan-run.service')
31
-
32
- const resolved = new Map<string, PlanArtifactRecord>()
33
- const unresolved: PlanDependency[] = []
34
- const notifications: DependencyResolutionResult['notifications'] = []
35
- const specs = await planRunService.listPlanSpecsByThread(params.threadId)
36
-
37
- for (const dep of params.dependencies) {
38
- const depKey = `${dep.sourcePlanSpecId}:${dep.sourceNodeId}:${dep.artifactName}`
39
-
40
- const sourceSpec = specs.find((s) => recordIdToString(s.id, TABLES.PLAN_SPEC) === dep.sourcePlanSpecId)
41
- if (!sourceSpec) {
42
- const reason = `Source plan "${dep.sourcePlanSpecId}" not found in thread.`
43
- if (dep.triggerMode === 'block') {
44
- unresolved.push(dep)
45
- } else if (dep.triggerMode === 'notify') {
46
- notifications.push({ dependency: dep, reason })
47
- serverLogger.warn`Dependency unmet (notify): ${reason}`
48
- }
49
- // best-effort: silently proceed
50
- continue
51
- }
52
-
53
- const runs = await planRunService.listRunsBySpec(sourceSpec.id)
54
- const activeRun = runs.find((r) => r.status === 'completed' || r.status === 'running')
55
- if (!activeRun) {
56
- const reason = `No active run found for plan "${sourceSpec.title}".`
57
- if (dep.triggerMode === 'block') {
58
- unresolved.push(dep)
59
- } else if (dep.triggerMode === 'notify') {
60
- notifications.push({ dependency: dep, reason })
61
- serverLogger.warn`Dependency unmet (notify): ${reason}`
62
- }
63
- continue
64
- }
65
-
66
- const artifacts = await planRunService.listArtifacts(activeRun.id)
67
- const artifact = artifacts.find((a) => a.nodeId === dep.sourceNodeId && a.name === dep.artifactName)
68
-
69
- if (!artifact) {
70
- const reason = `Artifact "${dep.artifactName}" not found on node "${dep.sourceNodeId}" in plan "${sourceSpec.title}".`
71
- if (dep.triggerMode === 'block') {
72
- unresolved.push(dep)
73
- } else if (dep.triggerMode === 'notify') {
74
- notifications.push({ dependency: dep, reason })
75
- serverLogger.warn`Dependency unmet (notify): ${reason}`
76
- }
77
- continue
78
- }
79
-
80
- if (dep.maxStalenessMs && this.isStale(artifact, dep.maxStalenessMs)) {
81
- const reason = `Artifact "${dep.artifactName}" from plan "${sourceSpec.title}" is stale.`
82
- if (dep.triggerMode === 'block') {
83
- unresolved.push(dep)
84
- continue
85
- }
86
- if (dep.triggerMode === 'notify') {
87
- notifications.push({ dependency: dep, reason })
88
- serverLogger.warn`Dependency stale (notify): ${reason}`
89
- }
90
- // best-effort and notify: use stale artifact anyway
91
- }
92
-
93
- resolved.set(depKey, artifact)
94
- }
95
-
96
- return { resolved, unresolved, notifications }
97
- }
98
-
99
- /** Check if an artifact has exceeded the staleness window. */
100
- isStale(artifact: Pick<PlanArtifactRecord, 'createdAt'>, maxStalenessMs: number): boolean {
101
- if (!maxStalenessMs) return false
102
- return Date.now() - artifact.createdAt.getTime() > maxStalenessMs
103
- }
104
-
105
- /**
106
- * Validate no circular dependencies exist using Kahn's algorithm.
107
- * Build adjacency: planSpecId -> depends on upstream planSpecIds
108
- * Run topological sort; if not all visited -> cycle exists.
109
- */
110
- validateNoCycles(
111
- specs: Array<{ id: string; title?: string; dependencies?: PlanDependency[] }>,
112
- ): PlanValidationIssueInput[] {
113
- const adj = new Map<string, Set<string>>()
114
- const inDegree = new Map<string, number>()
115
- const labels = new Map(specs.map((spec) => [spec.id, spec.title ?? spec.id]))
116
-
117
- for (const spec of specs) {
118
- if (!adj.has(spec.id)) adj.set(spec.id, new Set())
119
- if (!inDegree.has(spec.id)) inDegree.set(spec.id, 0)
120
-
121
- for (const dep of spec.dependencies ?? []) {
122
- if (!adj.has(dep.sourcePlanSpecId)) adj.set(dep.sourcePlanSpecId, new Set())
123
- if (!inDegree.has(dep.sourcePlanSpecId)) inDegree.set(dep.sourcePlanSpecId, 0)
124
-
125
- adj.get(dep.sourcePlanSpecId)?.add(spec.id)
126
- inDegree.set(spec.id, (inDegree.get(spec.id) ?? 0) + 1)
127
- }
128
- }
129
-
130
- const queue = [...inDegree.entries()].filter(([, d]) => d === 0).map(([t]) => t)
131
- const visited = new Set<string>()
132
-
133
- while (queue.length > 0) {
134
- const node = queue.shift()
135
- if (!node) break
136
- visited.add(node)
137
- for (const dep of adj.get(node) ?? []) {
138
- const d = (inDegree.get(dep) ?? 0) - 1
139
- inDegree.set(dep, d)
140
- if (d === 0) queue.push(dep)
141
- }
142
- }
143
-
144
- const unvisited = specs.filter((s) => !visited.has(s.id))
145
- if (unvisited.length === 0) return []
146
-
147
- return [
148
- {
149
- severity: 'blocking',
150
- code: 'circular_dependency',
151
- message: `Circular plan dependencies detected involving: ${unvisited.map((s) => labels.get(s.id) ?? s.id).join(', ')}`,
152
- },
153
- ]
154
- }
155
- }
156
-
157
- export const planCoordinationService = new PlanCoordinationService()
@@ -1,284 +0,0 @@
1
- import { PlanArtifactSchema, PlanCycleRecordSchema, PlanRunSchema } from '@lota-sdk/shared'
2
- import type {
3
- CarryForwardPolicy,
4
- CycleSchedule,
5
- PlanArtifactRecord,
6
- PlanCycleRecord,
7
- PlanDraft,
8
- PlanRunStatus,
9
- PlanScheduleSpec,
10
- PlanTemplateRecord,
11
- } from '@lota-sdk/shared'
12
-
13
- import type { RecordIdInput } from '../db/record-id'
14
- import { ensureRecordId, recordIdToString } from '../db/record-id'
15
- import { databaseService } from '../db/service'
16
- import { TABLES } from '../db/tables'
17
- import { planSchedulerService } from './plan-scheduler.service'
18
- import { planTemplateService } from './plan-template.service'
19
-
20
- const TERMINAL_RUN_STATUSES: ReadonlySet<PlanRunStatus> = new Set(['completed', 'failed', 'aborted'])
21
-
22
- function cycleScheduleToSpec(schedule: CycleSchedule): PlanScheduleSpec {
23
- const startAt = schedule.startAt ? new Date(schedule.startAt) : undefined
24
- const isFutureStart = startAt && startAt.getTime() > Date.now()
25
-
26
- if (isFutureStart) {
27
- return { type: 'absolute', at: startAt.toISOString(), missedPolicy: 'run-immediately' }
28
- }
29
-
30
- const cron = frequencyToCron(schedule.frequency, schedule.customIntervalMs)
31
- if (cron) {
32
- return { type: 'cron', cron, missedPolicy: 'run-immediately' }
33
- }
34
-
35
- if (schedule.frequency === 'biweekly') {
36
- return { type: 'monitoring', intervalMs: 14 * 24 * 60 * 60 * 1000, missedPolicy: 'run-immediately' }
37
- }
38
-
39
- if (schedule.frequency === 'custom' && schedule.customIntervalMs) {
40
- return { type: 'monitoring', intervalMs: schedule.customIntervalMs, missedPolicy: 'run-immediately' }
41
- }
42
-
43
- return { type: 'cron', cron: '0 0 * * *', missedPolicy: 'run-immediately' }
44
- }
45
-
46
- function frequencyToCron(frequency: CycleSchedule['frequency'], _customIntervalMs?: number): string | null {
47
- switch (frequency) {
48
- case 'daily':
49
- return '0 0 * * *'
50
- case 'weekly':
51
- return '0 0 * * 1'
52
- case 'monthly':
53
- return '0 0 1 * *'
54
- case 'quarterly':
55
- return '0 0 1 */3 *'
56
- case 'biweekly':
57
- case 'custom':
58
- return null
59
- }
60
- }
61
-
62
- class PlanCycleService {
63
- cycleScheduleToSpec = cycleScheduleToSpec
64
-
65
- async createCycle(params: {
66
- organizationId: RecordIdInput
67
- threadId: RecordIdInput
68
- templateId: RecordIdInput
69
- name: string
70
- schedule: CycleSchedule
71
- carryForwardPolicy?: CarryForwardPolicy
72
- leadAgentId: string
73
- }): Promise<PlanCycleRecord> {
74
- const now = new Date()
75
-
76
- const cycle = await databaseService.create(
77
- TABLES.PLAN_CYCLE,
78
- {
79
- organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
80
- threadId: ensureRecordId(params.threadId, TABLES.THREAD),
81
- templateId: ensureRecordId(params.templateId, TABLES.PLAN_TEMPLATE),
82
- name: params.name,
83
- schedule: params.schedule,
84
- carryForwardPolicy: params.carryForwardPolicy ?? 'incomplete-only',
85
- status: 'active',
86
- currentIteration: 0,
87
- createdAt: now,
88
- },
89
- PlanCycleRecordSchema,
90
- )
91
-
92
- const createResult = await planTemplateService.instantiate({
93
- templateId: params.templateId,
94
- organizationId: params.organizationId,
95
- threadId: params.threadId,
96
- leadAgentId: params.leadAgentId,
97
- })
98
-
99
- const createdRunId = createResult.plan?.runId
100
-
101
- const scheduleSpec = cycleScheduleToSpec(params.schedule)
102
- const scheduleRecord = await planSchedulerService.createSchedule({
103
- organizationId: params.organizationId,
104
- threadId: params.threadId,
105
- ...(createdRunId ? { runId: ensureRecordId(createdRunId, TABLES.PLAN_RUN) } : {}),
106
- scheduleSpec,
107
- })
108
-
109
- const updated = await databaseService.update(
110
- TABLES.PLAN_CYCLE,
111
- ensureRecordId(cycle.id, TABLES.PLAN_CYCLE),
112
- {
113
- scheduleId: ensureRecordId(scheduleRecord.id, TABLES.PLAN_SCHEDULE),
114
- ...(createdRunId ? { currentRunId: ensureRecordId(createdRunId, TABLES.PLAN_RUN) } : {}),
115
- currentIteration: 1,
116
- },
117
- PlanCycleRecordSchema,
118
- )
119
-
120
- return updated ?? cycle
121
- }
122
-
123
- async advanceCycle(cycleId: RecordIdInput): Promise<void> {
124
- const cycle = await this.getCycle(cycleId)
125
- if (!cycle) {
126
- throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
127
- }
128
-
129
- if (cycle.status !== 'active') {
130
- return
131
- }
132
-
133
- if (cycle.currentRunId) {
134
- const runRecord = await databaseService.findOne(
135
- TABLES.PLAN_RUN,
136
- { id: ensureRecordId(cycle.currentRunId, TABLES.PLAN_RUN) },
137
- PlanRunSchema,
138
- )
139
- if (runRecord && !TERMINAL_RUN_STATUSES.has(runRecord.status)) {
140
- return
141
- }
142
- }
143
-
144
- const template = await planTemplateService.getTemplate(cycle.templateId)
145
- if (!template) {
146
- throw new Error(`Template not found for cycle: ${recordIdToString(cycle.templateId, TABLES.PLAN_TEMPLATE)}`)
147
- }
148
-
149
- const previousRunArtifacts = cycle.currentRunId
150
- ? await databaseService.findMany(
151
- TABLES.PLAN_ARTIFACT,
152
- { runId: ensureRecordId(cycle.currentRunId, TABLES.PLAN_RUN) },
153
- PlanArtifactSchema,
154
- { orderBy: 'createdAt', orderDir: 'ASC' },
155
- )
156
- : []
157
-
158
- const draft = this.buildCarryForwardDraft({ template, previousRunArtifacts, policy: cycle.carryForwardPolicy })
159
-
160
- const result = await planTemplateService.instantiate({
161
- templateId: cycle.templateId,
162
- organizationId: cycle.organizationId,
163
- threadId: cycle.threadId,
164
- leadAgentId: 'system',
165
- overrides: draft,
166
- })
167
-
168
- const newRunId = result.plan?.runId
169
-
170
- await databaseService.update(
171
- TABLES.PLAN_CYCLE,
172
- ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
173
- {
174
- ...(newRunId ? { currentRunId: ensureRecordId(newRunId, TABLES.PLAN_RUN) } : {}),
175
- currentIteration: cycle.currentIteration + 1,
176
- },
177
- PlanCycleRecordSchema,
178
- )
179
-
180
- // TODO: re-enable when playbook records exist and IDs align
181
- // The previous implementation passed a PLAN_TEMPLATE record ID as a PLAYBOOK ID,
182
- // causing refineFromCycle to always fail silently.
183
- }
184
-
185
- buildCarryForwardDraft(params: {
186
- template: PlanTemplateRecord
187
- previousRunArtifacts: PlanArtifactRecord[]
188
- policy: CarryForwardPolicy
189
- }): PlanDraft {
190
- const draft = { ...params.template.draft }
191
-
192
- if (params.policy === 'none' || params.previousRunArtifacts.length === 0) {
193
- return draft
194
- }
195
-
196
- let artifacts: PlanArtifactRecord[]
197
- if (params.policy === 'incomplete-only') {
198
- artifacts = params.previousRunArtifacts.filter((a) => a.kind === 'markdown' || a.kind === 'json')
199
- } else {
200
- // 'all-pending'
201
- artifacts = [...params.previousRunArtifacts]
202
- }
203
-
204
- if (artifacts.length > 0) {
205
- const carryContext = artifacts.map((a) => `[carry-forward] ${a.name}: ${a.pointer}`)
206
- draft.objective = `${draft.objective}\n\nCarry-forward context:\n${carryContext.join('\n')}`
207
- }
208
-
209
- return draft
210
- }
211
-
212
- async cancelCycle(cycleId: RecordIdInput): Promise<void> {
213
- const cycle = await this.getCycle(cycleId)
214
- if (!cycle) {
215
- throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
216
- }
217
-
218
- if (cycle.scheduleId) {
219
- await planSchedulerService.cancelSchedule(cycle.scheduleId)
220
- }
221
-
222
- await databaseService.update(
223
- TABLES.PLAN_CYCLE,
224
- ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
225
- { status: 'cancelled' },
226
- PlanCycleRecordSchema,
227
- )
228
- }
229
-
230
- async pauseCycle(cycleId: RecordIdInput): Promise<void> {
231
- const cycle = await this.getCycle(cycleId)
232
- if (!cycle) {
233
- throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
234
- }
235
-
236
- if (cycle.scheduleId) {
237
- await planSchedulerService.pauseSchedule(cycle.scheduleId)
238
- }
239
-
240
- await databaseService.update(
241
- TABLES.PLAN_CYCLE,
242
- ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
243
- { status: 'paused' },
244
- PlanCycleRecordSchema,
245
- )
246
- }
247
-
248
- async resumeCycle(cycleId: RecordIdInput): Promise<void> {
249
- const cycle = await this.getCycle(cycleId)
250
- if (!cycle) {
251
- throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
252
- }
253
-
254
- if (cycle.scheduleId) {
255
- await planSchedulerService.resumeSchedule(cycle.scheduleId)
256
- }
257
-
258
- await databaseService.update(
259
- TABLES.PLAN_CYCLE,
260
- ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
261
- { status: 'active' },
262
- PlanCycleRecordSchema,
263
- )
264
- }
265
-
266
- async listCycles(threadId: RecordIdInput): Promise<PlanCycleRecord[]> {
267
- return databaseService.findMany(
268
- TABLES.PLAN_CYCLE,
269
- { threadId: ensureRecordId(threadId, TABLES.THREAD) },
270
- PlanCycleRecordSchema,
271
- { orderBy: 'createdAt', orderDir: 'ASC' },
272
- )
273
- }
274
-
275
- async getCycle(cycleId: RecordIdInput): Promise<PlanCycleRecord | null> {
276
- return databaseService.findOne(
277
- TABLES.PLAN_CYCLE,
278
- { id: ensureRecordId(cycleId, TABLES.PLAN_CYCLE) },
279
- PlanCycleRecordSchema,
280
- )
281
- }
282
- }
283
-
284
- export const planCycleService = new PlanCycleService()