@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
@@ -1,6 +1,18 @@
1
+ import { Schema, Effect } from 'effect'
2
+
1
3
  import type { RecordIdRef } from '../db/record-id'
4
+ import { recordIdToString } from '../db/record-id'
5
+ import { TABLES } from '../db/tables'
6
+ import {
7
+ effectTryMaybeAsync as effectTryMaybeAsyncShared,
8
+ effectTryPromise as effectTryPromiseShared,
9
+ } from '../effect/helpers'
2
10
  import { buildAgentPromptContext } from './agent-prompt-context'
3
- import { buildIndexedRepositoriesContext, getPluginService } from './plugin-resolution'
11
+ import {
12
+ buildIndexedRepositoriesContext,
13
+ getGithubInstallationForOrganization,
14
+ getLinearInstallationByOrgId,
15
+ } from './plugin-resolution'
4
16
  import type {
5
17
  LotaRuntimeIndexedRepositoriesContext,
6
18
  LotaRuntimeTurnHooks,
@@ -8,12 +20,32 @@ import type {
8
20
  LotaRuntimeWorkspaceProjectionState,
9
21
  LotaRuntimeWorkspaceProvider,
10
22
  } from './runtime-extensions'
11
- import { asRecord, readInstructionSections, readOptionalString } from './thread-chat-helpers'
23
+ import { readInstructionSections, readOptionalString } from './thread-chat-helpers'
12
24
 
13
25
  function readOptionalBoolean(value: unknown): boolean | undefined {
14
26
  return typeof value === 'boolean' ? value : undefined
15
27
  }
16
28
 
29
+ function isRecord(value: unknown): value is Record<string, unknown> {
30
+ return typeof value === 'object' && value !== null
31
+ }
32
+
33
+ class ThreadTurnContextError extends Schema.TaggedErrorClass<ThreadTurnContextError>()('ThreadTurnContextError', {
34
+ message: Schema.String,
35
+ cause: Schema.optional(Schema.Defect),
36
+ }) {}
37
+
38
+ function effectTryPromise<A>(thunk: () => PromiseLike<A>, message: string): Effect.Effect<A, ThreadTurnContextError> {
39
+ return effectTryPromiseShared(thunk, (cause) => new ThreadTurnContextError({ message, cause }))
40
+ }
41
+
42
+ function effectTryMaybeAsync<A>(
43
+ evaluate: () => A | PromiseLike<A>,
44
+ message: string,
45
+ ): Effect.Effect<A, ThreadTurnContextError> {
46
+ return effectTryMaybeAsyncShared(evaluate, (cause) => new ThreadTurnContextError({ message, cause }))
47
+ }
48
+
17
49
  interface AssembledThreadTurnContext {
18
50
  workspace: Record<string, unknown>
19
51
  workspaceLifecycleState: LotaRuntimeWorkspaceLifecycleState | undefined
@@ -28,7 +60,7 @@ interface AssembledThreadTurnContext {
28
60
  hookInstructionSections: string[]
29
61
  }
30
62
 
31
- export async function assembleThreadTurnContext(params: {
63
+ const assembleThreadTurnContextEffect = Effect.fn('ThreadTurnContext.assemble')(function* (params: {
32
64
  thread: unknown
33
65
  threadRef: RecordIdRef
34
66
  orgRef: RecordIdRef
@@ -40,54 +72,67 @@ export async function assembleThreadTurnContext(params: {
40
72
  workspacePromise: Promise<Record<string, unknown>>
41
73
  workspaceProvider?: LotaRuntimeWorkspaceProvider
42
74
  turnHooks: LotaRuntimeTurnHooks
43
- onStep?: (name: string) => void
44
- }): Promise<AssembledThreadTurnContext> {
45
- const workspace = await params.workspacePromise
46
- params.onStep?.('fetch-workspace')
75
+ }) {
76
+ const workspace = yield* effectTryPromise(() => params.workspacePromise, 'Failed to load thread workspace').pipe(
77
+ Effect.withSpan('ThreadTurnContext.loadWorkspace'),
78
+ )
47
79
 
48
- const [workspaceLifecycleState, workspaceProfileState] = await Promise.all([
49
- params.workspaceProvider ? params.workspaceProvider.getLifecycleState?.(workspace) : Promise.resolve(undefined),
50
- params.workspaceProvider
51
- ? params.workspaceProvider.readProfileProjectionState?.(workspace)
52
- : Promise.resolve(undefined),
53
- ])
54
- params.onStep?.('workspace-state')
80
+ const workspaceLifecycleState = yield* effectTryMaybeAsync(
81
+ () => params.workspaceProvider?.getLifecycleState?.(workspace),
82
+ 'Failed to load workspace lifecycle state',
83
+ ).pipe(Effect.withSpan('ThreadTurnContext.loadWorkspaceLifecycleState'))
84
+ const workspaceProfileState = yield* effectTryMaybeAsync(
85
+ () => params.workspaceProvider?.readProfileProjectionState?.(workspace),
86
+ 'Failed to load workspace profile state',
87
+ ).pipe(Effect.withSpan('ThreadTurnContext.loadWorkspaceProfileState'))
55
88
 
56
89
  const onboardingActive = workspaceLifecycleState?.bootstrapActive ?? false
57
- const getLinearInstallationByOrgId = getPluginService([
58
- 'linear',
59
- 'services',
60
- 'linearService',
61
- 'getInstallationByOrgId',
62
- ])
63
- const getGithubInstallationForOrganization = getPluginService([
64
- 'github',
65
- 'services',
66
- 'githubService',
67
- 'getInstallationForOrganization',
68
- ])
69
- const [linearInstallation, githubInstallation, indexedRepoContext, recentDomainEvents, promptSummary] =
70
- await Promise.all([
71
- getLinearInstallationByOrgId
72
- ? (getLinearInstallationByOrgId(params.orgRef) as Promise<unknown>)
73
- : Promise.resolve(null),
74
- getGithubInstallationForOrganization
75
- ? (getGithubInstallationForOrganization(params.orgIdString) as Promise<unknown>)
76
- : Promise.resolve(null),
77
- buildIndexedRepositoriesContext(params.orgIdString),
78
- params.workspaceProvider?.listRecentDomainEvents?.(params.orgRef, 5) ??
79
- Promise.resolve([] as Array<Record<string, unknown>>),
80
- params.workspaceProvider?.buildPromptSummary
81
- ? params.workspaceProvider.buildPromptSummary(params.orgRef).catch(() => undefined)
82
- : Promise.resolve(undefined),
83
- ])
84
- params.onStep?.('parallel-context-fetch(plugins+repos+events+summary)')
90
+ const linearInstallation = yield* effectTryPromise(
91
+ () => getLinearInstallationByOrgId(params.orgRef),
92
+ 'Failed to load Linear installation',
93
+ ).pipe(
94
+ Effect.catch(() => Effect.succeed(null)),
95
+ Effect.withSpan('ThreadTurnContext.loadLinearInstallation'),
96
+ )
97
+ const githubInstallation = yield* effectTryPromise(
98
+ () => getGithubInstallationForOrganization(params.orgIdString),
99
+ 'Failed to load GitHub installation',
100
+ ).pipe(
101
+ Effect.catch(() => Effect.succeed(null)),
102
+ Effect.withSpan('ThreadTurnContext.loadGithubInstallation'),
103
+ )
104
+ const indexedRepoContext = yield* effectTryPromise(
105
+ () => buildIndexedRepositoriesContext(params.orgIdString),
106
+ 'Failed to build indexed repository context',
107
+ ).pipe(
108
+ Effect.catch(() =>
109
+ Effect.succeed({
110
+ provideRepoTool: false,
111
+ defaultSectionsByAgent: {},
112
+ context: '',
113
+ } as LotaRuntimeIndexedRepositoriesContext),
114
+ ),
115
+ Effect.withSpan('ThreadTurnContext.buildIndexedRepositoriesContext'),
116
+ )
117
+ const recentDomainEvents = yield* effectTryMaybeAsync(
118
+ () => params.workspaceProvider?.listRecentDomainEvents?.(params.orgRef, 5) ?? [],
119
+ 'Failed to load recent domain events',
120
+ ).pipe(Effect.withSpan('ThreadTurnContext.loadRecentDomainEvents'))
121
+ const promptSummary =
122
+ params.workspaceProvider?.buildPromptSummary === undefined
123
+ ? undefined
124
+ : yield* effectTryMaybeAsync(
125
+ () => params.workspaceProvider?.buildPromptSummary?.(params.orgRef),
126
+ 'Failed to build workspace prompt summary',
127
+ ).pipe(
128
+ Effect.catch(() => Effect.as(Effect.void, undefined as string | undefined)),
129
+ Effect.withSpan('ThreadTurnContext.buildPromptSummary'),
130
+ )
85
131
 
86
132
  let linearInstalled = Boolean(linearInstallation)
87
133
  let githubInstalled = Boolean(githubInstallation)
88
134
  let promptContext = buildAgentPromptContext({
89
- workspaceName:
90
- workspaceProfileState?.workspaceName ?? readOptionalString((workspace as { name?: unknown }).name) ?? undefined,
135
+ workspaceName: workspaceProfileState?.workspaceName ?? readOptionalString(workspace.name) ?? undefined,
91
136
  summaryBlock: workspaceProfileState?.summaryBlock,
92
137
  promptSummary,
93
138
  userName: params.userName ?? undefined,
@@ -95,35 +140,40 @@ export async function assembleThreadTurnContext(params: {
95
140
  })
96
141
  let retrievedKnowledgeSection: string | undefined = !params.messageText
97
142
  ? undefined
98
- : await params.workspaceProvider?.buildRetrievedKnowledgeSection?.({
99
- workspaceId: params.orgIdString,
100
- userId: params.userIdString,
101
- query: params.messageText,
102
- })
103
- params.onStep?.('rag-knowledge-retrieval')
104
-
105
- const buildContextResult = asRecord(
106
- await params.turnHooks.buildContext?.({
107
- thread: params.thread,
108
- threadRef: params.threadRef,
109
- orgRef: params.orgRef,
110
- userRef: params.userRef,
111
- userName: params.userName,
112
- workspace,
113
- onboardingActive,
114
- messageText: params.messageText,
115
- linearInstalled,
116
- githubInstalled,
117
- indexedRepoContext,
118
- promptContext,
119
- workspaceLifecycleState,
120
- workspaceProfileState,
121
- promptSummary,
122
- recentDomainEvents,
123
- retrievedKnowledgeSection,
124
- }),
125
- )
126
- params.onStep?.('hook:buildContext')
143
+ : yield* effectTryMaybeAsync(
144
+ () =>
145
+ params.workspaceProvider?.buildRetrievedKnowledgeSection?.({
146
+ workspaceId: params.orgIdString,
147
+ userId: params.userIdString,
148
+ query: params.messageText,
149
+ }),
150
+ 'Failed to build retrieved knowledge section',
151
+ ).pipe(Effect.withSpan('ThreadTurnContext.buildRetrievedKnowledgeSection'))
152
+
153
+ const buildContextResultValue = yield* effectTryMaybeAsync(
154
+ () =>
155
+ params.turnHooks.buildContext?.({
156
+ thread: params.thread,
157
+ threadRef: params.threadRef,
158
+ orgRef: params.orgRef,
159
+ userRef: params.userRef,
160
+ userName: params.userName,
161
+ workspace,
162
+ onboardingActive,
163
+ messageText: params.messageText,
164
+ linearInstalled,
165
+ githubInstalled,
166
+ indexedRepoContext,
167
+ promptContext,
168
+ workspaceLifecycleState,
169
+ workspaceProfileState,
170
+ promptSummary,
171
+ recentDomainEvents,
172
+ retrievedKnowledgeSection,
173
+ }),
174
+ 'Failed to build thread context',
175
+ ).pipe(Effect.withSpan('ThreadTurnContext.buildContextHook'))
176
+ const buildContextResult = isRecord(buildContextResultValue) ? buildContextResultValue : null
127
177
 
128
178
  const buildContextPromptDetails = readOptionalString(buildContextResult?.systemWorkspaceDetails)
129
179
  if (buildContextPromptDetails) {
@@ -143,28 +193,31 @@ export async function assembleThreadTurnContext(params: {
143
193
  }
144
194
 
145
195
  const hookInstructionSections = readInstructionSections(
146
- await params.turnHooks.buildExtraInstructionSections?.({
147
- thread: params.thread,
148
- threadRef: params.threadRef,
149
- orgRef: params.orgRef,
150
- userRef: params.userRef,
151
- userName: params.userName,
152
- workspace,
153
- onboardingActive,
154
- messageText: params.messageText,
155
- linearInstalled,
156
- githubInstalled,
157
- indexedRepoContext,
158
- promptContext,
159
- workspaceLifecycleState,
160
- workspaceProfileState,
161
- promptSummary,
162
- recentDomainEvents,
163
- retrievedKnowledgeSection,
164
- context: buildContextResult,
165
- }),
196
+ yield* effectTryMaybeAsync(
197
+ () =>
198
+ params.turnHooks.buildExtraInstructionSections?.({
199
+ thread: params.thread,
200
+ threadRef: params.threadRef,
201
+ orgRef: params.orgRef,
202
+ userRef: params.userRef,
203
+ userName: params.userName,
204
+ workspace,
205
+ onboardingActive,
206
+ messageText: params.messageText,
207
+ linearInstalled,
208
+ githubInstalled,
209
+ indexedRepoContext,
210
+ promptContext,
211
+ workspaceLifecycleState,
212
+ workspaceProfileState,
213
+ promptSummary,
214
+ recentDomainEvents,
215
+ retrievedKnowledgeSection,
216
+ context: buildContextResult,
217
+ }),
218
+ 'Failed to build extra instruction sections',
219
+ ).pipe(Effect.withSpan('ThreadTurnContext.buildExtraInstructionSections')),
166
220
  )
167
- params.onStep?.('hook:buildExtraInstructionSections')
168
221
 
169
222
  return {
170
223
  workspace,
@@ -179,4 +232,29 @@ export async function assembleThreadTurnContext(params: {
179
232
  buildContextResult,
180
233
  hookInstructionSections,
181
234
  }
235
+ })
236
+
237
+ export function assembleThreadTurnContext(params: {
238
+ thread: unknown
239
+ threadRef: RecordIdRef
240
+ orgRef: RecordIdRef
241
+ userRef: RecordIdRef
242
+ userName?: string | null
243
+ orgIdString: string
244
+ userIdString: string
245
+ messageText: string
246
+ workspacePromise: Promise<Record<string, unknown>>
247
+ workspaceProvider?: LotaRuntimeWorkspaceProvider
248
+ turnHooks: LotaRuntimeTurnHooks
249
+ }): Promise<AssembledThreadTurnContext> {
250
+ return Effect.runPromise(
251
+ assembleThreadTurnContextEffect(params).pipe(
252
+ Effect.annotateSpans({
253
+ threadId: recordIdToString(params.threadRef, TABLES.THREAD),
254
+ orgId: params.orgIdString,
255
+ userId: params.userIdString,
256
+ messageLength: params.messageText.length,
257
+ }),
258
+ ),
259
+ )
182
260
  }
@@ -1,36 +1,102 @@
1
1
  import type { ChatMessage } from '@lota-sdk/shared'
2
+ import { Schema, Effect } from 'effect'
2
3
 
3
- export async function finalizeTurnRun(params: {
4
+ import { serverLogger } from '../config/logger'
5
+ import { effectTryMaybeAsync } from '../effect/helpers'
6
+
7
+ export class TurnLifecycleError extends Schema.TaggedErrorClass<TurnLifecycleError>()('TurnLifecycleError', {
8
+ message: Schema.String,
9
+ cause: Schema.optional(Schema.Defect),
10
+ }) {}
11
+
12
+ type MaybeAsync<A> = A | PromiseLike<A>
13
+ type MaybeEffect<A> = MaybeAsync<A> | Effect.Effect<A, TurnLifecycleError>
14
+
15
+ function effectFromMaybeEffect<A>(
16
+ evaluate: () => MaybeEffect<A>,
17
+ onError: (error: unknown) => TurnLifecycleError,
18
+ ): Effect.Effect<A, TurnLifecycleError> {
19
+ return Effect.suspend(() => {
20
+ try {
21
+ const result = evaluate()
22
+ return Effect.isEffect(result)
23
+ ? result.pipe(Effect.mapError(onError))
24
+ : effectTryMaybeAsync(() => result, onError)
25
+ } catch (error) {
26
+ return Effect.fail(onError(error))
27
+ }
28
+ })
29
+ }
30
+
31
+ export function finalizeTurnRunEffect(params: {
4
32
  serverRunId: string
5
- getEntity: () => Promise<{ lastCompactedMessageId?: string | null; compactionSummary?: string | null }>
6
- getUncompactedMessages: (cursor?: string) => Promise<ChatMessage[]>
33
+ getEntity: () => MaybeEffect<{ lastCompactedMessageId?: string | null; compactionSummary?: string | null }>
34
+ getUncompactedMessages: (cursor?: string) => MaybeEffect<ChatMessage[]>
7
35
  assessCompaction: (summaryText: string, messages: ChatMessage[]) => { shouldCompact: boolean }
8
- enqueueCompaction: () => Promise<void>
36
+ enqueueCompaction: () => MaybeEffect<void>
9
37
  unregisterRun: (runId: string) => void
10
- clearActiveRunId: (runId: string) => Promise<void>
38
+ clearActiveRunId: (runId: string) => MaybeEffect<void>
11
39
  disposeAbort: () => void
12
40
  activeStreamId?: string
13
- clearActiveStreamId?: (streamId: string) => Promise<void>
14
- }): Promise<void> {
15
- // Clear the active run immediately so new messages are not blocked
16
- params.unregisterRun(params.serverRunId)
17
- await params.clearActiveRunId(params.serverRunId)
18
- if (params.activeStreamId && params.clearActiveStreamId) {
19
- await params.clearActiveStreamId(params.activeStreamId)
20
- }
21
- params.disposeAbort()
22
-
23
- try {
24
- const entity = await params.getEntity()
25
- const cursor = typeof entity.lastCompactedMessageId === 'string' ? entity.lastCompactedMessageId : undefined
26
- const uncompactedMessages = await params.getUncompactedMessages(cursor)
27
- const summaryText = typeof entity.compactionSummary === 'string' ? entity.compactionSummary : ''
28
- const { shouldCompact } = params.assessCompaction(summaryText, uncompactedMessages)
29
-
30
- if (shouldCompact) {
31
- await params.enqueueCompaction()
41
+ clearActiveStreamId?: (streamId: string) => MaybeEffect<void>
42
+ }): Effect.Effect<void, TurnLifecycleError> {
43
+ return Effect.gen(function* () {
44
+ // Clear the active run immediately so new messages are not blocked
45
+ params.unregisterRun(params.serverRunId)
46
+ yield* effectFromMaybeEffect(
47
+ () => params.clearActiveRunId(params.serverRunId),
48
+ (error) => new TurnLifecycleError({ message: String(error), cause: error }),
49
+ )
50
+ const activeStreamId = params.activeStreamId
51
+ const clearActiveStreamId = params.clearActiveStreamId
52
+ if (activeStreamId && clearActiveStreamId) {
53
+ yield* effectFromMaybeEffect(
54
+ () => clearActiveStreamId(activeStreamId),
55
+ (error) => new TurnLifecycleError({ message: String(error), cause: error }),
56
+ )
32
57
  }
33
- } catch {
34
- // compaction assessment errors should not surface to callers
35
- }
58
+ params.disposeAbort()
59
+
60
+ yield* Effect.gen(function* () {
61
+ const entity = yield* effectFromMaybeEffect(
62
+ () => params.getEntity(),
63
+ (error) => new TurnLifecycleError({ message: String(error), cause: error }),
64
+ )
65
+ const cursor = typeof entity.lastCompactedMessageId === 'string' ? entity.lastCompactedMessageId : undefined
66
+ const uncompactedMessages = yield* effectFromMaybeEffect(
67
+ () => params.getUncompactedMessages(cursor),
68
+ (error) => new TurnLifecycleError({ message: String(error), cause: error }),
69
+ )
70
+ const summaryText = typeof entity.compactionSummary === 'string' ? entity.compactionSummary : ''
71
+ const { shouldCompact } = params.assessCompaction(summaryText, uncompactedMessages)
72
+
73
+ if (shouldCompact) {
74
+ yield* effectFromMaybeEffect(
75
+ () => params.enqueueCompaction(),
76
+ (error) => new TurnLifecycleError({ message: String(error), cause: error }),
77
+ )
78
+ }
79
+ }).pipe(
80
+ Effect.catch((error: unknown) =>
81
+ Effect.sync(() => {
82
+ serverLogger.error`Failed to assess/enqueue compaction: ${error}`
83
+ }),
84
+ ),
85
+ )
86
+ })
87
+ }
88
+
89
+ export function finalizeTurnRun(params: {
90
+ serverRunId: string
91
+ getEntity: () => MaybeEffect<{ lastCompactedMessageId?: string | null; compactionSummary?: string | null }>
92
+ getUncompactedMessages: (cursor?: string) => MaybeEffect<ChatMessage[]>
93
+ assessCompaction: (summaryText: string, messages: ChatMessage[]) => { shouldCompact: boolean }
94
+ enqueueCompaction: () => MaybeEffect<void>
95
+ unregisterRun: (runId: string) => void
96
+ clearActiveRunId: (runId: string) => MaybeEffect<void>
97
+ disposeAbort: () => void
98
+ activeStreamId?: string
99
+ clearActiveStreamId?: (streamId: string) => MaybeEffect<void>
100
+ }): Promise<void> {
101
+ return Effect.runPromise(finalizeTurnRunEffect(params))
36
102
  }