@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,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
+ )