@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,343 @@
1
+ import type { ChatMessage } from '@lota-sdk/shared'
2
+ import { createUIMessageStream } from 'ai'
3
+ import { Context, Schema, Effect, Layer } from 'effect'
4
+
5
+ import { ensureRecordId, recordIdToString } from '../../db/record-id'
6
+ import { TABLES } from '../../db/tables'
7
+ import { BadRequestError } from '../../effect/errors'
8
+ import { runPromise } from '../../effect/runtime'
9
+ import { getCurrentRuntime } from '../../effect/runtime-ref'
10
+ import { hasApprovalRespondedParts, isApprovalContinuationRequest } from '../../runtime/approval-continuation'
11
+ import { shouldPlanNodeUseVisibleTurn } from '../../runtime/execution-plan-visibility'
12
+ import { wrapResponseWithKeepalive } from '../../utils/sse-keepalive'
13
+ import type { makePlanExecutorService } from '../plan/plan-executor.service'
14
+ import { PlanExecutorServiceTag } from '../plan/plan-executor.service'
15
+ import type { makePlanRunService } from '../plan/plan-run.service'
16
+ import { PlanRunServiceTag } from '../plan/plan-run.service'
17
+ import type { makeUserService } from '../user.service'
18
+ import { UserServiceTag } from '../user.service'
19
+ import type {
20
+ PreparedThreadTurnResult,
21
+ ThreadApprovalContinuationParams,
22
+ ThreadPlanTurnParams,
23
+ ThreadTurnParams,
24
+ makeThreadTurnPreparationService,
25
+ } from './thread-turn-preparation.service'
26
+ import { ThreadTurnPreparationServiceTag } from './thread-turn-preparation.service'
27
+ import { buildThreadTurnSpanAttributes, compactSpanAttributes } from './thread-turn-tracing'
28
+ import type { makeThreadService } from './thread.service'
29
+ import { ThreadServiceTag } from './thread.service'
30
+
31
+ export { hasApprovalRespondedParts, isApprovalContinuationRequest }
32
+ export { wrapResponseWithKeepalive }
33
+ export type { PreparedThreadTurnResult }
34
+ export type { ThreadPlanTurnParams }
35
+
36
+ class ThreadTurnServiceError extends Schema.TaggedErrorClass<ThreadTurnServiceError>()('ThreadTurnServiceError', {
37
+ message: Schema.String,
38
+ cause: Schema.optional(Schema.Defect),
39
+ }) {}
40
+
41
+ interface ThreadTurnDeps {
42
+ planExecutor: ReturnType<typeof makePlanExecutorService>
43
+ planRun: ReturnType<typeof makePlanRunService>
44
+ thread: ReturnType<typeof makeThreadService>
45
+ threadTurnPreparation: ReturnType<typeof makeThreadTurnPreparationService>
46
+ user: ReturnType<typeof makeUserService>
47
+ }
48
+
49
+ const createThreadApprovalContinuationStreamEffect = Effect.fn('ThreadTurn.createApprovalContinuationStream')(
50
+ function* (deps: ThreadTurnDeps, params: ThreadApprovalContinuationParams) {
51
+ const currentContext = yield* Effect.context()
52
+ const runPromiseWithCurrentContext = Effect.runPromiseWith(currentContext)
53
+ const prepared = yield* deps.threadTurnPreparation.prepareThreadRunCoreEffect({
54
+ ...params,
55
+ kind: 'approvalContinuation',
56
+ })
57
+ return createUIMessageStream<ChatMessage>({
58
+ originalMessages: prepared.originalMessages,
59
+ onError: (error) => (error instanceof Error ? error.message : 'Approval continuation stream failed.'),
60
+ execute: ({ writer }) =>
61
+ runPromiseWithCurrentContext(
62
+ prepared.run(writer).pipe(Effect.withSpan('ThreadTurn.executeApprovalContinuationStream'), Effect.asVoid),
63
+ ),
64
+ })
65
+ },
66
+ )
67
+
68
+ function createThreadApprovalContinuationStreamWith(deps: ThreadTurnDeps, params: ThreadApprovalContinuationParams) {
69
+ return createThreadApprovalContinuationStreamEffect(deps, params).pipe(
70
+ Effect.annotateSpans(
71
+ buildThreadTurnSpanAttributes({
72
+ threadRef: params.threadRef,
73
+ orgRef: params.orgRef,
74
+ userRef: params.userRef,
75
+ kind: 'approvalContinuation',
76
+ streamId: params.streamId,
77
+ threadType: params.thread.type,
78
+ }),
79
+ ),
80
+ )
81
+ }
82
+
83
+ const createThreadNativeToolApprovalStreamEffect = Effect.fn('ThreadTurn.createNativeToolApprovalStream')(function* (
84
+ deps: ThreadTurnDeps,
85
+ params: ThreadApprovalContinuationParams,
86
+ ) {
87
+ const currentContext = yield* Effect.context()
88
+ const runPromiseWithCurrentContext = Effect.runPromiseWith(currentContext)
89
+ const prepared = yield* deps.threadTurnPreparation.prepareThreadRunCoreEffect({
90
+ ...params,
91
+ kind: 'nativeToolApprovalTurn',
92
+ })
93
+ return createUIMessageStream<ChatMessage>({
94
+ originalMessages: prepared.originalMessages,
95
+ onError: (error) => (error instanceof Error ? error.message : 'Native tool approval stream failed.'),
96
+ execute: ({ writer }) =>
97
+ runPromiseWithCurrentContext(
98
+ prepared.run(writer).pipe(Effect.withSpan('ThreadTurn.executeNativeToolApprovalStream'), Effect.asVoid),
99
+ ),
100
+ })
101
+ })
102
+
103
+ function createThreadNativeToolApprovalStreamWith(deps: ThreadTurnDeps, params: ThreadApprovalContinuationParams) {
104
+ return createThreadNativeToolApprovalStreamEffect(deps, params).pipe(
105
+ Effect.annotateSpans(
106
+ buildThreadTurnSpanAttributes({
107
+ threadRef: params.threadRef,
108
+ orgRef: params.orgRef,
109
+ userRef: params.userRef,
110
+ kind: 'nativeToolApprovalTurn',
111
+ streamId: params.streamId,
112
+ threadType: params.thread.type,
113
+ }),
114
+ ),
115
+ )
116
+ }
117
+
118
+ const createThreadTurnStreamEffect = Effect.fn('ThreadTurn.createThreadTurnStream')(function* (
119
+ deps: ThreadTurnDeps,
120
+ params: ThreadTurnParams,
121
+ ) {
122
+ const currentContext = yield* Effect.context()
123
+ const runPromiseWithCurrentContext = Effect.runPromiseWith(currentContext)
124
+ const prepared = yield* deps.threadTurnPreparation.prepareThreadRunCoreEffect({ ...params, kind: 'userTurn' })
125
+ return createUIMessageStream<ChatMessage>({
126
+ originalMessages: prepared.originalMessages,
127
+ onError: (error) => (error instanceof Error ? error.message : 'Chat stream failed.'),
128
+ execute: ({ writer }) =>
129
+ runPromiseWithCurrentContext(
130
+ prepared.run(writer).pipe(Effect.withSpan('ThreadTurn.executeThreadTurnStream'), Effect.asVoid),
131
+ ),
132
+ })
133
+ })
134
+
135
+ function createThreadTurnStreamWith(deps: ThreadTurnDeps, params: ThreadTurnParams) {
136
+ return createThreadTurnStreamEffect(deps, params).pipe(
137
+ Effect.annotateSpans(
138
+ buildThreadTurnSpanAttributes({
139
+ threadRef: params.threadRef,
140
+ orgRef: params.orgRef,
141
+ userRef: params.userRef,
142
+ kind: 'userTurn',
143
+ streamId: params.streamId,
144
+ threadType: params.thread.type,
145
+ }),
146
+ ),
147
+ )
148
+ }
149
+
150
+ const runThreadTurnInBackgroundEffect = Effect.fn('ThreadTurn.runThreadTurnInBackground')(function* (
151
+ deps: ThreadTurnDeps,
152
+ params: ThreadTurnParams,
153
+ ) {
154
+ const prepared = yield* deps.threadTurnPreparation.prepareThreadRunCoreEffect({ ...params, kind: 'userTurn' })
155
+ return yield* prepared.run().pipe(Effect.withSpan('ThreadTurn.executeBackgroundTurn'))
156
+ })
157
+
158
+ function runThreadTurnInBackgroundWith(deps: ThreadTurnDeps, params: ThreadTurnParams) {
159
+ return runThreadTurnInBackgroundEffect(deps, params).pipe(
160
+ Effect.annotateSpans(
161
+ buildThreadTurnSpanAttributes({
162
+ threadRef: params.threadRef,
163
+ orgRef: params.orgRef,
164
+ userRef: params.userRef,
165
+ kind: 'background',
166
+ streamId: params.streamId,
167
+ threadType: params.thread.type,
168
+ agentId: params.agentIdOverride,
169
+ }),
170
+ ),
171
+ )
172
+ }
173
+
174
+ const triggerPlanNodeTurnEffect = Effect.fn('ThreadTurn.triggerPlanNodeTurn')(function* (
175
+ deps: ThreadTurnDeps,
176
+ params: { runId: string; nodeId: string; abortSignal?: AbortSignal; streamId?: string },
177
+ ) {
178
+ const run = yield* deps.planRun.getRunById(params.runId)
179
+ const spec = yield* deps.planRun.getPlanSpecById(run.planSpecId)
180
+ const nodeSpec = yield* deps.planRun.getNodeSpecByNodeId(spec.id, params.nodeId)
181
+
182
+ if (!shouldPlanNodeUseVisibleTurn(spec, nodeSpec) || nodeSpec.owner.executorType !== 'agent') {
183
+ return yield* new BadRequestError({
184
+ message: `Plan node "${params.nodeId}" is not eligible for a visible plan turn.`,
185
+ })
186
+ }
187
+
188
+ let activeRun = run
189
+ let nodeRun = yield* deps.planRun.getNodeRunByNodeId(run.id, params.nodeId)
190
+ if (nodeRun.status === 'ready') {
191
+ yield* Effect.tryPromise({
192
+ try: () => deps.planExecutor.transitionNodeToRunning({ runId: params.runId, nodeId: params.nodeId }),
193
+ catch: (cause) => new ThreadTurnServiceError({ message: 'Failed to transition plan node to running.', cause }),
194
+ })
195
+ activeRun = yield* deps.planRun.getRunById(params.runId)
196
+ nodeRun = yield* deps.planRun.getNodeRunByNodeId(run.id, params.nodeId)
197
+ }
198
+
199
+ if (nodeRun.status !== 'running') {
200
+ return { assistantMessages: [], inputMessageId: undefined }
201
+ }
202
+
203
+ const thread = yield* deps.thread.getThread(activeRun.threadId)
204
+ const userRef = ensureRecordId(thread.userId, TABLES.USER)
205
+ const [artifacts, nodeRuns, nodeSpecs, userName] = yield* Effect.all([
206
+ deps.planRun.listArtifacts(activeRun.id),
207
+ deps.planRun.listNodeRuns(activeRun.id),
208
+ deps.planRun.listNodeSpecs(spec.id),
209
+ deps.user.getUser(recordIdToString(userRef, TABLES.USER)).pipe(
210
+ Effect.map((user) => user.name),
211
+ Effect.catch(() => Effect.succeed(undefined as string | undefined)),
212
+ ),
213
+ ]).pipe(Effect.withSpan('ThreadTurn.loadPlanTurnContext'))
214
+
215
+ const inputArtifacts = artifacts
216
+ .filter((artifact) => nodeSpec.upstreamNodeIds.includes(artifact.nodeId))
217
+ .map((artifact) => ({
218
+ name: artifact.name,
219
+ kind: artifact.kind,
220
+ ...(artifact.description ? { description: artifact.description } : {}),
221
+ ...(artifact.content !== undefined ? { content: artifact.content } : {}),
222
+ ...(artifact.payload !== undefined ? { payload: artifact.payload } : {}),
223
+ ...(artifact.publishedArtifactId
224
+ ? { publishedArtifactId: recordIdToString(artifact.publishedArtifactId, TABLES.ARTIFACT) }
225
+ : {}),
226
+ }))
227
+ const upstreamNodeSpecs = new Map(nodeSpecs.map((upstreamNodeSpec) => [upstreamNodeSpec.nodeId, upstreamNodeSpec]))
228
+ const upstreamHandoffs = nodeRuns
229
+ .filter((candidate) => nodeSpec.upstreamNodeIds.includes(candidate.nodeId) && candidate.latestNotes)
230
+ .map((candidate) => ({
231
+ nodeId: candidate.nodeId,
232
+ label: upstreamNodeSpecs.get(candidate.nodeId)?.label ?? candidate.nodeId,
233
+ ownerRef: upstreamNodeSpecs.get(candidate.nodeId)?.owner.ref ?? 'system',
234
+ ownerType: upstreamNodeSpecs.get(candidate.nodeId)?.owner.executorType ?? 'system',
235
+ summary: candidate.latestNotes ?? undefined,
236
+ }))
237
+
238
+ const threadRef = ensureRecordId(activeRun.threadId, TABLES.THREAD)
239
+ const orgRef = ensureRecordId(activeRun.organizationId, TABLES.ORGANIZATION)
240
+ const prepared = yield* deps.threadTurnPreparation.prepareThreadRunCoreEffect({
241
+ kind: 'planTurn',
242
+ thread,
243
+ threadRef,
244
+ orgRef,
245
+ userRef,
246
+ userName,
247
+ abortSignal: params.abortSignal,
248
+ streamId: params.streamId,
249
+ planTurn: {
250
+ runId: params.runId,
251
+ nodeId: params.nodeId,
252
+ planTitle: spec.title,
253
+ nodeSpec,
254
+ nodeRun,
255
+ resolvedInput: nodeRun.resolvedInput ?? {},
256
+ inputArtifacts,
257
+ upstreamHandoffs,
258
+ },
259
+ })
260
+ return yield* prepared.run().pipe(Effect.withSpan('ThreadTurn.executePlanTurn'))
261
+ })
262
+
263
+ function triggerPlanNodeTurnWith(
264
+ deps: ThreadTurnDeps,
265
+ params: { runId: string; nodeId: string; abortSignal?: AbortSignal; streamId?: string },
266
+ ) {
267
+ return triggerPlanNodeTurnEffect(deps, params).pipe(
268
+ Effect.annotateSpans(
269
+ compactSpanAttributes({
270
+ turnKind: 'planTurn',
271
+ streamId: params.streamId,
272
+ planRunId: params.runId,
273
+ planNodeId: params.nodeId,
274
+ }),
275
+ ),
276
+ )
277
+ }
278
+
279
+ export function makeThreadTurnService(deps: ThreadTurnDeps) {
280
+ return {
281
+ createThreadApprovalContinuationStream(params: ThreadApprovalContinuationParams) {
282
+ return createThreadApprovalContinuationStreamWith(deps, params)
283
+ },
284
+ createThreadNativeToolApprovalStream(params: ThreadApprovalContinuationParams) {
285
+ return createThreadNativeToolApprovalStreamWith(deps, params)
286
+ },
287
+ createThreadTurnStream(params: ThreadTurnParams) {
288
+ return createThreadTurnStreamWith(deps, params)
289
+ },
290
+ runThreadTurnInBackground(params: ThreadTurnParams) {
291
+ return runThreadTurnInBackgroundWith(deps, params)
292
+ },
293
+ triggerPlanNodeTurn(params: { runId: string; nodeId: string; abortSignal?: AbortSignal; streamId?: string }) {
294
+ return triggerPlanNodeTurnWith(deps, params)
295
+ },
296
+ } as const
297
+ }
298
+
299
+ export class ThreadTurnServiceTag extends Context.Service<
300
+ ThreadTurnServiceTag,
301
+ ReturnType<typeof makeThreadTurnService>
302
+ >()('ThreadTurnService') {}
303
+
304
+ export const ThreadTurnServiceLive = Layer.effect(
305
+ ThreadTurnServiceTag,
306
+ Effect.gen(function* () {
307
+ const planExecutor = yield* PlanExecutorServiceTag
308
+ const planRun = yield* PlanRunServiceTag
309
+ const thread = yield* ThreadServiceTag
310
+ const threadTurnPreparation = yield* ThreadTurnPreparationServiceTag
311
+ const user = yield* UserServiceTag
312
+ return makeThreadTurnService({ planExecutor, planRun, thread, threadTurnPreparation, user })
313
+ }),
314
+ )
315
+
316
+ function getThreadTurnService() {
317
+ return getCurrentRuntime().runSync(Effect.service(ThreadTurnServiceTag))
318
+ }
319
+
320
+ export function createThreadApprovalContinuationStream(params: ThreadApprovalContinuationParams) {
321
+ return runPromise(getThreadTurnService().createThreadApprovalContinuationStream(params))
322
+ }
323
+
324
+ export function createThreadNativeToolApprovalStream(params: ThreadApprovalContinuationParams) {
325
+ return runPromise(getThreadTurnService().createThreadNativeToolApprovalStream(params))
326
+ }
327
+
328
+ export function createThreadTurnStream(params: ThreadTurnParams) {
329
+ return runPromise(getThreadTurnService().createThreadTurnStream(params))
330
+ }
331
+
332
+ export function runThreadTurnInBackground(params: ThreadTurnParams): Promise<PreparedThreadTurnResult> {
333
+ return runPromise(getThreadTurnService().runThreadTurnInBackground(params))
334
+ }
335
+
336
+ export function triggerPlanNodeTurn(params: {
337
+ runId: string
338
+ nodeId: string
339
+ abortSignal?: AbortSignal
340
+ streamId?: string
341
+ }): Promise<PreparedThreadTurnResult> {
342
+ return runPromise(getThreadTurnService().triggerPlanNodeTurn(params))
343
+ }
@@ -0,0 +1,335 @@
1
+ import { THREAD, sdkThreadStatusSchema } from '@lota-sdk/shared'
2
+ import { Context, Effect, Layer } from 'effect'
3
+ import { surql } from 'surrealdb'
4
+
5
+ import { getCoreThreadProfile, isAgentName } from '../../config/agent-defaults'
6
+ import { ensureRecordId, isRecordIdInput, recordIdToString } from '../../db/record-id'
7
+ import type { RecordIdRef } from '../../db/record-id'
8
+ import type { SurrealDBService } from '../../db/service'
9
+ import { TABLES } from '../../db/tables'
10
+ import { BadRequestError, ServiceError } from '../../effect/errors'
11
+ import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
12
+ import { DatabaseServiceTag, RedisServiceTag } from '../../effect/services'
13
+ import type { RedisConnectionManager } from '../../redis/connection'
14
+ import { CompactionCoordinationTag } from '../../runtime/chat-run-orchestration'
15
+ import { toIsoDateTimeString } from '../../utils/date-time'
16
+ import { ChatRunRegistryTag } from '../chat-run-registry.service'
17
+ import { ContextCompactionServiceTag } from '../context-compaction.service'
18
+ import type { makeContextCompactionService } from '../context-compaction.service'
19
+ import { createThreadActiveRunHelpers } from './thread-active-run'
20
+ import { createThreadBootstrapHelpers } from './thread-bootstrap'
21
+ import { createThreadListingHelpers } from './thread-listing'
22
+ import { createThreadMemoryBlockHelpers, formatMemoryBlockForPrompt } from './thread-memory-block'
23
+ import { ThreadMessageServiceTag } from './thread-message.service'
24
+ import type { makeThreadMessageService } from './thread-message.service'
25
+ import { createThreadRecordStore } from './thread-record-store'
26
+ import { NormalizedThreadSchema, PublicThreadSchema } from './thread.types'
27
+ import type { NormalizedThread, PublicThread, ThreadRecord } from './thread.types'
28
+
29
+ export { ActiveThreadRunConflictError } from '../../effect/errors'
30
+
31
+ function assertMutableThreadEffect(thread: ThreadRecord): Effect.Effect<void, BadRequestError> {
32
+ return thread.type === 'default'
33
+ ? Effect.fail(new BadRequestError({ message: 'Default threads cannot be modified.' }))
34
+ : Effect.void
35
+ }
36
+
37
+ function getDefaultTitle(thread: Pick<ThreadRecord, 'type' | 'threadType'>): string {
38
+ if (thread.type === 'thread' && typeof thread.threadType === 'string') {
39
+ return getCoreThreadProfile(thread.threadType).config.title
40
+ }
41
+
42
+ return THREAD.DEFAULT_TITLE
43
+ }
44
+
45
+ function normalizeRecordIdStringEffect(id: unknown, table: string): Effect.Effect<string, BadRequestError> {
46
+ return isRecordIdInput(id)
47
+ ? Effect.succeed(recordIdToString(ensureRecordId(id, table), table))
48
+ : Effect.fail(new BadRequestError({ message: `Invalid record id for table ${table}.` }))
49
+ }
50
+
51
+ function toPublicThread(thread: NormalizedThread): PublicThread {
52
+ const { organizationId: _organizationId, userId: _userId, memoryBlock: _memoryBlock, ...publicThread } = thread
53
+ return PublicThreadSchema.parse(publicThread)
54
+ }
55
+
56
+ type ThreadServiceError = ServiceError | BadRequestError
57
+
58
+ const effectTryPromise = makeEffectTryPromiseWithMessage((message, cause) => new ServiceError({ message, cause }))
59
+
60
+ type ChatRunRegistry = Context.Service.Shape<typeof ChatRunRegistryTag>
61
+
62
+ type CompactionCoordination = Context.Service.Shape<typeof CompactionCoordinationTag>
63
+
64
+ interface ThreadServiceDeps {
65
+ db: SurrealDBService
66
+ redis: RedisConnectionManager
67
+ chatRunRegistry: ChatRunRegistry
68
+ threadMessageService: ReturnType<typeof makeThreadMessageService>
69
+ contextCompactionService: ReturnType<typeof makeContextCompactionService>
70
+ compactionCoordination: CompactionCoordination
71
+ }
72
+
73
+ export function makeThreadService(deps: ThreadServiceDeps) {
74
+ const threadStore = createThreadRecordStore({ db: deps.db })
75
+ const activeRun = createThreadActiveRunHelpers({
76
+ db: deps.db,
77
+ threadStore,
78
+ redis: deps.redis,
79
+ chatRunRegistry: deps.chatRunRegistry,
80
+ })
81
+
82
+ function computeIsRunning(
83
+ thread: Pick<ThreadRecord, 'id' | 'activeRunId'>,
84
+ options: { checkLease: boolean },
85
+ ): Effect.Effect<boolean, ThreadServiceError> {
86
+ const activeRunId =
87
+ typeof thread.activeRunId === 'string' && thread.activeRunId.trim().length > 0 ? thread.activeRunId : null
88
+
89
+ if (activeRunId === null) {
90
+ return Effect.succeed(false)
91
+ }
92
+
93
+ if (deps.chatRunRegistry.has(activeRunId)) {
94
+ return Effect.succeed(true)
95
+ }
96
+
97
+ if (!options.checkLease) {
98
+ return Effect.succeed(true)
99
+ }
100
+
101
+ return effectTryPromise(
102
+ () => activeRun.hasActiveRunLease(ensureRecordId(thread.id, TABLES.THREAD)),
103
+ 'Failed to check active thread run lease.',
104
+ )
105
+ }
106
+
107
+ function toNormalizedThread(
108
+ thread: ThreadRecord,
109
+ options: { checkLease?: boolean } = {},
110
+ ): Effect.Effect<NormalizedThread, ThreadServiceError> {
111
+ return Effect.gen(function* () {
112
+ const isRunning = yield* computeIsRunning(thread, { checkLease: options.checkLease ?? true })
113
+ const [id, userId, organizationId] = yield* Effect.all([
114
+ normalizeRecordIdStringEffect(thread.id, TABLES.THREAD),
115
+ normalizeRecordIdStringEffect(thread.userId, TABLES.USER),
116
+ normalizeRecordIdStringEffect(thread.organizationId, TABLES.ORGANIZATION),
117
+ ])
118
+ return NormalizedThreadSchema.parse({
119
+ id,
120
+ userId,
121
+ organizationId,
122
+ type: thread.type,
123
+ ...(thread.type === 'thread' && typeof thread.threadType === 'string' ? { threadType: thread.threadType } : {}),
124
+ nameGenerated: thread.nameGenerated,
125
+ isRunning,
126
+ isCompacting: thread.isCompacting === true,
127
+ ...(isAgentName(thread.agentId) ? { agentId: thread.agentId } : {}),
128
+ title: thread.title ?? getDefaultTitle(thread),
129
+ status: thread.status,
130
+ memoryBlock: formatMemoryBlockForPrompt(thread),
131
+ members: thread.members,
132
+ createdAt: toIsoDateTimeString(thread.createdAt),
133
+ updatedAt: toIsoDateTimeString(thread.updatedAt),
134
+ })
135
+ })
136
+ }
137
+
138
+ function toNormalizedThreads(
139
+ threads: ThreadRecord[],
140
+ options: { checkLease?: boolean } = {},
141
+ ): Effect.Effect<NormalizedThread[], ThreadServiceError> {
142
+ return Effect.forEach(threads, (thread) => toNormalizedThread(thread, options))
143
+ }
144
+
145
+ const bootstrap = createThreadBootstrapHelpers({
146
+ threadStore,
147
+ threadMessageService: deps.threadMessageService,
148
+ redis: deps.redis,
149
+ normalizeThread: toNormalizedThread,
150
+ })
151
+
152
+ const listing = createThreadListingHelpers({ db: deps.db, normalizeThreads: toNormalizedThreads })
153
+
154
+ const memory = createThreadMemoryBlockHelpers({
155
+ threadStore,
156
+ contextCompactionService: { compactMemoryBlock: deps.contextCompactionService.compactMemoryBlock },
157
+ })
158
+
159
+ function getById(threadId: RecordIdRef) {
160
+ return effectTryPromise(() => threadStore.getById(threadId), 'Failed to load thread.')
161
+ }
162
+
163
+ function getThread(threadId: RecordIdRef) {
164
+ return Effect.gen(function* () {
165
+ const thread = yield* getById(threadId)
166
+ return yield* toNormalizedThread(thread)
167
+ })
168
+ }
169
+
170
+ function updateTitle(threadId: RecordIdRef, title: string) {
171
+ return Effect.gen(function* () {
172
+ const existing = yield* getById(threadId)
173
+ yield* assertMutableThreadEffect(existing)
174
+ const thread = yield* effectTryPromise(
175
+ () => threadStore.update(threadId, { title, nameGenerated: true }),
176
+ 'Failed to update thread title.',
177
+ )
178
+ return yield* toNormalizedThread(thread)
179
+ })
180
+ }
181
+
182
+ function updateStatus(threadId: RecordIdRef, status: string) {
183
+ return Effect.gen(function* () {
184
+ const parsedStatus = sdkThreadStatusSchema.safeParse(status)
185
+ if (!parsedStatus.success) {
186
+ return yield* new BadRequestError({ message: `Invalid thread status: ${status}` })
187
+ }
188
+ const existing = yield* getById(threadId)
189
+ yield* assertMutableThreadEffect(existing)
190
+ const thread = yield* effectTryPromise(
191
+ () => threadStore.update(threadId, { status: parsedStatus.data }),
192
+ 'Failed to update thread status.',
193
+ )
194
+ return yield* toNormalizedThread(thread)
195
+ })
196
+ }
197
+
198
+ function setCompacting(threadId: RecordIdRef, value: boolean) {
199
+ const threadRef = ensureRecordId(threadId, TABLES.THREAD)
200
+ const threadIdString = recordIdToString(threadRef, TABLES.THREAD)
201
+ return Effect.asVoid(
202
+ effectTryPromise(
203
+ () => deps.db.query<unknown>(surql`UPDATE ONLY ${threadRef} SET isCompacting = ${value}`),
204
+ 'Failed to update thread compaction flag.',
205
+ ).pipe(Effect.tap(() => deps.compactionCoordination.signal(threadIdString, value))),
206
+ )
207
+ }
208
+
209
+ function clearThread(threadId: RecordIdRef) {
210
+ return Effect.gen(function* () {
211
+ const existing = yield* getById(threadId)
212
+ yield* assertMutableThreadEffect(existing)
213
+ const threadRef = ensureRecordId(threadId, TABLES.THREAD)
214
+ yield* effectTryPromise(
215
+ () => deps.db.deleteWhere(TABLES.THREAD_MESSAGE, { threadId: threadRef }),
216
+ 'Failed to delete thread messages.',
217
+ )
218
+ yield* effectTryPromise(
219
+ () =>
220
+ deps.db.query<unknown>(surql`
221
+ UPDATE ONLY ${threadRef}
222
+ SET turnCount = 0,
223
+ compactionSummary = NONE,
224
+ lastCompactedMessageId = NONE,
225
+ activeRunId = NONE,
226
+ activeStreamId = NONE,
227
+ isCompacting = false
228
+ `),
229
+ 'Failed to reset thread state.',
230
+ )
231
+ })
232
+ }
233
+
234
+ function deleteThread(threadId: RecordIdRef) {
235
+ return Effect.gen(function* () {
236
+ const existing = yield* getById(threadId)
237
+ yield* assertMutableThreadEffect(existing)
238
+ yield* effectTryPromise(() => threadStore.deleteById(threadId), 'Failed to delete thread.')
239
+ })
240
+ }
241
+
242
+ function incrementTurnCount(threadId: RecordIdRef) {
243
+ const threadRef = ensureRecordId(threadId, TABLES.THREAD)
244
+ return Effect.gen(function* () {
245
+ const result = yield* effectTryPromise(
246
+ () =>
247
+ deps.db.query<{ turnCount: number }>(
248
+ surql`
249
+ UPDATE ONLY ${threadRef}
250
+ SET turnCount += 1
251
+ RETURN turnCount
252
+ `,
253
+ ),
254
+ 'Failed to increment thread turn count.',
255
+ )
256
+ return result[0].turnCount
257
+ })
258
+ }
259
+
260
+ return {
261
+ findById: (...args: Parameters<typeof threadStore.findById>) =>
262
+ effectTryPromise(() => threadStore.findById(...args), 'Failed to find thread.'),
263
+ getById,
264
+ findAll: (...args: Parameters<typeof threadStore.findAll>) =>
265
+ effectTryPromise(() => threadStore.findAll(...args), 'Failed to find all threads.'),
266
+ create: (...args: Parameters<typeof threadStore.create>) =>
267
+ effectTryPromise(() => threadStore.create(...args), 'Failed to create thread.'),
268
+ update: (...args: Parameters<typeof threadStore.update>) =>
269
+ effectTryPromise(() => threadStore.update(...args), 'Failed to update thread.'),
270
+ delete: (...args: Parameters<typeof threadStore.deleteById>) =>
271
+ effectTryPromise(() => threadStore.deleteById(...args), 'Failed to delete thread.'),
272
+ getOrCreateDefault: (...args: Parameters<typeof bootstrap.getOrCreateDefault>) =>
273
+ effectTryPromise(() => bootstrap.getOrCreateDefault(...args), 'Failed to get or create default thread.'),
274
+ getOrCreateThread: (...args: Parameters<typeof bootstrap.getOrCreateThread>) =>
275
+ effectTryPromise(() => bootstrap.getOrCreateThread(...args), 'Failed to get or create thread.'),
276
+ createThread: (...args: Parameters<typeof bootstrap.createThread>) =>
277
+ effectTryPromise(() => bootstrap.createThread(...args), 'Failed to create thread.'),
278
+ ensureBootstrapThreads: (...args: Parameters<typeof bootstrap.ensureBootstrapThreads>) =>
279
+ effectTryPromise(() => bootstrap.ensureBootstrapThreads(...args), 'Failed to ensure bootstrap threads.'),
280
+ listThreads: (...args: Parameters<typeof listing.listThreads>) =>
281
+ effectTryPromise(() => listing.listThreads(...args), 'Failed to list threads.'),
282
+ listOrganizationThreads: (...args: Parameters<typeof listing.listOrganizationThreads>) =>
283
+ effectTryPromise(() => listing.listOrganizationThreads(...args), 'Failed to list organization threads.'),
284
+ getThread,
285
+ updateTitle,
286
+ updateStatus,
287
+ setActiveTurn: activeRun.setActiveTurn,
288
+ getActiveTurn: (...args: Parameters<typeof activeRun.getActiveTurn>) =>
289
+ effectTryPromise(() => activeRun.getActiveTurn(...args), 'Failed to get active turn.'),
290
+ getActiveRunId: activeRun.getActiveRunId,
291
+ hasActiveRunLease: activeRun.hasActiveRunLease,
292
+ withActiveRunLease: activeRun.withActiveRunLease,
293
+ getActiveStreamId: activeRun.getActiveStreamId,
294
+ clearActiveTurn: activeRun.clearActiveTurn,
295
+ clearStaleActiveRunIfMissingFromRegistry: activeRun.clearStaleActiveRunIfMissingFromRegistry,
296
+ stopActiveRun: (...args: Parameters<typeof activeRun.stopActiveRun>) =>
297
+ effectTryPromise(() => activeRun.stopActiveRun(...args), 'Failed to stop active run.'),
298
+ setCompacting,
299
+ appendMemoryBlock: (...args: Parameters<typeof memory.appendMemoryBlock>) =>
300
+ effectTryPromise(() => memory.appendMemoryBlock(...args), 'Failed to append memory block.'),
301
+ compactMemoryBlock: (...args: Parameters<typeof memory.compactMemoryBlock>) =>
302
+ effectTryPromise(() => memory.compactMemoryBlock(...args), 'Failed to compact memory block.'),
303
+ clearThread,
304
+ deleteThread,
305
+ listRecentThreads: (...args: Parameters<typeof listing.listRecentThreads>) =>
306
+ effectTryPromise(() => listing.listRecentThreads(...args), 'Failed to list recent threads.'),
307
+ formatMemoryBlockForPrompt,
308
+ toPublicThread,
309
+ incrementTurnCount,
310
+ }
311
+ }
312
+
313
+ export class ThreadServiceTag extends Context.Service<ThreadServiceTag, ReturnType<typeof makeThreadService>>()(
314
+ 'ThreadService',
315
+ ) {}
316
+
317
+ export const ThreadServiceLive = Layer.effect(
318
+ ThreadServiceTag,
319
+ Effect.gen(function* () {
320
+ const db = yield* DatabaseServiceTag
321
+ const redis = yield* RedisServiceTag
322
+ const chatRunRegistry = yield* ChatRunRegistryTag
323
+ const threadMessageService = yield* ThreadMessageServiceTag
324
+ const contextCompactionService = yield* ContextCompactionServiceTag
325
+ const compactionCoordination = yield* CompactionCoordinationTag
326
+ return makeThreadService({
327
+ db,
328
+ redis,
329
+ chatRunRegistry,
330
+ threadMessageService,
331
+ contextCompactionService,
332
+ compactionCoordination,
333
+ })
334
+ }),
335
+ )