@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.
- package/package.json +3 -3
- package/src/ai-gateway/ai-gateway.ts +214 -98
- package/src/ai-gateway/index.ts +16 -1
- package/src/config/agent-defaults.ts +4 -120
- package/src/config/logger.ts +18 -34
- package/src/config/model-constants.ts +1 -0
- package/src/config/thread-defaults.ts +1 -18
- package/src/create-runtime.ts +90 -28
- package/src/db/base.service.ts +30 -38
- package/src/db/service.ts +489 -545
- package/src/effect/index.ts +0 -2
- package/src/effect/layers.ts +6 -13
- package/src/embeddings/provider.ts +2 -7
- package/src/index.ts +4 -5
- package/src/queues/autonomous-job.queue.ts +159 -113
- package/src/queues/context-compaction.queue.ts +39 -25
- package/src/queues/delayed-node-promotion.queue.ts +56 -29
- package/src/queues/document-processor.queue.ts +5 -3
- package/src/queues/index.ts +1 -0
- package/src/queues/memory-consolidation.queue.ts +79 -53
- package/src/queues/organization-learning.queue.ts +63 -39
- package/src/queues/plan-agent-heartbeat.queue.ts +104 -79
- package/src/queues/plan-scheduler.queue.ts +100 -84
- package/src/queues/post-chat-memory.queue.ts +55 -33
- package/src/queues/queue-factory.ts +40 -41
- package/src/queues/queues.service.ts +61 -0
- package/src/queues/title-generation.queue.ts +42 -31
- package/src/redis/org-memory-lock.ts +24 -9
- package/src/redis/redis-lease-lock.ts +8 -1
- package/src/runtime/agent-identity-overrides.ts +7 -3
- package/src/runtime/agent-runtime-policy.ts +9 -4
- package/src/runtime/agent-stream-helpers.ts +9 -4
- package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
- package/src/runtime/context-compaction/context-compaction.ts +9 -7
- package/src/runtime/domain-layer.ts +15 -4
- package/src/runtime/execution-plan-visibility.ts +5 -2
- package/src/runtime/graph-designer.ts +0 -22
- package/src/runtime/index.ts +2 -0
- package/src/runtime/indexed-repositories-policy.ts +2 -6
- package/src/runtime/live-turn-trace.ts +344 -0
- package/src/runtime/plugin-resolution.ts +29 -12
- package/src/runtime/post-turn-side-effects.ts +139 -141
- package/src/runtime/runtime-config.ts +0 -6
- package/src/runtime/runtime-extensions.ts +0 -54
- package/src/runtime/runtime-lifecycle.ts +4 -4
- package/src/runtime/runtime-services.ts +125 -53
- package/src/runtime/runtime-worker-registry.ts +113 -30
- package/src/runtime/social-chat/social-chat-agent-runner.ts +6 -3
- package/src/runtime/social-chat/social-chat-history.ts +3 -1
- package/src/runtime/social-chat/social-chat.ts +35 -20
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +6 -5
- package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
- package/src/runtime/thread-chat-helpers.ts +18 -9
- package/src/runtime/thread-turn-context.ts +7 -47
- package/src/runtime/turn-lifecycle.ts +6 -14
- package/src/services/agent-activity.service.ts +168 -175
- package/src/services/agent-executor.service.ts +35 -16
- package/src/services/attachment.service.ts +4 -70
- package/src/services/autonomous-job.service.ts +53 -61
- package/src/services/context-compaction.service.ts +7 -9
- package/src/services/execution-plan/execution-plan-graph.ts +106 -115
- package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
- package/src/services/execution-plan/execution-plan.service.ts +67 -50
- package/src/services/global-orchestrator.service.ts +18 -7
- package/src/services/graph-full-routing.ts +7 -6
- package/src/services/memory/memory-conversation.ts +10 -5
- package/src/services/memory/memory.service.ts +11 -8
- package/src/services/ownership-dispatcher.service.ts +16 -5
- package/src/services/plan/plan-agent-heartbeat.service.ts +29 -15
- package/src/services/plan/plan-agent-query.service.ts +12 -8
- package/src/services/plan/plan-completion-side-effects.ts +93 -101
- package/src/services/plan/plan-cycle.service.ts +7 -45
- package/src/services/plan/plan-deadline.service.ts +28 -17
- package/src/services/plan/plan-event-delivery.service.ts +47 -40
- package/src/services/plan/plan-executor-context.ts +2 -0
- package/src/services/plan/plan-executor-graph.ts +366 -391
- package/src/services/plan/plan-executor.service.ts +13 -91
- package/src/services/plan/plan-scheduler.service.ts +62 -49
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/recent-activity-title.service.ts +6 -2
- package/src/services/thread/thread-bootstrap.ts +11 -9
- package/src/services/thread/thread-message.service.ts +6 -5
- package/src/services/thread/thread-turn-execution.ts +86 -82
- package/src/services/thread/thread-turn-preparation.service.ts +92 -45
- package/src/services/thread/thread-turn-streaming.ts +60 -28
- package/src/services/thread/thread-turn.ts +212 -46
- package/src/services/thread/thread.service.ts +21 -6
- package/src/system-agents/recent-activity-title-refiner.agent.ts +8 -5
- package/src/system-agents/thread-router.agent.ts +23 -20
- package/src/tools/execution-plan.tool.ts +8 -3
- package/src/tools/fetch-webpage.tool.ts +10 -9
- package/src/tools/firecrawl-client.ts +0 -15
- package/src/tools/remember-memory.tool.ts +3 -6
- package/src/tools/research-topic.tool.ts +12 -3
- package/src/tools/search-web.tool.ts +10 -9
- package/src/tools/search.tool.ts +4 -5
- package/src/tools/team-think.tool.ts +139 -121
- package/src/workers/bootstrap.ts +9 -10
- package/src/workers/memory-consolidation.worker.ts +4 -1
- package/src/workers/organization-learning.worker.ts +15 -2
- package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +21 -14
- package/src/workers/skill-extraction.runner.ts +13 -15
- package/src/workers/worker-utils.ts +6 -18
- package/src/effect/awaitable-effect.ts +0 -96
- package/src/effect/runtime-ref.ts +0 -25
- package/src/effect/runtime.ts +0 -46
- package/src/redis/runtime-connection.ts +0 -20
- package/src/runtime/runtime-accessors.ts +0 -92
- 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(
|
|
50
|
+
return Effect.fromNullishOr(pluginRuntime).pipe(
|
|
52
51
|
Effect.mapError(() => configError(`Plugin runtime is not configured. Missing "${pluginName}" integration.`)),
|
|
53
|
-
Effect.flatMap((
|
|
54
|
-
requireRecord(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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({
|