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