@lota-sdk/core 0.4.8 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -0,0 +1,405 @@
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
+ import { Context, Schema, Effect, Layer } from 'effect'
13
+
14
+ import type { RecordIdInput } from '../../db/record-id'
15
+ import { ensureRecordId, recordIdToString } from '../../db/record-id'
16
+ import type { SurrealDBService } from '../../db/service'
17
+ import { TABLES } from '../../db/tables'
18
+ import { runPromise } from '../../effect/runtime'
19
+ import { DatabaseServiceTag } from '../../effect/services'
20
+ import { nowDate, nowEpochMillis, unsafeDateFrom } from '../../utils/date-time'
21
+ import type { makePlanSchedulerService } from './plan-scheduler.service'
22
+ import { PlanSchedulerServiceTag } from './plan-scheduler.service'
23
+ import type { makePlanTemplateService } from './plan-template.service'
24
+ import { PlanTemplateServiceTag } from './plan-template.service'
25
+
26
+ const TERMINAL_RUN_STATUSES: ReadonlySet<PlanRunStatus> = new Set(['completed', 'failed', 'aborted'])
27
+
28
+ class PlanCycleNotFoundError extends Schema.TaggedErrorClass<PlanCycleNotFoundError>()('PlanCycleNotFoundError', {
29
+ cycleId: Schema.String,
30
+ message: Schema.String,
31
+ }) {}
32
+
33
+ class PlanTemplateNotFoundForCycleError extends Schema.TaggedErrorClass<PlanTemplateNotFoundForCycleError>()(
34
+ 'PlanTemplateNotFoundForCycleError',
35
+ { templateId: Schema.String, message: Schema.String },
36
+ ) {}
37
+
38
+ function cycleScheduleToSpec(schedule: CycleSchedule): PlanScheduleSpec {
39
+ const startAt = schedule.startAt ? unsafeDateFrom(schedule.startAt) : undefined
40
+ const isFutureStart = startAt && startAt.getTime() > nowEpochMillis()
41
+
42
+ if (isFutureStart) {
43
+ return { type: 'absolute', at: startAt.toISOString(), missedPolicy: 'run-immediately' }
44
+ }
45
+
46
+ const cron = frequencyToCron(schedule.frequency, schedule.customIntervalMs)
47
+ if (cron) {
48
+ return { type: 'cron', cron, missedPolicy: 'run-immediately' }
49
+ }
50
+
51
+ if (schedule.frequency === 'biweekly') {
52
+ return { type: 'monitoring', intervalMs: 14 * 24 * 60 * 60 * 1000, missedPolicy: 'run-immediately' }
53
+ }
54
+
55
+ if (schedule.frequency === 'custom' && schedule.customIntervalMs) {
56
+ return { type: 'monitoring', intervalMs: schedule.customIntervalMs, missedPolicy: 'run-immediately' }
57
+ }
58
+
59
+ return { type: 'cron', cron: '0 0 * * *', missedPolicy: 'run-immediately' }
60
+ }
61
+
62
+ function frequencyToCron(frequency: CycleSchedule['frequency'], _customIntervalMs?: number): string | null {
63
+ switch (frequency) {
64
+ case 'daily':
65
+ return '0 0 * * *'
66
+ case 'weekly':
67
+ return '0 0 * * 1'
68
+ case 'monthly':
69
+ return '0 0 1 * *'
70
+ case 'quarterly':
71
+ return '0 0 1 */3 *'
72
+ case 'biweekly':
73
+ case 'custom':
74
+ return null
75
+ }
76
+ }
77
+
78
+ function buildCarryForwardDraft(params: {
79
+ template: PlanTemplateRecord
80
+ previousRunArtifacts: PlanArtifactRecord[]
81
+ policy: CarryForwardPolicy
82
+ }): PlanDraft {
83
+ const draft = { ...params.template.draft }
84
+
85
+ if (params.policy === 'none' || params.previousRunArtifacts.length === 0) {
86
+ return draft
87
+ }
88
+
89
+ let artifacts: PlanArtifactRecord[]
90
+ if (params.policy === 'incomplete-only') {
91
+ artifacts = params.previousRunArtifacts.filter((a) => a.kind === 'markdown' || a.kind === 'json')
92
+ } else {
93
+ artifacts = [...params.previousRunArtifacts]
94
+ }
95
+
96
+ if (artifacts.length > 0) {
97
+ const carryContext = artifacts.map((a) => `[carry-forward] ${a.name}: ${a.pointer}`)
98
+ draft.objective = `${draft.objective}\n\nCarry-forward context:\n${carryContext.join('\n')}`
99
+ }
100
+
101
+ return draft
102
+ }
103
+
104
+ interface PlanCycleDeps {
105
+ db: SurrealDBService
106
+ planSchedulerService: ReturnType<typeof makePlanSchedulerService>
107
+ planTemplateService: ReturnType<typeof makePlanTemplateService>
108
+ }
109
+
110
+ export function makePlanCycleService(deps: PlanCycleDeps) {
111
+ const { db, planSchedulerService, planTemplateService } = deps
112
+
113
+ const getCycleEffect = Effect.fn('PlanCycle.getCycle')(function* (cycleId: RecordIdInput) {
114
+ return yield* db.findOne(
115
+ TABLES.PLAN_CYCLE,
116
+ { id: ensureRecordId(cycleId, TABLES.PLAN_CYCLE) },
117
+ PlanCycleRecordSchema,
118
+ )
119
+ })
120
+
121
+ const createCycleEffect = Effect.fn('PlanCycle.createCycle')(function* (params: {
122
+ organizationId: RecordIdInput
123
+ threadId: RecordIdInput
124
+ templateId: RecordIdInput
125
+ name: string
126
+ schedule: CycleSchedule
127
+ carryForwardPolicy?: CarryForwardPolicy
128
+ leadAgentId: string
129
+ }) {
130
+ const now = nowDate()
131
+
132
+ const cycle = yield* db
133
+ .create(
134
+ TABLES.PLAN_CYCLE,
135
+ {
136
+ organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
137
+ threadId: ensureRecordId(params.threadId, TABLES.THREAD),
138
+ templateId: ensureRecordId(params.templateId, TABLES.PLAN_TEMPLATE),
139
+ name: params.name,
140
+ schedule: params.schedule,
141
+ carryForwardPolicy: params.carryForwardPolicy ?? 'incomplete-only',
142
+ status: 'active',
143
+ currentIteration: 0,
144
+ createdAt: now,
145
+ },
146
+ PlanCycleRecordSchema,
147
+ )
148
+ .pipe(Effect.withSpan('PlanCycle.persistCycle'))
149
+
150
+ const createResult = yield* planTemplateService
151
+ .instantiate({
152
+ templateId: params.templateId,
153
+ organizationId: params.organizationId,
154
+ threadId: params.threadId,
155
+ leadAgentId: params.leadAgentId,
156
+ })
157
+ .pipe(Effect.withSpan('PlanCycle.instantiateTemplate'))
158
+
159
+ const createdRunId = createResult.plan?.runId
160
+ const scheduleSpec = cycleScheduleToSpec(params.schedule)
161
+ const scheduleParams: {
162
+ organizationId: RecordIdInput
163
+ threadId: RecordIdInput
164
+ scheduleSpec: PlanScheduleSpec
165
+ runId?: RecordIdInput
166
+ } = { organizationId: params.organizationId, threadId: params.threadId, scheduleSpec }
167
+ if (createdRunId !== undefined) {
168
+ scheduleParams.runId = ensureRecordId(createdRunId, TABLES.PLAN_RUN)
169
+ }
170
+
171
+ const scheduleRecord = yield* planSchedulerService.createSchedule(scheduleParams)
172
+
173
+ const updatedPayload: { scheduleId: RecordIdInput; currentRunId?: RecordIdInput; currentIteration: number } = {
174
+ scheduleId: ensureRecordId(scheduleRecord.id, TABLES.PLAN_SCHEDULE),
175
+ currentIteration: 1,
176
+ }
177
+ if (createdRunId !== undefined) {
178
+ updatedPayload.currentRunId = ensureRecordId(createdRunId, TABLES.PLAN_RUN)
179
+ }
180
+
181
+ const updated = yield* db.update(
182
+ TABLES.PLAN_CYCLE,
183
+ ensureRecordId(cycle.id, TABLES.PLAN_CYCLE),
184
+ updatedPayload,
185
+ PlanCycleRecordSchema,
186
+ )
187
+
188
+ return updated ?? cycle
189
+ })
190
+
191
+ const advanceCycleEffect = Effect.fn('PlanCycle.advanceCycle')(function* (cycleId: RecordIdInput) {
192
+ const cycle = yield* getCycleEffect(cycleId)
193
+ if (!cycle) {
194
+ return yield* new PlanCycleNotFoundError({
195
+ cycleId: recordIdToString(cycleId, TABLES.PLAN_CYCLE),
196
+ message: `Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`,
197
+ })
198
+ }
199
+
200
+ if (cycle.status !== 'active') {
201
+ return
202
+ }
203
+
204
+ const currentRunId = cycle.currentRunId
205
+ if (currentRunId !== undefined) {
206
+ const runRecord = yield* db.findOne(
207
+ TABLES.PLAN_RUN,
208
+ { id: ensureRecordId(currentRunId, TABLES.PLAN_RUN) },
209
+ PlanRunSchema,
210
+ )
211
+ if (runRecord && !TERMINAL_RUN_STATUSES.has(runRecord.status)) {
212
+ return
213
+ }
214
+ }
215
+
216
+ const template = yield* planTemplateService.getTemplate(cycle.templateId)
217
+ if (!template) {
218
+ return yield* new PlanTemplateNotFoundForCycleError({
219
+ templateId: recordIdToString(cycle.templateId, TABLES.PLAN_TEMPLATE),
220
+ message: `Template not found for cycle: ${recordIdToString(cycle.templateId, TABLES.PLAN_TEMPLATE)}`,
221
+ })
222
+ }
223
+
224
+ let previousRunArtifacts: PlanArtifactRecord[] = []
225
+ if (currentRunId !== undefined) {
226
+ previousRunArtifacts = yield* db
227
+ .findMany(TABLES.PLAN_ARTIFACT, { runId: ensureRecordId(currentRunId, TABLES.PLAN_RUN) }, PlanArtifactSchema, {
228
+ orderBy: 'createdAt',
229
+ orderDir: 'ASC',
230
+ })
231
+ .pipe(Effect.withSpan('PlanCycle.loadPreviousRunArtifacts'))
232
+ }
233
+
234
+ const draft = buildCarryForwardDraft({ template, previousRunArtifacts, policy: cycle.carryForwardPolicy })
235
+
236
+ const result = yield* planTemplateService
237
+ .instantiate({
238
+ templateId: cycle.templateId,
239
+ organizationId: cycle.organizationId,
240
+ threadId: cycle.threadId,
241
+ leadAgentId: 'system',
242
+ overrides: draft,
243
+ })
244
+ .pipe(Effect.withSpan('PlanCycle.instantiateTemplate'))
245
+
246
+ const newRunId = result.plan?.runId
247
+ const updatePayload: { currentRunId?: RecordIdInput; currentIteration: number } = {
248
+ currentIteration: cycle.currentIteration + 1,
249
+ }
250
+ if (newRunId !== undefined) {
251
+ updatePayload.currentRunId = ensureRecordId(newRunId, TABLES.PLAN_RUN)
252
+ }
253
+
254
+ yield* db.update(
255
+ TABLES.PLAN_CYCLE,
256
+ ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
257
+ updatePayload,
258
+ PlanCycleRecordSchema,
259
+ )
260
+ })
261
+
262
+ const cancelCycleEffect = Effect.fn('PlanCycle.cancelCycle')(function* (cycleId: RecordIdInput) {
263
+ const cycle = yield* getCycleEffect(cycleId)
264
+ if (!cycle) {
265
+ return yield* new PlanCycleNotFoundError({
266
+ cycleId: recordIdToString(cycleId, TABLES.PLAN_CYCLE),
267
+ message: `Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`,
268
+ })
269
+ }
270
+
271
+ const scheduleId = cycle.scheduleId
272
+ if (scheduleId !== undefined) {
273
+ yield* planSchedulerService.cancelSchedule(scheduleId)
274
+ }
275
+
276
+ yield* db.update(
277
+ TABLES.PLAN_CYCLE,
278
+ ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
279
+ { status: 'cancelled' },
280
+ PlanCycleRecordSchema,
281
+ )
282
+ })
283
+
284
+ const pauseCycleEffect = Effect.fn('PlanCycle.pauseCycle')(function* (cycleId: RecordIdInput) {
285
+ const cycle = yield* getCycleEffect(cycleId)
286
+ if (!cycle) {
287
+ return yield* new PlanCycleNotFoundError({
288
+ cycleId: recordIdToString(cycleId, TABLES.PLAN_CYCLE),
289
+ message: `Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`,
290
+ })
291
+ }
292
+
293
+ const scheduleId = cycle.scheduleId
294
+ if (scheduleId !== undefined) {
295
+ yield* planSchedulerService.pauseSchedule(scheduleId)
296
+ }
297
+
298
+ yield* db.update(
299
+ TABLES.PLAN_CYCLE,
300
+ ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
301
+ { status: 'paused' },
302
+ PlanCycleRecordSchema,
303
+ )
304
+ })
305
+
306
+ const resumeCycleEffect = Effect.fn('PlanCycle.resumeCycle')(function* (cycleId: RecordIdInput) {
307
+ const cycle = yield* getCycleEffect(cycleId)
308
+ if (!cycle) {
309
+ return yield* new PlanCycleNotFoundError({
310
+ cycleId: recordIdToString(cycleId, TABLES.PLAN_CYCLE),
311
+ message: `Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`,
312
+ })
313
+ }
314
+
315
+ const scheduleId = cycle.scheduleId
316
+ if (scheduleId !== undefined) {
317
+ yield* planSchedulerService.resumeSchedule(scheduleId)
318
+ }
319
+
320
+ yield* db.update(
321
+ TABLES.PLAN_CYCLE,
322
+ ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
323
+ { status: 'active' },
324
+ PlanCycleRecordSchema,
325
+ )
326
+ })
327
+
328
+ const listCyclesEffect = Effect.fn('PlanCycle.listCycles')(function* (threadId: RecordIdInput) {
329
+ return yield* db.findMany(
330
+ TABLES.PLAN_CYCLE,
331
+ { threadId: ensureRecordId(threadId, TABLES.THREAD) },
332
+ PlanCycleRecordSchema,
333
+ { orderBy: 'createdAt', orderDir: 'ASC' },
334
+ )
335
+ })
336
+
337
+ return {
338
+ cycleScheduleToSpec,
339
+ createCycle(params: {
340
+ organizationId: RecordIdInput
341
+ threadId: RecordIdInput
342
+ templateId: RecordIdInput
343
+ name: string
344
+ schedule: CycleSchedule
345
+ carryForwardPolicy?: CarryForwardPolicy
346
+ leadAgentId: string
347
+ }): Promise<PlanCycleRecord> {
348
+ return runPromise(createCycleEffect(params))
349
+ },
350
+ createCycleEffect,
351
+
352
+ advanceCycle(cycleId: RecordIdInput): Promise<void> {
353
+ return runPromise(advanceCycleEffect(cycleId))
354
+ },
355
+ advanceCycleEffect,
356
+
357
+ buildCarryForwardDraft(params: {
358
+ template: PlanTemplateRecord
359
+ previousRunArtifacts: PlanArtifactRecord[]
360
+ policy: CarryForwardPolicy
361
+ }): PlanDraft {
362
+ return buildCarryForwardDraft(params)
363
+ },
364
+
365
+ cancelCycle(cycleId: RecordIdInput): Promise<void> {
366
+ return runPromise(cancelCycleEffect(cycleId))
367
+ },
368
+ cancelCycleEffect,
369
+
370
+ pauseCycle(cycleId: RecordIdInput): Promise<void> {
371
+ return runPromise(pauseCycleEffect(cycleId))
372
+ },
373
+ pauseCycleEffect,
374
+
375
+ resumeCycle(cycleId: RecordIdInput): Promise<void> {
376
+ return runPromise(resumeCycleEffect(cycleId))
377
+ },
378
+ resumeCycleEffect,
379
+
380
+ listCycles(threadId: RecordIdInput): Promise<PlanCycleRecord[]> {
381
+ return runPromise(listCyclesEffect(threadId))
382
+ },
383
+ listCyclesEffect,
384
+
385
+ getCycle(cycleId: RecordIdInput): Promise<PlanCycleRecord | null> {
386
+ return runPromise(getCycleEffect(cycleId))
387
+ },
388
+ getCycleEffect,
389
+ }
390
+ }
391
+
392
+ export class PlanCycleServiceTag extends Context.Service<
393
+ PlanCycleServiceTag,
394
+ ReturnType<typeof makePlanCycleService>
395
+ >()('@lota-sdk/core/PlanCycleService') {}
396
+
397
+ export const PlanCycleServiceLive = Layer.effect(
398
+ PlanCycleServiceTag,
399
+ Effect.gen(function* () {
400
+ const db = yield* DatabaseServiceTag
401
+ const planSchedulerService = yield* PlanSchedulerServiceTag
402
+ const planTemplateService = yield* PlanTemplateServiceTag
403
+ return makePlanCycleService({ db, planSchedulerService, planTemplateService })
404
+ }),
405
+ )