@lota-sdk/core 0.4.8 → 0.4.10

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 (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -1,6 +1,15 @@
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 { effectTryMaybeAsync as effectTryMaybeAsyncShared, makeEffectTryPromiseWithMessage } from '../effect/helpers'
2
7
  import { buildAgentPromptContext } from './agent-prompt-context'
3
- import { buildIndexedRepositoriesContext, getPluginService } from './plugin-resolution'
8
+ import {
9
+ buildIndexedRepositoriesContext,
10
+ getGithubInstallationForOrganization,
11
+ getLinearInstallationByOrgId,
12
+ } from './plugin-resolution'
4
13
  import type {
5
14
  LotaRuntimeIndexedRepositoriesContext,
6
15
  LotaRuntimeTurnHooks,
@@ -8,12 +17,32 @@ import type {
8
17
  LotaRuntimeWorkspaceProjectionState,
9
18
  LotaRuntimeWorkspaceProvider,
10
19
  } from './runtime-extensions'
11
- import { asRecord, readInstructionSections, readOptionalString } from './thread-chat-helpers'
20
+ import { readInstructionSections, readOptionalString } from './thread-chat-helpers'
12
21
 
13
22
  function readOptionalBoolean(value: unknown): boolean | undefined {
14
23
  return typeof value === 'boolean' ? value : undefined
15
24
  }
16
25
 
26
+ function isRecord(value: unknown): value is Record<string, unknown> {
27
+ return typeof value === 'object' && value !== null
28
+ }
29
+
30
+ class ThreadTurnContextError extends Schema.TaggedErrorClass<ThreadTurnContextError>()('ThreadTurnContextError', {
31
+ message: Schema.String,
32
+ cause: Schema.optional(Schema.Defect),
33
+ }) {}
34
+
35
+ const effectTryPromise = makeEffectTryPromiseWithMessage(
36
+ (message, cause) => new ThreadTurnContextError({ message, cause }),
37
+ )
38
+
39
+ function effectTryMaybeAsync<A>(
40
+ evaluate: () => A | PromiseLike<A>,
41
+ message: string,
42
+ ): Effect.Effect<A, ThreadTurnContextError> {
43
+ return effectTryMaybeAsyncShared(evaluate, (cause) => new ThreadTurnContextError({ message, cause }))
44
+ }
45
+
17
46
  interface AssembledThreadTurnContext {
18
47
  workspace: Record<string, unknown>
19
48
  workspaceLifecycleState: LotaRuntimeWorkspaceLifecycleState | undefined
@@ -28,7 +57,7 @@ interface AssembledThreadTurnContext {
28
57
  hookInstructionSections: string[]
29
58
  }
30
59
 
31
- export async function assembleThreadTurnContext(params: {
60
+ const assembleThreadTurnContextEffect = Effect.fn('ThreadTurnContext.assemble')(function* (params: {
32
61
  thread: unknown
33
62
  threadRef: RecordIdRef
34
63
  orgRef: RecordIdRef
@@ -40,131 +69,149 @@ export async function assembleThreadTurnContext(params: {
40
69
  workspacePromise: Promise<Record<string, unknown>>
41
70
  workspaceProvider?: LotaRuntimeWorkspaceProvider
42
71
  turnHooks: LotaRuntimeTurnHooks
43
- onStep?: (name: string) => void
44
- }): Promise<AssembledThreadTurnContext> {
45
- const workspace = await params.workspacePromise
46
- params.onStep?.('fetch-workspace')
72
+ }) {
73
+ const workspace = yield* effectTryPromise(() => params.workspacePromise, 'Failed to load thread workspace').pipe(
74
+ Effect.withSpan('ThreadTurnContext.loadWorkspace'),
75
+ )
47
76
 
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')
77
+ const workspaceLifecycleState = yield* effectTryMaybeAsync(
78
+ () => params.workspaceProvider?.getLifecycleState?.(workspace),
79
+ 'Failed to load workspace lifecycle state',
80
+ ).pipe(Effect.withSpan('ThreadTurnContext.loadWorkspaceLifecycleState'))
81
+ const workspaceProfileState = yield* effectTryMaybeAsync(
82
+ () => params.workspaceProvider?.readProfileProjectionState?.(workspace),
83
+ 'Failed to load workspace profile state',
84
+ ).pipe(Effect.withSpan('ThreadTurnContext.loadWorkspaceProfileState'))
55
85
 
56
86
  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)')
85
-
86
- let linearInstalled = Boolean(linearInstallation)
87
- let githubInstalled = Boolean(githubInstallation)
88
- let promptContext = buildAgentPromptContext({
89
- workspaceName:
90
- workspaceProfileState?.workspaceName ?? readOptionalString((workspace as { name?: unknown }).name) ?? undefined,
87
+ const linearInstallation = yield* effectTryPromise(
88
+ () => getLinearInstallationByOrgId(params.orgRef),
89
+ 'Failed to load Linear installation',
90
+ ).pipe(
91
+ Effect.catch(() => Effect.succeed(null)),
92
+ Effect.withSpan('ThreadTurnContext.loadLinearInstallation'),
93
+ )
94
+ const githubInstallation = yield* effectTryPromise(
95
+ () => getGithubInstallationForOrganization(params.orgIdString),
96
+ 'Failed to load GitHub installation',
97
+ ).pipe(
98
+ Effect.catch(() => Effect.succeed(null)),
99
+ Effect.withSpan('ThreadTurnContext.loadGithubInstallation'),
100
+ )
101
+ const indexedRepoContext = yield* effectTryPromise(
102
+ () => buildIndexedRepositoriesContext(params.orgIdString),
103
+ 'Failed to build indexed repository context',
104
+ ).pipe(
105
+ Effect.catch(() =>
106
+ Effect.succeed({
107
+ provideRepoTool: false,
108
+ defaultSectionsByAgent: {},
109
+ context: '',
110
+ } as LotaRuntimeIndexedRepositoriesContext),
111
+ ),
112
+ Effect.withSpan('ThreadTurnContext.buildIndexedRepositoriesContext'),
113
+ )
114
+ const recentDomainEvents = yield* effectTryMaybeAsync(
115
+ () => params.workspaceProvider?.listRecentDomainEvents?.(params.orgRef, 5) ?? [],
116
+ 'Failed to load recent domain events',
117
+ ).pipe(Effect.withSpan('ThreadTurnContext.loadRecentDomainEvents'))
118
+ const promptSummary =
119
+ params.workspaceProvider?.buildPromptSummary === undefined
120
+ ? undefined
121
+ : yield* effectTryMaybeAsync(
122
+ () => params.workspaceProvider?.buildPromptSummary?.(params.orgRef),
123
+ 'Failed to build workspace prompt summary',
124
+ ).pipe(
125
+ Effect.catch(() => Effect.as(Effect.void, undefined as string | undefined)),
126
+ Effect.withSpan('ThreadTurnContext.buildPromptSummary'),
127
+ )
128
+
129
+ const initialLinearInstalled = Boolean(linearInstallation)
130
+ const initialGithubInstalled = Boolean(githubInstallation)
131
+ const initialPromptContext = buildAgentPromptContext({
132
+ workspaceName: workspaceProfileState?.workspaceName ?? readOptionalString(workspace.name) ?? undefined,
91
133
  summaryBlock: workspaceProfileState?.summaryBlock,
92
134
  promptSummary,
93
135
  userName: params.userName ?? undefined,
94
136
  recentDomainEvents,
95
137
  })
96
- let retrievedKnowledgeSection: string | undefined = !params.messageText
138
+ const initialRetrievedKnowledgeSection: string | undefined = !params.messageText
97
139
  ? 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')
140
+ : yield* effectTryMaybeAsync(
141
+ () =>
142
+ params.workspaceProvider?.buildRetrievedKnowledgeSection?.({
143
+ workspaceId: params.orgIdString,
144
+ userId: params.userIdString,
145
+ query: params.messageText,
146
+ }),
147
+ 'Failed to build retrieved knowledge section',
148
+ ).pipe(Effect.withSpan('ThreadTurnContext.buildRetrievedKnowledgeSection'))
149
+
150
+ const buildContextResultValue = yield* effectTryMaybeAsync(
151
+ () =>
152
+ params.turnHooks.buildContext?.({
153
+ thread: params.thread,
154
+ threadRef: params.threadRef,
155
+ orgRef: params.orgRef,
156
+ userRef: params.userRef,
157
+ userName: params.userName,
158
+ workspace,
159
+ onboardingActive,
160
+ messageText: params.messageText,
161
+ linearInstalled: initialLinearInstalled,
162
+ githubInstalled: initialGithubInstalled,
163
+ indexedRepoContext,
164
+ promptContext: initialPromptContext,
165
+ workspaceLifecycleState,
166
+ workspaceProfileState,
167
+ promptSummary,
168
+ recentDomainEvents,
169
+ retrievedKnowledgeSection: initialRetrievedKnowledgeSection,
170
+ }),
171
+ 'Failed to build thread context',
172
+ ).pipe(Effect.withSpan('ThreadTurnContext.buildContextHook'))
173
+ const buildContextResult = isRecord(buildContextResultValue) ? buildContextResultValue : null
127
174
 
128
175
  const buildContextPromptDetails = readOptionalString(buildContextResult?.systemWorkspaceDetails)
129
- if (buildContextPromptDetails) {
130
- promptContext = { systemWorkspaceDetails: buildContextPromptDetails }
131
- }
176
+ const promptContext = buildContextPromptDetails
177
+ ? { systemWorkspaceDetails: buildContextPromptDetails }
178
+ : initialPromptContext
132
179
  const buildContextRetrievedKnowledge = readOptionalString(buildContextResult?.retrievedKnowledgeSection)
133
- if (buildContextRetrievedKnowledge !== undefined) {
134
- retrievedKnowledgeSection = buildContextRetrievedKnowledge
135
- }
180
+ const retrievedKnowledgeSection =
181
+ buildContextRetrievedKnowledge !== undefined ? buildContextRetrievedKnowledge : initialRetrievedKnowledgeSection
136
182
  const buildContextLinearInstalled = readOptionalBoolean(buildContextResult?.linearInstalled)
137
- if (buildContextLinearInstalled !== undefined) {
138
- linearInstalled = buildContextLinearInstalled
139
- }
183
+ const linearInstalled =
184
+ buildContextLinearInstalled !== undefined ? buildContextLinearInstalled : initialLinearInstalled
140
185
  const buildContextGithubInstalled = readOptionalBoolean(buildContextResult?.githubInstalled)
141
- if (buildContextGithubInstalled !== undefined) {
142
- githubInstalled = buildContextGithubInstalled
143
- }
186
+ const githubInstalled =
187
+ buildContextGithubInstalled !== undefined ? buildContextGithubInstalled : initialGithubInstalled
144
188
 
145
189
  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
- }),
190
+ yield* effectTryMaybeAsync(
191
+ () =>
192
+ params.turnHooks.buildExtraInstructionSections?.({
193
+ thread: params.thread,
194
+ threadRef: params.threadRef,
195
+ orgRef: params.orgRef,
196
+ userRef: params.userRef,
197
+ userName: params.userName,
198
+ workspace,
199
+ onboardingActive,
200
+ messageText: params.messageText,
201
+ linearInstalled,
202
+ githubInstalled,
203
+ indexedRepoContext,
204
+ promptContext,
205
+ workspaceLifecycleState,
206
+ workspaceProfileState,
207
+ promptSummary,
208
+ recentDomainEvents,
209
+ retrievedKnowledgeSection,
210
+ context: buildContextResult,
211
+ }),
212
+ 'Failed to build extra instruction sections',
213
+ ).pipe(Effect.withSpan('ThreadTurnContext.buildExtraInstructionSections')),
166
214
  )
167
- params.onStep?.('hook:buildExtraInstructionSections')
168
215
 
169
216
  return {
170
217
  workspace,
@@ -179,4 +226,29 @@ export async function assembleThreadTurnContext(params: {
179
226
  buildContextResult,
180
227
  hookInstructionSections,
181
228
  }
229
+ })
230
+
231
+ export function assembleThreadTurnContext(params: {
232
+ thread: unknown
233
+ threadRef: RecordIdRef
234
+ orgRef: RecordIdRef
235
+ userRef: RecordIdRef
236
+ userName?: string | null
237
+ orgIdString: string
238
+ userIdString: string
239
+ messageText: string
240
+ workspacePromise: Promise<Record<string, unknown>>
241
+ workspaceProvider?: LotaRuntimeWorkspaceProvider
242
+ turnHooks: LotaRuntimeTurnHooks
243
+ }): Promise<AssembledThreadTurnContext> {
244
+ return Effect.runPromise(
245
+ assembleThreadTurnContextEffect(params).pipe(
246
+ Effect.annotateSpans({
247
+ threadId: recordIdToString(params.threadRef, TABLES.THREAD),
248
+ orgId: params.orgIdString,
249
+ userId: params.userIdString,
250
+ messageLength: params.messageText.length,
251
+ }),
252
+ ),
253
+ )
182
254
  }
@@ -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
  }