@lota-sdk/core 0.4.7 → 0.4.9

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