@lota-sdk/core 0.4.8 → 0.4.9

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