@lota-sdk/core 0.4.10 → 0.4.12

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 (110) hide show
  1. package/package.json +3 -3
  2. package/src/ai-gateway/ai-gateway.ts +214 -98
  3. package/src/ai-gateway/index.ts +16 -1
  4. package/src/config/agent-defaults.ts +4 -120
  5. package/src/config/logger.ts +18 -34
  6. package/src/config/model-constants.ts +1 -0
  7. package/src/config/thread-defaults.ts +1 -18
  8. package/src/create-runtime.ts +90 -28
  9. package/src/db/base.service.ts +30 -38
  10. package/src/db/service.ts +489 -545
  11. package/src/effect/index.ts +0 -2
  12. package/src/effect/layers.ts +6 -13
  13. package/src/embeddings/provider.ts +2 -7
  14. package/src/index.ts +4 -5
  15. package/src/queues/autonomous-job.queue.ts +159 -113
  16. package/src/queues/context-compaction.queue.ts +39 -25
  17. package/src/queues/delayed-node-promotion.queue.ts +56 -29
  18. package/src/queues/document-processor.queue.ts +5 -3
  19. package/src/queues/index.ts +1 -0
  20. package/src/queues/memory-consolidation.queue.ts +79 -53
  21. package/src/queues/organization-learning.queue.ts +63 -39
  22. package/src/queues/plan-agent-heartbeat.queue.ts +104 -79
  23. package/src/queues/plan-scheduler.queue.ts +100 -84
  24. package/src/queues/post-chat-memory.queue.ts +55 -33
  25. package/src/queues/queue-factory.ts +40 -41
  26. package/src/queues/queues.service.ts +61 -0
  27. package/src/queues/title-generation.queue.ts +42 -31
  28. package/src/redis/org-memory-lock.ts +24 -9
  29. package/src/redis/redis-lease-lock.ts +8 -1
  30. package/src/runtime/agent-identity-overrides.ts +7 -3
  31. package/src/runtime/agent-runtime-policy.ts +9 -4
  32. package/src/runtime/agent-stream-helpers.ts +9 -4
  33. package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
  34. package/src/runtime/context-compaction/context-compaction.ts +9 -7
  35. package/src/runtime/domain-layer.ts +15 -4
  36. package/src/runtime/execution-plan-visibility.ts +5 -2
  37. package/src/runtime/graph-designer.ts +0 -22
  38. package/src/runtime/index.ts +2 -0
  39. package/src/runtime/indexed-repositories-policy.ts +2 -6
  40. package/src/runtime/live-turn-trace.ts +344 -0
  41. package/src/runtime/plugin-resolution.ts +29 -12
  42. package/src/runtime/post-turn-side-effects.ts +139 -141
  43. package/src/runtime/runtime-config.ts +0 -6
  44. package/src/runtime/runtime-extensions.ts +0 -54
  45. package/src/runtime/runtime-lifecycle.ts +4 -4
  46. package/src/runtime/runtime-services.ts +125 -53
  47. package/src/runtime/runtime-worker-registry.ts +113 -30
  48. package/src/runtime/social-chat/social-chat-agent-runner.ts +6 -3
  49. package/src/runtime/social-chat/social-chat-history.ts +3 -1
  50. package/src/runtime/social-chat/social-chat.ts +35 -20
  51. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +6 -5
  52. package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
  53. package/src/runtime/thread-chat-helpers.ts +18 -9
  54. package/src/runtime/thread-turn-context.ts +7 -47
  55. package/src/runtime/turn-lifecycle.ts +6 -14
  56. package/src/services/agent-activity.service.ts +168 -175
  57. package/src/services/agent-executor.service.ts +35 -16
  58. package/src/services/attachment.service.ts +4 -70
  59. package/src/services/autonomous-job.service.ts +53 -61
  60. package/src/services/context-compaction.service.ts +7 -9
  61. package/src/services/execution-plan/execution-plan-graph.ts +106 -115
  62. package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
  63. package/src/services/execution-plan/execution-plan.service.ts +67 -50
  64. package/src/services/global-orchestrator.service.ts +18 -7
  65. package/src/services/graph-full-routing.ts +7 -6
  66. package/src/services/memory/memory-conversation.ts +10 -5
  67. package/src/services/memory/memory.service.ts +11 -8
  68. package/src/services/ownership-dispatcher.service.ts +16 -5
  69. package/src/services/plan/plan-agent-heartbeat.service.ts +29 -15
  70. package/src/services/plan/plan-agent-query.service.ts +12 -8
  71. package/src/services/plan/plan-completion-side-effects.ts +93 -101
  72. package/src/services/plan/plan-cycle.service.ts +7 -45
  73. package/src/services/plan/plan-deadline.service.ts +28 -17
  74. package/src/services/plan/plan-event-delivery.service.ts +47 -40
  75. package/src/services/plan/plan-executor-context.ts +2 -0
  76. package/src/services/plan/plan-executor-graph.ts +366 -391
  77. package/src/services/plan/plan-executor.service.ts +13 -91
  78. package/src/services/plan/plan-scheduler.service.ts +62 -49
  79. package/src/services/plan/plan-transaction-events.ts +1 -1
  80. package/src/services/recent-activity-title.service.ts +6 -2
  81. package/src/services/thread/thread-bootstrap.ts +11 -9
  82. package/src/services/thread/thread-message.service.ts +6 -5
  83. package/src/services/thread/thread-turn-execution.ts +86 -82
  84. package/src/services/thread/thread-turn-preparation.service.ts +92 -45
  85. package/src/services/thread/thread-turn-streaming.ts +60 -28
  86. package/src/services/thread/thread-turn.ts +212 -46
  87. package/src/services/thread/thread.service.ts +21 -6
  88. package/src/system-agents/recent-activity-title-refiner.agent.ts +8 -5
  89. package/src/system-agents/thread-router.agent.ts +23 -20
  90. package/src/tools/execution-plan.tool.ts +8 -3
  91. package/src/tools/fetch-webpage.tool.ts +10 -9
  92. package/src/tools/firecrawl-client.ts +0 -15
  93. package/src/tools/remember-memory.tool.ts +3 -6
  94. package/src/tools/research-topic.tool.ts +12 -3
  95. package/src/tools/search-web.tool.ts +10 -9
  96. package/src/tools/search.tool.ts +4 -5
  97. package/src/tools/team-think.tool.ts +139 -121
  98. package/src/workers/bootstrap.ts +9 -10
  99. package/src/workers/memory-consolidation.worker.ts +4 -1
  100. package/src/workers/organization-learning.worker.ts +15 -2
  101. package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
  102. package/src/workers/regular-chat-memory-digest.runner.ts +21 -14
  103. package/src/workers/skill-extraction.runner.ts +13 -15
  104. package/src/workers/worker-utils.ts +6 -18
  105. package/src/effect/awaitable-effect.ts +0 -96
  106. package/src/effect/runtime-ref.ts +0 -25
  107. package/src/effect/runtime.ts +0 -46
  108. package/src/redis/runtime-connection.ts +0 -20
  109. package/src/runtime/runtime-accessors.ts +0 -92
  110. package/src/runtime/runtime-token.ts +0 -47
@@ -0,0 +1,344 @@
1
+ import type { AgentActivityData, ThinkingStepData } from '@lota-sdk/shared'
2
+ import type { UIMessage, UIMessageStreamWriter } from 'ai'
3
+
4
+ type StreamChunk<TMessage extends UIMessage> = Parameters<UIMessageStreamWriter<TMessage>['write']>[0]
5
+
6
+ interface ReasoningBlockState {
7
+ pendingChunk: string
8
+ }
9
+
10
+ function asRecord(value: unknown): Record<string, unknown> | null {
11
+ return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : null
12
+ }
13
+
14
+ function readString(value: unknown): string | null {
15
+ return typeof value === 'string' && value.length > 0 ? value : null
16
+ }
17
+
18
+ function normalizeWhitespace(value: string): string {
19
+ return value.replace(/\s+/g, ' ').trim()
20
+ }
21
+
22
+ function clipThinkingTitle(value: string): string {
23
+ if (value.length <= 120) return value
24
+ return `${value.slice(0, 117).trimEnd()}...`
25
+ }
26
+
27
+ function normalizeThinkingTitle(value: string): string {
28
+ return clipThinkingTitle(normalizeWhitespace(value))
29
+ }
30
+
31
+ function sanitizeReasoningText(value: string): string {
32
+ return value.replace(/\[REDACTED\]/gi, '').replace(/\r\n/g, '\n')
33
+ }
34
+
35
+ function stripMarkdownTitleDecorators(line: string): string {
36
+ let value = line.trim()
37
+ value = value.replace(/^#{1,6}\s+/, '')
38
+ value = value.replace(/^\*\*(.+?)\*\*[:.]?$/, '$1')
39
+ value = value.replace(/^__(.+?)__[:.]?$/, '$1')
40
+ value = value.replace(/^`(.+?)`[:.]?$/, '$1')
41
+ value = value.replace(/[:\s]+$/, '')
42
+ return value.trim()
43
+ }
44
+
45
+ function readHeadingTitle(line: string): string | null {
46
+ const trimmed = line.trim()
47
+ if (/^#{1,6}\s+/.test(trimmed)) {
48
+ return stripMarkdownTitleDecorators(trimmed)
49
+ }
50
+ if (/^\*\*.+\*\*[:.]?$/.test(trimmed) || /^__.+__[:.]?$/.test(trimmed) || /^`.+`[:.]?$/.test(trimmed)) {
51
+ return stripMarkdownTitleDecorators(trimmed)
52
+ }
53
+ if (trimmed.length <= 90 && /^[A-Z0-9].*:\s*$/.test(trimmed)) {
54
+ return stripMarkdownTitleDecorators(trimmed)
55
+ }
56
+ return null
57
+ }
58
+
59
+ function readLeadLineTitle(line: string): string | null {
60
+ const trimmed = stripMarkdownTitleDecorators(line)
61
+ if (!trimmed) return null
62
+ if (trimmed.length > 90) return null
63
+ if (/[.!?]$/.test(trimmed)) return null
64
+ return trimmed
65
+ }
66
+
67
+ function readFirstCompleteSentence(chunk: string): string | null {
68
+ const compact = normalizeWhitespace(chunk)
69
+ if (!compact) return null
70
+ const sentenceMatch = compact.match(/^(.+?[.!?])(?:\s|$)/)
71
+ if (sentenceMatch?.[1]) {
72
+ return sentenceMatch[1]
73
+ }
74
+ return null
75
+ }
76
+
77
+ function isStableReasoningChunk(chunk: string, isLastChunk: boolean, isFinal: boolean): boolean {
78
+ if (!isLastChunk || isFinal) return true
79
+ if (chunk.includes('\n')) return true
80
+ return /[.!?:]\s*$/.test(chunk.trim())
81
+ }
82
+
83
+ export function extractThinkingTitlesFromReasoning(params: { text: string; isFinal?: boolean }): string[] {
84
+ const cleaned = sanitizeReasoningText(params.text).trim()
85
+ if (cleaned.length === 0) return []
86
+
87
+ const chunks = cleaned
88
+ .split(/\n{2,}/)
89
+ .map((chunk) => chunk.trim())
90
+ .filter((chunk) => chunk.length > 0)
91
+
92
+ const titles: string[] = []
93
+
94
+ for (const [index, chunk] of chunks.entries()) {
95
+ const isLastChunk = index === chunks.length - 1
96
+ if (!isStableReasoningChunk(chunk, isLastChunk, params.isFinal === true)) {
97
+ continue
98
+ }
99
+
100
+ const lines = chunk
101
+ .split('\n')
102
+ .map((line) => line.trim())
103
+ .filter((line) => line.length > 0)
104
+ if (lines.length === 0) continue
105
+
106
+ const headingTitle = readHeadingTitle(lines[0])
107
+ if (headingTitle) {
108
+ titles.push(normalizeThinkingTitle(headingTitle))
109
+ continue
110
+ }
111
+
112
+ if (lines.length > 1 || params.isFinal === true) {
113
+ const leadLineTitle = readLeadLineTitle(lines[0])
114
+ if (leadLineTitle) {
115
+ titles.push(normalizeThinkingTitle(leadLineTitle))
116
+ continue
117
+ }
118
+ }
119
+
120
+ const sentenceTitle = readFirstCompleteSentence(chunk)
121
+ if (sentenceTitle) {
122
+ titles.push(normalizeThinkingTitle(sentenceTitle))
123
+ }
124
+ }
125
+
126
+ return titles.filter((title) => title.length > 0)
127
+ }
128
+
129
+ function splitReasoningChunks(text: string, isFinal: boolean): { completedChunks: string[]; pendingChunk: string } {
130
+ const parts = text.split(/\n{2,}/)
131
+ if (isFinal) {
132
+ return { completedChunks: parts.map((part) => part.trim()).filter((part) => part.length > 0), pendingChunk: '' }
133
+ }
134
+
135
+ const completedChunks = parts
136
+ .slice(0, -1)
137
+ .map((part) => part.trim())
138
+ .filter((part) => part.length > 0)
139
+ const pendingChunk = parts.at(-1)?.trimStart() ?? ''
140
+ return { completedChunks, pendingChunk }
141
+ }
142
+
143
+ function readChunkType<TMessage extends UIMessage>(chunk: StreamChunk<TMessage>): string | null {
144
+ return readString(asRecord(chunk)?.type)
145
+ }
146
+
147
+ function readChunkToolCallId<TMessage extends UIMessage>(chunk: StreamChunk<TMessage>): string | null {
148
+ const record = asRecord(chunk)
149
+ return readString(record?.toolCallId) ?? readString(record?.id)
150
+ }
151
+
152
+ function readChunkToolName<TMessage extends UIMessage>(chunk: StreamChunk<TMessage>): string | null {
153
+ return readString(asRecord(chunk)?.toolName)
154
+ }
155
+
156
+ function readChunkReasoningId<TMessage extends UIMessage>(chunk: StreamChunk<TMessage>): string | null {
157
+ return readString(asRecord(chunk)?.id)
158
+ }
159
+
160
+ function readChunkReasoningDelta<TMessage extends UIMessage>(chunk: StreamChunk<TMessage>): string | null {
161
+ return readString(asRecord(chunk)?.delta)
162
+ }
163
+
164
+ function readChunkErrorText<TMessage extends UIMessage>(chunk: StreamChunk<TMessage>): string | null {
165
+ const record = asRecord(chunk)
166
+ return readString(record?.errorText) ?? readString(record?.error)
167
+ }
168
+
169
+ export function createLiveTurnTraceStreamObserver<TMessage extends UIMessage>(params: {
170
+ traceId: string
171
+ writer: UIMessageStreamWriter<TMessage>
172
+ agentId?: string
173
+ agentName?: string
174
+ }) {
175
+ const toolNamesByCallId = new Map<string, string>()
176
+ const startedToolIds = new Set<string>()
177
+ const completedToolIds = new Set<string>()
178
+ const reasoningBlocks = new Map<string, ReasoningBlockState>()
179
+ const emittedThinkingTitles = new Set<string>()
180
+ let activeThinkingStep: ThinkingStepData | null = null
181
+ let nextThinkingStepIndex = 0
182
+
183
+ const writeActivity = (data: AgentActivityData) => {
184
+ const chunk = {
185
+ type: 'data-agent-activity',
186
+ id: `agent-activity:${data.activityId}`,
187
+ data,
188
+ transient: true,
189
+ } as unknown as StreamChunk<TMessage>
190
+ params.writer.write(chunk)
191
+ }
192
+
193
+ const writeThinkingStep = (data: ThinkingStepData) => {
194
+ const chunk = {
195
+ type: 'data-thinking-step',
196
+ id: `thinking-step:${data.stepId}`,
197
+ data,
198
+ transient: true,
199
+ } as unknown as StreamChunk<TMessage>
200
+ params.writer.write(chunk)
201
+ }
202
+
203
+ const markThinkingStepDone = () => {
204
+ if (!activeThinkingStep) return
205
+ if (activeThinkingStep.status === 'done') return
206
+ activeThinkingStep = { ...activeThinkingStep, status: 'done' }
207
+ writeThinkingStep(activeThinkingStep)
208
+ }
209
+
210
+ const emitThinkingTitles = (titles: string[]) => {
211
+ for (const title of titles) {
212
+ const normalizedTitle = title.toLocaleLowerCase()
213
+ if (emittedThinkingTitles.has(normalizedTitle)) continue
214
+
215
+ markThinkingStepDone()
216
+
217
+ const nextStep: ThinkingStepData = {
218
+ traceId: params.traceId,
219
+ stepId: `${params.traceId}:thinking:${nextThinkingStepIndex}`,
220
+ index: nextThinkingStepIndex,
221
+ title,
222
+ status: 'streaming',
223
+ }
224
+ nextThinkingStepIndex += 1
225
+ emittedThinkingTitles.add(normalizedTitle)
226
+ activeThinkingStep = nextStep
227
+ writeThinkingStep(nextStep)
228
+ }
229
+ }
230
+
231
+ const emitToolPhase = (
232
+ phase: AgentActivityData['phase'],
233
+ toolCallId: string,
234
+ toolName: string,
235
+ errorText?: string,
236
+ ) => {
237
+ writeActivity({
238
+ traceId: params.traceId,
239
+ activityId: toolCallId,
240
+ kind: 'tool',
241
+ toolName,
242
+ toolCallId,
243
+ ...(params.agentId ? { agentId: params.agentId } : {}),
244
+ ...(params.agentName ? { agentName: params.agentName } : {}),
245
+ phase,
246
+ ...(errorText ? { errorText } : {}),
247
+ })
248
+ }
249
+
250
+ const startTool = (toolCallId: string, toolName: string) => {
251
+ if (!toolNamesByCallId.has(toolCallId)) {
252
+ toolNamesByCallId.set(toolCallId, toolName)
253
+ }
254
+ if (startedToolIds.has(toolCallId) || completedToolIds.has(toolCallId)) return
255
+ startedToolIds.add(toolCallId)
256
+ emitToolPhase('started', toolCallId, toolName)
257
+ }
258
+
259
+ const completeTool = (toolCallId: string, phase: 'completed' | 'failed', errorText?: string) => {
260
+ const toolName = toolNamesByCallId.get(toolCallId)
261
+ if (!toolName || completedToolIds.has(toolCallId)) return
262
+ completedToolIds.add(toolCallId)
263
+ emitToolPhase(phase, toolCallId, toolName, errorText)
264
+ }
265
+
266
+ const processReasoningText = (reasoningId: string, delta: string, isFinal: boolean) => {
267
+ const state = reasoningBlocks.get(reasoningId) ?? { pendingChunk: '' }
268
+ const nextBuffer = state.pendingChunk + sanitizeReasoningText(delta)
269
+ const { completedChunks, pendingChunk } = splitReasoningChunks(nextBuffer, isFinal)
270
+
271
+ for (const completedChunk of completedChunks) {
272
+ emitThinkingTitles(extractThinkingTitlesFromReasoning({ text: completedChunk, isFinal: true }))
273
+ }
274
+
275
+ if (!isFinal && pendingChunk.length > 0) {
276
+ emitThinkingTitles(extractThinkingTitlesFromReasoning({ text: pendingChunk, isFinal: false }))
277
+ }
278
+
279
+ if (isFinal) {
280
+ reasoningBlocks.delete(reasoningId)
281
+ return
282
+ }
283
+
284
+ reasoningBlocks.set(reasoningId, { pendingChunk })
285
+ }
286
+
287
+ return {
288
+ observeChunk(chunk: StreamChunk<TMessage>): void {
289
+ const chunkType = readChunkType(chunk)
290
+ if (!chunkType) return
291
+
292
+ switch (chunkType) {
293
+ case 'reasoning-delta': {
294
+ const reasoningId = readChunkReasoningId(chunk)
295
+ const delta = readChunkReasoningDelta(chunk)
296
+ if (!reasoningId || delta === null) return
297
+
298
+ processReasoningText(reasoningId, delta, false)
299
+ return
300
+ }
301
+
302
+ case 'reasoning-end': {
303
+ const reasoningId = readChunkReasoningId(chunk)
304
+ if (!reasoningId) return
305
+ processReasoningText(reasoningId, '', true)
306
+ markThinkingStepDone()
307
+ return
308
+ }
309
+
310
+ case 'tool-input-start':
311
+ case 'tool-input-available':
312
+ case 'tool-call': {
313
+ const toolCallId = readChunkToolCallId(chunk)
314
+ const toolName = readChunkToolName(chunk)
315
+ if (!toolCallId || !toolName) return
316
+ startTool(toolCallId, toolName)
317
+ return
318
+ }
319
+
320
+ case 'tool-output-available': {
321
+ const toolCallId = readChunkToolCallId(chunk)
322
+ if (!toolCallId) return
323
+ completeTool(toolCallId, 'completed')
324
+ return
325
+ }
326
+
327
+ case 'tool-output-error':
328
+ case 'tool-error': {
329
+ const toolCallId = readChunkToolCallId(chunk)
330
+ if (!toolCallId) return
331
+ completeTool(toolCallId, 'failed', readChunkErrorText(chunk) ?? undefined)
332
+ return
333
+ }
334
+
335
+ default:
336
+ return
337
+ }
338
+ },
339
+
340
+ finish(): void {
341
+ markThinkingStepDone()
342
+ },
343
+ }
344
+ }
@@ -1,9 +1,7 @@
1
1
  import { Schema, Effect } from 'effect'
2
2
 
3
- import { getPluginRuntime } from '../config/agent-defaults'
4
3
  import type { RecordIdRef } from '../db/record-id'
5
- import type { LotaRuntimeIndexedRepositoriesContext } from './runtime-extensions'
6
- import { getRuntimeAdapters } from './runtime-extensions'
4
+ import type { LotaRuntimeAdapters, LotaRuntimeIndexedRepositoriesContext } from './runtime-extensions'
7
5
 
8
6
  function isRecord(value: unknown): value is Record<string, unknown> {
9
7
  return typeof value === 'object' && value !== null
@@ -44,14 +42,15 @@ function requireRecord(value: unknown, message: string): Effect.Effect<Record<st
44
42
  }
45
43
 
46
44
  function resolvePluginServiceEffect(
45
+ pluginRuntime: Record<string, unknown> | undefined,
47
46
  pluginName: string,
48
47
  serviceName: string,
49
48
  methodName: string,
50
49
  ): Effect.Effect<(...args: unknown[]) => PromiseLike<unknown>, PluginResolutionError> {
51
- return Effect.fromNullishOr(getPluginRuntime()).pipe(
50
+ return Effect.fromNullishOr(pluginRuntime).pipe(
52
51
  Effect.mapError(() => configError(`Plugin runtime is not configured. Missing "${pluginName}" integration.`)),
53
- Effect.flatMap((pluginRuntime) =>
54
- requireRecord(pluginRuntime[pluginName], `Plugin "${pluginName}" is not configured in the current runtime.`),
52
+ Effect.flatMap((runtime) =>
53
+ requireRecord(runtime[pluginName], `Plugin "${pluginName}" is not configured in the current runtime.`),
55
54
  ),
56
55
  Effect.flatMap((plugin) =>
57
56
  requireRecord(plugin.services, `Plugin "${pluginName}" does not expose a services registry.`),
@@ -76,17 +75,26 @@ function resolvePluginServiceEffect(
76
75
  }
77
76
 
78
77
  function tryResolvePluginServiceEffect(
78
+ pluginRuntime: Record<string, unknown> | undefined,
79
79
  pluginName: string,
80
80
  serviceName: string,
81
81
  methodName: string,
82
82
  ): Effect.Effect<((...args: unknown[]) => PromiseLike<unknown>) | void, never, never> {
83
- return Effect.catch(resolvePluginServiceEffect(pluginName, serviceName, methodName), () => Effect.void)
83
+ return Effect.catch(resolvePluginServiceEffect(pluginRuntime, pluginName, serviceName, methodName), () => Effect.void)
84
84
  }
85
85
 
86
- export function getLinearInstallationByOrgId(organizationId: RecordIdRef): Promise<unknown> {
86
+ export function getLinearInstallationByOrgId(
87
+ pluginRuntime: Record<string, unknown> | undefined,
88
+ organizationId: RecordIdRef,
89
+ ): Promise<unknown> {
87
90
  return Effect.runPromise(
88
91
  Effect.gen(function* () {
89
- const fn = yield* tryResolvePluginServiceEffect('linear', 'linearService', 'getInstallationByOrgId')
92
+ const fn = yield* tryResolvePluginServiceEffect(
93
+ pluginRuntime,
94
+ 'linear',
95
+ 'linearService',
96
+ 'getInstallationByOrgId',
97
+ )
90
98
  if (!fn) return null
91
99
  return yield* Effect.tryPromise({
92
100
  try: () => fn(organizationId),
@@ -96,10 +104,18 @@ export function getLinearInstallationByOrgId(organizationId: RecordIdRef): Promi
96
104
  )
97
105
  }
98
106
 
99
- export function getGithubInstallationForOrganization(organizationId: string): Promise<unknown> {
107
+ export function getGithubInstallationForOrganization(
108
+ pluginRuntime: Record<string, unknown> | undefined,
109
+ organizationId: string,
110
+ ): Promise<unknown> {
100
111
  return Effect.runPromise(
101
112
  Effect.gen(function* () {
102
- const fn = yield* tryResolvePluginServiceEffect('github', 'githubService', 'getInstallationForOrganization')
113
+ const fn = yield* tryResolvePluginServiceEffect(
114
+ pluginRuntime,
115
+ 'github',
116
+ 'githubService',
117
+ 'getInstallationForOrganization',
118
+ )
103
119
  if (!fn) return null
104
120
  return yield* Effect.tryPromise({
105
121
  try: () => fn(organizationId),
@@ -116,9 +132,10 @@ const EMPTY_INDEXED_REPO_CONTEXT: LotaRuntimeIndexedRepositoriesContext = {
116
132
  }
117
133
 
118
134
  export function buildIndexedRepositoriesContext(
135
+ adapters: LotaRuntimeAdapters,
119
136
  organizationId: string,
120
137
  ): Promise<LotaRuntimeIndexedRepositoriesContext> {
121
- const buildContext = getRuntimeAdapters().buildIndexedRepositoriesContext
138
+ const buildContext = adapters.buildIndexedRepositoriesContext
122
139
  if (!buildContext) return Promise.resolve(EMPTY_INDEXED_REPO_CONTEXT)
123
140
  return Effect.runPromise(
124
141
  Effect.tryPromise({