@lota-sdk/core 0.1.9 → 0.1.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/infrastructure/schema/00_workstream.surql +1 -0
- package/infrastructure/schema/02_execution_plan.surql +202 -52
- package/package.json +4 -87
- package/src/ai/index.ts +3 -0
- package/src/bifrost/bifrost.ts +94 -25
- package/src/bifrost/index.ts +1 -0
- package/src/config/agent-defaults.ts +30 -7
- package/src/config/constants.ts +0 -9
- package/src/config/debug-logger.ts +43 -0
- package/src/config/index.ts +5 -0
- package/src/config/model-constants.ts +8 -9
- package/src/config/workstream-defaults.ts +4 -0
- package/src/db/cursor-pagination.ts +2 -2
- package/src/db/index.ts +10 -0
- package/src/db/memory-store.ts +3 -71
- package/src/db/memory.ts +9 -15
- package/src/db/service.ts +42 -2
- package/src/db/tables.ts +9 -2
- package/src/document/index.ts +2 -0
- package/src/document/parsing.ts +0 -25
- package/src/embeddings/provider.ts +102 -22
- package/src/index.ts +15 -499
- package/src/queues/index.ts +10 -0
- package/src/redis/connection-accessor.ts +26 -0
- package/src/redis/connection.ts +1 -1
- package/src/redis/index.ts +9 -25
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +1 -1
- package/src/redis/stream-context.ts +54 -0
- package/src/runtime/agent-runtime-policy.ts +9 -5
- package/src/runtime/agent-stream-helpers.ts +6 -3
- package/src/runtime/agent-types.ts +1 -5
- package/src/runtime/approval-continuation.ts +68 -1
- package/src/runtime/chat-attachments.ts +1 -1
- package/src/runtime/chat-request-routing.ts +6 -2
- package/src/runtime/context-compaction-runtime.ts +2 -2
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +22 -15
- package/src/runtime/index.ts +26 -0
- package/src/runtime/indexed-repositories-policy.ts +10 -10
- package/src/runtime/memory-pipeline.ts +0 -2
- package/src/runtime/runtime-config.ts +238 -0
- package/src/runtime/runtime-extensions.ts +3 -2
- package/src/runtime/runtime-worker-registry.ts +47 -0
- package/src/runtime/team-consultation-orchestrator.ts +9 -6
- package/src/runtime/team-consultation-prompts.ts +3 -2
- package/src/runtime/turn-lifecycle.ts +13 -5
- package/src/runtime/workstream-chat-helpers.ts +0 -54
- package/src/runtime/workstream-routing-policy.ts +3 -7
- package/src/runtime.ts +387 -0
- package/src/services/chat-attachments.service.ts +1 -1
- package/src/services/context-compaction.service.ts +1 -1
- package/src/services/document-chunk.service.ts +2 -2
- package/src/services/execution-plan.service.ts +584 -793
- package/src/services/index.ts +14 -0
- package/src/services/learned-skill.service.ts +82 -39
- package/src/services/memory.service.ts +5 -4
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +1 -1
- package/src/services/plan-approval.service.ts +83 -0
- package/src/services/plan-artifact.service.ts +44 -0
- package/src/services/plan-builder.service.ts +61 -0
- package/src/services/plan-checkpoint.service.ts +53 -0
- package/src/services/plan-compiler.service.ts +81 -0
- package/src/services/plan-executor.service.ts +1624 -0
- package/src/services/plan-run.service.ts +422 -0
- package/src/services/plan-validator.service.ts +760 -0
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +14 -16
- package/src/services/user.service.ts +2 -2
- package/src/services/workstream-message.service.ts +2 -3
- package/src/services/workstream-title.service.ts +1 -1
- package/src/services/workstream-turn-preparation.ts +156 -59
- package/src/services/workstream-turn.ts +26 -1
- package/src/services/workstream.service.ts +35 -9
- package/src/services/workstream.types.ts +1 -0
- package/src/storage/attachment-parser.ts +1 -1
- package/src/storage/attachment-storage.service.ts +11 -10
- package/src/storage/generated-document-storage.service.ts +7 -6
- package/src/storage/index.ts +10 -0
- package/src/system-agents/delegated-agent-factory.ts +78 -29
- package/src/system-agents/index.ts +4 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +38 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/skill-extractor.agent.ts +1 -1
- package/src/system-agents/skill-manager.agent.ts +2 -4
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +22 -48
- package/src/tools/firecrawl-client.ts +2 -2
- package/src/tools/index.ts +12 -0
- package/src/tools/log-hello-world.tool.ts +17 -0
- package/src/tools/research-topic.tool.ts +1 -1
- package/src/tools/team-think.tool.ts +1 -1
- package/src/tools/user-questions.tool.ts +2 -2
- package/src/utils/index.ts +6 -0
- package/src/workers/bootstrap.ts +8 -16
- package/src/workers/index.ts +7 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +1 -1
- package/src/workers/skill-extraction.runner.ts +3 -3
- package/src/workers/utils/{repo-indexer-chunker.ts → file-section-chunker.ts} +23 -52
- package/src/workers/utils/repo-structure-extractor.ts +2 -5
- package/src/workers/utils/repomix-file-sections.ts +42 -0
- package/src/config/env-shapes.ts +0 -121
- package/src/runtime/agent-contract.ts +0 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { toTimestamp, withMessageCreatedAt } from '@lota-sdk/shared/runtime/chat-message-metadata'
|
|
2
|
-
import { baseChatMessageSchema } from '@lota-sdk/shared/schemas/chat-api'
|
|
3
|
-
import { messageMetadataSchema, dataPartsSchema } from '@lota-sdk/shared/schemas/chat-message'
|
|
4
|
-
import type { ChatMessage, MessageMetadata } from '@lota-sdk/shared/schemas/chat-message'
|
|
5
1
|
import {
|
|
6
|
-
|
|
2
|
+
baseChatMessageSchema,
|
|
7
3
|
CONSULT_SPECIALIST_TOOL_NAME,
|
|
4
|
+
CONSULT_TEAM_TOOL_NAME,
|
|
8
5
|
ConsultSpecialistArgsSchema,
|
|
9
|
-
|
|
6
|
+
dataPartsSchema,
|
|
7
|
+
messageMetadataSchema,
|
|
8
|
+
toTimestamp,
|
|
9
|
+
withMessageCreatedAt,
|
|
10
|
+
} from '@lota-sdk/shared'
|
|
11
|
+
import type { ChatMessage, MessageMetadata } from '@lota-sdk/shared'
|
|
10
12
|
import { convertToModelMessages, readUIMessageStream, stepCountIs, tool as createTool, validateUIMessages } from 'ai'
|
|
11
13
|
import type { PrepareStepFunction, StopCondition, ToolSet, UIMessageStreamWriter } from 'ai'
|
|
12
14
|
import type { z } from 'zod'
|
|
@@ -15,10 +17,12 @@ import {
|
|
|
15
17
|
agentDisplayNames,
|
|
16
18
|
buildAgentTools,
|
|
17
19
|
createAgent,
|
|
20
|
+
getLeadAgentId,
|
|
18
21
|
getCoreWorkstreamProfile,
|
|
19
22
|
getAgentRuntimeConfig,
|
|
20
23
|
pluginRuntime,
|
|
21
24
|
} from '../config/agent-defaults'
|
|
25
|
+
import { lotaDebugLogger } from '../config/debug-logger'
|
|
22
26
|
import { aiLogger } from '../config/logger'
|
|
23
27
|
import type { RecordIdRef } from '../db/record-id'
|
|
24
28
|
import { recordIdToString } from '../db/record-id'
|
|
@@ -93,7 +97,7 @@ interface UIMessageStreamResult {
|
|
|
93
97
|
toUIMessageStream(options: Record<string, unknown>): ReadableStream<unknown>
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
function hasUIMessageStream(value: unknown): value is UIMessageStreamResult {
|
|
97
101
|
return (
|
|
98
102
|
typeof value === 'object' &&
|
|
99
103
|
value !== null &&
|
|
@@ -101,7 +105,7 @@ export function hasUIMessageStream(value: unknown): value is UIMessageStreamResu
|
|
|
101
105
|
)
|
|
102
106
|
}
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
function getPluginService(path: string[]): ((...args: unknown[]) => unknown) | undefined {
|
|
105
109
|
let current: unknown = pluginRuntime
|
|
106
110
|
let owner: unknown = undefined
|
|
107
111
|
for (const key of path) {
|
|
@@ -118,7 +122,7 @@ export function getPluginService(path: string[]): ((...args: unknown[]) => unkno
|
|
|
118
122
|
: (current as (...args: unknown[]) => unknown)
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
|
|
125
|
+
async function buildIndexedRepositoriesContext(
|
|
122
126
|
workspaceId: string,
|
|
123
127
|
): Promise<{ provideRepoTool: boolean; defaultSectionsByAgent: Record<string, unknown>; context: string }> {
|
|
124
128
|
const buildContext = getRuntimeAdapters().workstream?.buildIndexedRepositoriesContext
|
|
@@ -136,11 +140,20 @@ export async function buildIndexedRepositoriesContext(
|
|
|
136
140
|
|
|
137
141
|
const PRESEEDED_MEMORY_LOOKUP_LIMIT = 3
|
|
138
142
|
|
|
139
|
-
|
|
143
|
+
function parsePersistedWorkstreamState(value: unknown): WorkstreamState | null {
|
|
140
144
|
return parseWorkstreamState(value)
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
function stripExecutionPlanFieldsFromWorkstreamState(
|
|
148
|
+
state: WorkstreamState | null | undefined,
|
|
149
|
+
hasExecutionPlan: boolean,
|
|
150
|
+
): WorkstreamState | null | undefined {
|
|
151
|
+
if (!state || !hasExecutionPlan) return state
|
|
152
|
+
|
|
153
|
+
return { ...state, currentPlan: null, tasks: [], artifacts: [] }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function waitForWorkstreamCompactionIfNeeded(workstreamId: RecordIdRef): Promise<WorkstreamRecord> {
|
|
144
157
|
return await waitForCompactionIfNeeded({
|
|
145
158
|
entityId: recordIdToString(workstreamId, TABLES.WORKSTREAM),
|
|
146
159
|
entityLabel: 'Workstream',
|
|
@@ -149,13 +162,13 @@ export async function waitForWorkstreamCompactionIfNeeded(workstreamId: RecordId
|
|
|
149
162
|
})
|
|
150
163
|
}
|
|
151
164
|
|
|
152
|
-
|
|
165
|
+
function parseChatMessageCandidate(value: unknown): ChatMessage | undefined {
|
|
153
166
|
const parsed = baseChatMessageSchema.safeParse(value)
|
|
154
167
|
if (!parsed.success) return undefined
|
|
155
168
|
return parsed.data as ChatMessage
|
|
156
169
|
}
|
|
157
170
|
|
|
158
|
-
|
|
171
|
+
function getChatMessageFromToolOutput(output: unknown): ChatMessage | undefined {
|
|
159
172
|
const directCandidate = parseChatMessageCandidate(output)
|
|
160
173
|
if (directCandidate) return directCandidate
|
|
161
174
|
|
|
@@ -173,7 +186,7 @@ export function getChatMessageFromToolOutput(output: unknown): ChatMessage | und
|
|
|
173
186
|
return undefined
|
|
174
187
|
}
|
|
175
188
|
|
|
176
|
-
|
|
189
|
+
class WorkstreamTurnError extends AppError {
|
|
177
190
|
constructor(
|
|
178
191
|
message: string,
|
|
179
192
|
readonly statusCode: 400 | 409,
|
|
@@ -183,19 +196,19 @@ export class WorkstreamTurnError extends AppError {
|
|
|
183
196
|
}
|
|
184
197
|
}
|
|
185
198
|
|
|
186
|
-
|
|
199
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
187
200
|
return value && typeof value === 'object' ? (value as Record<string, unknown>) : null
|
|
188
201
|
}
|
|
189
202
|
|
|
190
|
-
|
|
203
|
+
function readOptionalString(value: unknown): string | undefined {
|
|
191
204
|
return typeof value === 'string' && value.trim().length > 0 ? value : undefined
|
|
192
205
|
}
|
|
193
206
|
|
|
194
|
-
|
|
207
|
+
function readOptionalBoolean(value: unknown): boolean | undefined {
|
|
195
208
|
return typeof value === 'boolean' ? value : undefined
|
|
196
209
|
}
|
|
197
210
|
|
|
198
|
-
|
|
211
|
+
function readInstructionSections(value: unknown): string[] {
|
|
199
212
|
if (!Array.isArray(value)) {
|
|
200
213
|
return []
|
|
201
214
|
}
|
|
@@ -206,7 +219,7 @@ export function readInstructionSections(value: unknown): string[] {
|
|
|
206
219
|
.filter((section) => section.length > 0)
|
|
207
220
|
}
|
|
208
221
|
|
|
209
|
-
|
|
222
|
+
function optionalInstructionSection(value: unknown): string[] | undefined {
|
|
210
223
|
const section = readOptionalString(value)
|
|
211
224
|
return section ? [section] : undefined
|
|
212
225
|
}
|
|
@@ -220,6 +233,7 @@ export interface WorkstreamTurnParams {
|
|
|
220
233
|
inputMessage: ChatMessage
|
|
221
234
|
persistInputMessage?: boolean
|
|
222
235
|
abortSignal?: AbortSignal
|
|
236
|
+
streamId?: string
|
|
223
237
|
}
|
|
224
238
|
|
|
225
239
|
export interface WorkstreamApprovalContinuationParams {
|
|
@@ -230,26 +244,29 @@ export interface WorkstreamApprovalContinuationParams {
|
|
|
230
244
|
userName?: string | null
|
|
231
245
|
approvalMessages: ChatMessage[]
|
|
232
246
|
abortSignal?: AbortSignal
|
|
247
|
+
streamId?: string
|
|
233
248
|
}
|
|
234
249
|
|
|
235
|
-
|
|
250
|
+
type WorkstreamRunCoreParams = {
|
|
236
251
|
workstream: NormalizedWorkstream
|
|
237
252
|
workstreamRef: RecordIdRef
|
|
238
253
|
orgRef: RecordIdRef
|
|
239
254
|
userRef: RecordIdRef
|
|
240
255
|
userName?: string | null
|
|
241
256
|
abortSignal?: AbortSignal
|
|
257
|
+
streamId?: string
|
|
242
258
|
} & (
|
|
243
259
|
| { kind: 'userTurn'; inputMessage: ChatMessage; persistInputMessage?: boolean }
|
|
244
260
|
| { kind: 'approvalContinuation'; approvalMessages: ChatMessage[] }
|
|
261
|
+
| { kind: 'nativeToolApprovalTurn'; approvalMessages: ChatMessage[] }
|
|
245
262
|
)
|
|
246
263
|
|
|
247
|
-
|
|
264
|
+
interface PreparedWorkstreamTurn {
|
|
248
265
|
originalMessages: ChatMessage[]
|
|
249
266
|
run: (writer?: UIMessageStreamWriter<ChatMessage>) => Promise<void>
|
|
250
267
|
}
|
|
251
268
|
|
|
252
|
-
|
|
269
|
+
function upsertChatHistoryMessage(messages: ChatMessage[], nextMessage: ChatMessage): ChatMessage[] {
|
|
253
270
|
const existingIndex = messages.findIndex((message) => message.id === nextMessage.id)
|
|
254
271
|
if (existingIndex === -1) {
|
|
255
272
|
return [...messages, nextMessage]
|
|
@@ -260,22 +277,19 @@ export function upsertChatHistoryMessage(messages: ChatMessage[], nextMessage: C
|
|
|
260
277
|
return nextMessages
|
|
261
278
|
}
|
|
262
279
|
|
|
263
|
-
|
|
280
|
+
function buildRecentActivityChatDeepLink(params: {
|
|
264
281
|
workstream: NormalizedWorkstream
|
|
265
282
|
workstreamId: string
|
|
266
283
|
visibleAgentId: string
|
|
267
|
-
}): { route:
|
|
284
|
+
}): { route: string; search: Record<string, string> } {
|
|
268
285
|
if (params.workstream.mode === 'direct') {
|
|
269
|
-
return {
|
|
270
|
-
route: '/chat',
|
|
271
|
-
search: { agent: params.visibleAgentId, tab: params.visibleAgentId === 'chief' ? 'cos' : 'team' },
|
|
272
|
-
}
|
|
286
|
+
return { route: 'direct-workstream', search: { workstreamId: params.workstreamId, agentId: params.visibleAgentId } }
|
|
273
287
|
}
|
|
274
288
|
|
|
275
|
-
return { route: '
|
|
289
|
+
return { route: 'group-workstream', search: { workstreamId: params.workstreamId } }
|
|
276
290
|
}
|
|
277
291
|
|
|
278
|
-
|
|
292
|
+
function buildRecentActivityChatSystemTitle(params: {
|
|
279
293
|
workstream: NormalizedWorkstream
|
|
280
294
|
visibleAgentId: string
|
|
281
295
|
}): string {
|
|
@@ -307,7 +321,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
307
321
|
|
|
308
322
|
let inputMessage: ChatMessage | undefined
|
|
309
323
|
const shouldPersistInputMessage = params.kind === 'userTurn' ? params.persistInputMessage !== false : false
|
|
310
|
-
const shouldProcessPostRunSideEffects =
|
|
324
|
+
const shouldProcessPostRunSideEffects =
|
|
325
|
+
params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn' || shouldPersistInputMessage
|
|
311
326
|
if (params.kind === 'userTurn') {
|
|
312
327
|
inputMessage = hydrateMessageFileUrls(withMessageCreatedAt(params.inputMessage))
|
|
313
328
|
if (inputMessage.role !== 'user') {
|
|
@@ -321,12 +336,18 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
321
336
|
}
|
|
322
337
|
}
|
|
323
338
|
|
|
339
|
+
const timer = lotaDebugLogger.timer('prepare')
|
|
340
|
+
|
|
341
|
+
// Start workspace fetch early — does not depend on compaction gate
|
|
342
|
+
const workspacePromise = workspaceProvider ? workspaceProvider.getWorkspace(orgRef) : Promise.resolve({})
|
|
343
|
+
|
|
324
344
|
const workstreamRecord = await waitForWorkstreamCompactionIfNeeded(workstreamRef)
|
|
345
|
+
timer.step('compaction-gate')
|
|
325
346
|
if (toOptionalTrimmedString(workstreamRecord.activeRunId)) {
|
|
326
347
|
throw new WorkstreamTurnError('A chat run is already active.', 409)
|
|
327
348
|
}
|
|
328
349
|
|
|
329
|
-
if (params.kind === 'approvalContinuation') {
|
|
350
|
+
if (params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn') {
|
|
330
351
|
const approvedAssistantMessage = [...params.approvalMessages]
|
|
331
352
|
.reverse()
|
|
332
353
|
.find((m) => m.role === 'assistant' && hasApprovalRespondedParts(m))
|
|
@@ -334,9 +355,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
334
355
|
throw new WorkstreamTurnError('No approval-responded message found.', 400)
|
|
335
356
|
}
|
|
336
357
|
await workstreamMessageService.upsertMessages({ workstreamId: workstreamRef, messages: [approvedAssistantMessage] })
|
|
358
|
+
timer.step('persist-approval-message')
|
|
337
359
|
}
|
|
338
360
|
|
|
339
|
-
const workspacePromise = workspaceProvider ? workspaceProvider.getWorkspace(orgRef) : Promise.resolve({})
|
|
340
361
|
const initialWorkstreamState = parsePersistedWorkstreamState(workstreamRecord.state)
|
|
341
362
|
const persistedCompactionCursor = toOptionalTrimmedString(workstreamRecord.lastCompactedMessageId) ?? undefined
|
|
342
363
|
const persistedLiveHistoryPromise = workstreamMessageService.listMessagesAfterCursor(
|
|
@@ -361,10 +382,13 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
361
382
|
persistedLiveHistoryPromise,
|
|
362
383
|
persistedRecentHistoryPromise,
|
|
363
384
|
])
|
|
385
|
+
timer.step('fetch-workspace+history')
|
|
364
386
|
const workspaceLifecycleState = workspaceProvider ? await workspaceProvider.getLifecycleState?.(workspace) : undefined
|
|
387
|
+
timer.step('workspace-lifecycle-state')
|
|
365
388
|
const workspaceProfileState = workspaceProvider
|
|
366
389
|
? await workspaceProvider.readProfileProjectionState?.(workspace)
|
|
367
390
|
: undefined
|
|
391
|
+
timer.step('workspace-profile-state')
|
|
368
392
|
const [liveHistory, recentHistory] = await Promise.all([
|
|
369
393
|
persistedLiveHistory.length === 0
|
|
370
394
|
? Promise.resolve([] as ChatMessage[])
|
|
@@ -381,9 +405,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
381
405
|
dataSchemas: dataPartsSchema,
|
|
382
406
|
}).then((messages) => messages.map(hydrateMessageFileUrls)),
|
|
383
407
|
])
|
|
408
|
+
timer.step('validate+hydrate-history')
|
|
384
409
|
|
|
385
410
|
if (userMessage && shouldPersistInputMessage) {
|
|
386
411
|
await workstreamMessageService.upsertMessages({ workstreamId: workstreamRef, messages: [userMessage] })
|
|
412
|
+
timer.step('persist-user-message')
|
|
387
413
|
}
|
|
388
414
|
|
|
389
415
|
const originalMessages = userMessage ? upsertChatHistoryMessage(liveHistory, userMessage) : liveHistory
|
|
@@ -419,8 +445,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
419
445
|
skills?: string[]
|
|
420
446
|
})
|
|
421
447
|
: null
|
|
448
|
+
const defaultLeadAgentId = getLeadAgentId()
|
|
422
449
|
const visibleWorkstreamAgentId =
|
|
423
|
-
workstream.mode === 'direct' ? workstream.agentId : (coreWorkstreamProfile?.config.agentId ??
|
|
450
|
+
workstream.mode === 'direct' ? workstream.agentId : (coreWorkstreamProfile?.config.agentId ?? defaultLeadAgentId)
|
|
424
451
|
const coreInstructionSections = coreWorkstreamProfile ? [coreWorkstreamProfile.instructions] : undefined
|
|
425
452
|
const getLinearInstallationByOrgId = getPluginService([
|
|
426
453
|
'linear',
|
|
@@ -442,6 +469,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
442
469
|
forceDeep: highImpactAssessment.classes.length > 0 || policyAssessment.classes.length > 0,
|
|
443
470
|
explicitProfile: onboardingActive ? 'standard' : undefined,
|
|
444
471
|
})
|
|
472
|
+
timer.step('reasoning-classification')
|
|
445
473
|
|
|
446
474
|
const [linearInstallation, githubInstallation, indexedRepoContext, recentDomainEvents, promptSummary] =
|
|
447
475
|
await Promise.all([
|
|
@@ -455,6 +483,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
455
483
|
? workspaceProvider.buildPromptSummary(orgRef).catch(() => undefined)
|
|
456
484
|
: Promise.resolve(undefined),
|
|
457
485
|
])
|
|
486
|
+
timer.step('parallel-context-fetch(plugins+repos+events+summary)')
|
|
458
487
|
let linearInstalled = Boolean(linearInstallation)
|
|
459
488
|
let githubInstalled = Boolean(githubInstallation)
|
|
460
489
|
let promptContext = buildAgentPromptContext({
|
|
@@ -473,6 +502,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
473
502
|
userId: userIdString,
|
|
474
503
|
query: messageText,
|
|
475
504
|
})
|
|
505
|
+
timer.step('rag-knowledge-retrieval')
|
|
476
506
|
const buildContextResult = asRecord(
|
|
477
507
|
await turnHooks.buildContext?.({
|
|
478
508
|
workstream,
|
|
@@ -494,6 +524,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
494
524
|
retrievedKnowledgeSection,
|
|
495
525
|
}),
|
|
496
526
|
)
|
|
527
|
+
timer.step('hook:buildContext')
|
|
497
528
|
const buildContextPromptDetails = readOptionalString(buildContextResult?.systemWorkspaceDetails)
|
|
498
529
|
if (buildContextPromptDetails) {
|
|
499
530
|
promptContext = { systemWorkspaceDetails: buildContextPromptDetails }
|
|
@@ -532,6 +563,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
532
563
|
context: buildContextResult,
|
|
533
564
|
}),
|
|
534
565
|
)
|
|
566
|
+
timer.step('hook:buildExtraInstructionSections')
|
|
535
567
|
|
|
536
568
|
let memoryBlock = workstreamService.formatMemoryBlockForPrompt(workstreamRecord)
|
|
537
569
|
let workstreamState = initialWorkstreamState
|
|
@@ -539,11 +571,39 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
539
571
|
disabled: onboardingActive,
|
|
540
572
|
loadPlan: async () => await executionPlanService.getActivePlanForWorkstream(workstreamRef),
|
|
541
573
|
})
|
|
574
|
+
const getExecutionPlan = async () => await executionPlanInstructionSectionCache.getPlan()
|
|
542
575
|
const getExecutionPlanInstructionSections = async (): Promise<string[] | undefined> =>
|
|
543
576
|
await executionPlanInstructionSectionCache.getSections()
|
|
544
577
|
const invalidateExecutionPlanInstructionSections = () => {
|
|
545
578
|
executionPlanInstructionSectionCache.invalidate()
|
|
546
579
|
}
|
|
580
|
+
const getWorkstreamStateSection = async (): Promise<string | undefined> => {
|
|
581
|
+
const executionPlan = await getExecutionPlan()
|
|
582
|
+
return contextCompactionRuntime.formatWorkstreamStateForPrompt(
|
|
583
|
+
stripExecutionPlanFieldsFromWorkstreamState(workstreamState, Boolean(executionPlan)),
|
|
584
|
+
)
|
|
585
|
+
}
|
|
586
|
+
const respondedBy = recordIdToString(userRef, TABLES.USER)
|
|
587
|
+
if (params.kind === 'approvalContinuation') {
|
|
588
|
+
const appliedApproval = await executionPlanService.applyApprovalResponseFromMessages({
|
|
589
|
+
workstreamId: workstreamRef,
|
|
590
|
+
approvalMessages: params.approvalMessages,
|
|
591
|
+
respondedBy,
|
|
592
|
+
})
|
|
593
|
+
if (appliedApproval) {
|
|
594
|
+
invalidateExecutionPlanInstructionSections()
|
|
595
|
+
}
|
|
596
|
+
} else if (userMessage) {
|
|
597
|
+
const appliedHumanInput = await executionPlanService.applyHumanInputFromUserMessage({
|
|
598
|
+
workstreamId: workstreamRef,
|
|
599
|
+
message: userMessage,
|
|
600
|
+
respondedBy,
|
|
601
|
+
})
|
|
602
|
+
if (appliedHumanInput) {
|
|
603
|
+
invalidateExecutionPlanInstructionSections()
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
timer.step('execution-plan-input')
|
|
547
607
|
const preSeededMemoriesByAgent = new Map<string, string | undefined>()
|
|
548
608
|
const getPreSeededMemoriesSection = async (agentId: string): Promise<string | undefined> => {
|
|
549
609
|
if (preSeededMemoriesByAgent.has(agentId)) {
|
|
@@ -594,14 +654,21 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
594
654
|
uploadMetadataText: buildReadableUploadMetadataText(listReadableUploads(extraMessages)),
|
|
595
655
|
})
|
|
596
656
|
|
|
657
|
+
timer.step('preparation-complete')
|
|
658
|
+
|
|
597
659
|
return {
|
|
598
660
|
originalMessages,
|
|
599
661
|
run: async (writer?: UIMessageStreamWriter<ChatMessage>) => {
|
|
600
662
|
const executeRun = async () => {
|
|
663
|
+
const runTimer = lotaDebugLogger.timer('run')
|
|
601
664
|
const serverRunId = Bun.randomUUIDv7()
|
|
602
665
|
const runAbort = createServerRunAbortController(params.abortSignal)
|
|
603
|
-
await
|
|
666
|
+
await Promise.all([
|
|
667
|
+
workstreamService.setActiveRunId(workstreamRef, serverRunId),
|
|
668
|
+
params.streamId ? workstreamService.setActiveStreamId(workstreamRef, params.streamId) : undefined,
|
|
669
|
+
])
|
|
604
670
|
chatRunRegistry.register(serverRunId, runAbort.controller)
|
|
671
|
+
runTimer.step('set-active-run+stream')
|
|
605
672
|
|
|
606
673
|
try {
|
|
607
674
|
const buildAgentMetadataPatch = (agentId: string, agentName: string): NonNullable<MessageMetadata> => ({
|
|
@@ -660,7 +727,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
660
727
|
prepareStep?: PrepareStepFunction<ToolSet>
|
|
661
728
|
abortSignal?: AbortSignal
|
|
662
729
|
}): Promise<ChatMessage> => {
|
|
730
|
+
const agentTimer = lotaDebugLogger.timer(`agent:${streamParams.agentId}`)
|
|
663
731
|
const executionPlanInstructionSections = await getExecutionPlanInstructionSections()
|
|
732
|
+
agentTimer.step('get-execution-plan')
|
|
664
733
|
const agentResolution = asRecord(
|
|
665
734
|
await turnHooks.resolveAgent?.({
|
|
666
735
|
agentId: streamParams.agentId,
|
|
@@ -679,7 +748,14 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
679
748
|
context: buildContextResult,
|
|
680
749
|
}),
|
|
681
750
|
)
|
|
751
|
+
agentTimer.step('hook:resolveAgent')
|
|
682
752
|
const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? streamParams.agentId
|
|
753
|
+
const [preSeededMemoriesSection, workstreamStateSection, learnedSkillsSection] = await Promise.all([
|
|
754
|
+
getPreSeededMemoriesSection(resolvedAgentId),
|
|
755
|
+
getWorkstreamStateSection(),
|
|
756
|
+
getLearnedSkillsSection(resolvedAgentId),
|
|
757
|
+
])
|
|
758
|
+
agentTimer.step('parallel-fetch(memories+state+skills)')
|
|
683
759
|
const config = getAgentRuntimeConfig({
|
|
684
760
|
agentId: resolvedAgentId,
|
|
685
761
|
workstreamMode: workstream.mode,
|
|
@@ -689,11 +765,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
689
765
|
linearInstalled,
|
|
690
766
|
reasoningProfile: reasoningProfile.name,
|
|
691
767
|
systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
|
|
692
|
-
preSeededMemoriesSection
|
|
768
|
+
preSeededMemoriesSection,
|
|
693
769
|
retrievedKnowledgeSection,
|
|
694
770
|
workstreamMemoryBlock: memoryBlock,
|
|
695
|
-
workstreamStateSection
|
|
696
|
-
learnedSkillsSection
|
|
771
|
+
workstreamStateSection,
|
|
772
|
+
learnedSkillsSection,
|
|
697
773
|
additionalInstructionSections: mergeInstructionSections(
|
|
698
774
|
executionPlanInstructionSections,
|
|
699
775
|
streamParams.additionalInstructionSections,
|
|
@@ -703,9 +779,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
703
779
|
),
|
|
704
780
|
context: buildContextResult,
|
|
705
781
|
}) as AgentRuntimeConfig
|
|
782
|
+
agentTimer.step('build-agent-config')
|
|
706
783
|
const modelMessages = await convertToModelMessages(streamParams.messages, {
|
|
707
784
|
ignoreIncompleteToolCalls: true,
|
|
708
785
|
})
|
|
786
|
+
agentTimer.step('convert-model-messages')
|
|
709
787
|
const agent = (createAgent as unknown as AgentFactory)[config.id as string]({
|
|
710
788
|
mode: streamParams.mode,
|
|
711
789
|
tools: streamParams.tools,
|
|
@@ -719,12 +797,14 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
719
797
|
(agentResolution?.prepareStep as PrepareStepFunction<ToolSet> | undefined) ?? streamParams.prepareStep,
|
|
720
798
|
})
|
|
721
799
|
const agentAbortSignal = streamParams.abortSignal ?? runAbort.signal
|
|
800
|
+
agentTimer.step('agent-construction')
|
|
722
801
|
|
|
723
802
|
let result: unknown
|
|
724
803
|
try {
|
|
725
804
|
result = await streamParams.observer.run(() =>
|
|
726
805
|
agent.stream({ messages: modelMessages, abortSignal: agentAbortSignal }),
|
|
727
806
|
)
|
|
807
|
+
agentTimer.step('agent.stream()-resolved')
|
|
728
808
|
} catch (error) {
|
|
729
809
|
if (agentAbortSignal.aborted) {
|
|
730
810
|
streamParams.observer.recordAbort(error)
|
|
@@ -758,10 +838,15 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
758
838
|
},
|
|
759
839
|
}) as ReadableStream<ChatStreamChunk>
|
|
760
840
|
const reader = uiStream.getReader()
|
|
841
|
+
let firstChunkLogged = false
|
|
761
842
|
try {
|
|
762
843
|
for (;;) {
|
|
763
844
|
const { done, value } = await reader.read()
|
|
764
845
|
if (done) break
|
|
846
|
+
if (!firstChunkLogged) {
|
|
847
|
+
agentTimer.step('first-stream-chunk')
|
|
848
|
+
firstChunkLogged = true
|
|
849
|
+
}
|
|
765
850
|
if (streamParams.writer) {
|
|
766
851
|
streamParams.writer.write(value)
|
|
767
852
|
}
|
|
@@ -769,6 +854,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
769
854
|
} finally {
|
|
770
855
|
reader.releaseLock()
|
|
771
856
|
}
|
|
857
|
+
agentTimer.step('stream-complete')
|
|
772
858
|
|
|
773
859
|
const finalizedResponseMessage = await finishedStream.then(() => responseMessage)
|
|
774
860
|
if (finalizedResponseMessage === null) {
|
|
@@ -790,6 +876,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
790
876
|
extraMessages?: ChatMessage[]
|
|
791
877
|
extraTools?: ToolSet
|
|
792
878
|
}): Promise<ChatMessage> => {
|
|
879
|
+
const visibleTimer = lotaDebugLogger.timer(`visible:${runParams.agentId}`)
|
|
793
880
|
let runMemoryBlock = memoryBlock
|
|
794
881
|
const tools: ToolSet = {
|
|
795
882
|
...((await buildAgentTools({
|
|
@@ -819,6 +906,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
819
906
|
...toolProviders,
|
|
820
907
|
...runParams.extraTools,
|
|
821
908
|
}
|
|
909
|
+
visibleTimer.step('build-agent-tools')
|
|
822
910
|
const responseMessage = await streamAgentResponse({
|
|
823
911
|
agentId: runParams.agentId,
|
|
824
912
|
mode: runParams.mode,
|
|
@@ -830,6 +918,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
830
918
|
writer,
|
|
831
919
|
})
|
|
832
920
|
|
|
921
|
+
visibleTimer.step('stream-agent-response')
|
|
833
922
|
memoryBlock = runMemoryBlock
|
|
834
923
|
|
|
835
924
|
return await commitAssistantResponse(
|
|
@@ -839,6 +928,12 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
839
928
|
)
|
|
840
929
|
}
|
|
841
930
|
|
|
931
|
+
// Execution-plan approval continuations mutate plan state and persist the approval message,
|
|
932
|
+
// but they do not begin a new visible agent turn.
|
|
933
|
+
if (params.kind === 'approvalContinuation') {
|
|
934
|
+
return
|
|
935
|
+
}
|
|
936
|
+
|
|
842
937
|
const consultSpecialistTool = createTool({
|
|
843
938
|
description: 'Consult one specialist teammate for domain-specific guidance before replying to the user.',
|
|
844
939
|
inputSchema: ConsultSpecialistArgsSchema,
|
|
@@ -871,7 +966,17 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
871
966
|
context: buildContextResult,
|
|
872
967
|
})
|
|
873
968
|
|
|
874
|
-
const
|
|
969
|
+
const [
|
|
970
|
+
specialistExecutionPlanInstructionSections,
|
|
971
|
+
specialistPreSeededMemories,
|
|
972
|
+
specialistWorkstreamState,
|
|
973
|
+
specialistLearnedSkills,
|
|
974
|
+
] = await Promise.all([
|
|
975
|
+
getExecutionPlanInstructionSections(),
|
|
976
|
+
getPreSeededMemoriesSection(agentId),
|
|
977
|
+
getWorkstreamStateSection(),
|
|
978
|
+
getLearnedSkillsSection(agentId),
|
|
979
|
+
])
|
|
875
980
|
const specialistConfig = getAgentRuntimeConfig({
|
|
876
981
|
agentId,
|
|
877
982
|
workstreamMode: workstream.mode,
|
|
@@ -880,11 +985,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
880
985
|
linearInstalled,
|
|
881
986
|
reasoningProfile: reasoningProfile.name,
|
|
882
987
|
systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
|
|
883
|
-
preSeededMemoriesSection:
|
|
988
|
+
preSeededMemoriesSection: specialistPreSeededMemories,
|
|
884
989
|
retrievedKnowledgeSection,
|
|
885
990
|
workstreamMemoryBlock: specialistMemoryBlock,
|
|
886
|
-
workstreamStateSection:
|
|
887
|
-
learnedSkillsSection:
|
|
991
|
+
workstreamStateSection: specialistWorkstreamState,
|
|
992
|
+
learnedSkillsSection: specialistLearnedSkills,
|
|
888
993
|
additionalInstructionSections: mergeInstructionSections(
|
|
889
994
|
specialistExecutionPlanInstructionSections,
|
|
890
995
|
coreInstructionSections,
|
|
@@ -995,20 +1100,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
995
1100
|
throw new WorkstreamTurnError('Direct workstreams require an assigned agent.', 400)
|
|
996
1101
|
}
|
|
997
1102
|
await runVisibleAgent({ agentId: workstream.agentId, mode: 'direct' })
|
|
998
|
-
} else if (params.kind === 'userTurn') {
|
|
999
|
-
await runVisibleAgent({
|
|
1000
|
-
agentId: visibleWorkstreamAgentId ?? 'chief',
|
|
1001
|
-
mode: 'workstreamMode',
|
|
1002
|
-
skills: coreWorkstreamProfile?.skills ? [...coreWorkstreamProfile.skills] : undefined,
|
|
1003
|
-
additionalInstructionSections: mergeInstructionSections(coreInstructionSections, hookInstructionSections),
|
|
1004
|
-
extraTools: {
|
|
1005
|
-
[CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool,
|
|
1006
|
-
...(teamThinkTool ? { [CONSULT_TEAM_TOOL_NAME]: teamThinkTool } : {}),
|
|
1007
|
-
},
|
|
1008
|
-
})
|
|
1009
1103
|
} else {
|
|
1010
1104
|
await runVisibleAgent({
|
|
1011
|
-
agentId: visibleWorkstreamAgentId ??
|
|
1105
|
+
agentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1012
1106
|
mode: 'workstreamMode',
|
|
1013
1107
|
skills: coreWorkstreamProfile?.skills ? [...coreWorkstreamProfile.skills] : undefined,
|
|
1014
1108
|
additionalInstructionSections: mergeInstructionSections(coreInstructionSections, hookInstructionSections),
|
|
@@ -1043,6 +1137,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1043
1137
|
unregisterRun: (runId) => chatRunRegistry.unregister(runId),
|
|
1044
1138
|
clearActiveRunId: (runId) => workstreamService.clearActiveRunIdIfMatches(workstreamRef, runId),
|
|
1045
1139
|
disposeAbort: () => runAbort.dispose(),
|
|
1140
|
+
activeStreamId: params.streamId,
|
|
1141
|
+
clearActiveStreamId: (streamId) =>
|
|
1142
|
+
workstreamService.clearActiveStreamIdIfMatches(workstreamRef, streamId),
|
|
1046
1143
|
})
|
|
1047
1144
|
|
|
1048
1145
|
if (allAssistantMessages.length > 0 && shouldProcessPostRunSideEffects) {
|
|
@@ -1103,17 +1200,17 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1103
1200
|
mergeKey: `workstream:${workstreamIdString}`,
|
|
1104
1201
|
title: buildRecentActivityChatSystemTitle({
|
|
1105
1202
|
workstream,
|
|
1106
|
-
visibleAgentId: visibleWorkstreamAgentId ??
|
|
1203
|
+
visibleAgentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1107
1204
|
}),
|
|
1108
|
-
sourceLabel: agentDisplayNames[visibleWorkstreamAgentId ??
|
|
1205
|
+
sourceLabel: agentDisplayNames[visibleWorkstreamAgentId ?? defaultLeadAgentId],
|
|
1109
1206
|
deepLink: buildRecentActivityChatDeepLink({
|
|
1110
1207
|
workstream,
|
|
1111
1208
|
workstreamId: workstreamIdString,
|
|
1112
|
-
visibleAgentId: visibleWorkstreamAgentId ??
|
|
1209
|
+
visibleAgentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1113
1210
|
}),
|
|
1114
1211
|
metadata: {
|
|
1115
|
-
agentId: visibleWorkstreamAgentId ??
|
|
1116
|
-
agentName: agentDisplayNames[visibleWorkstreamAgentId ??
|
|
1212
|
+
agentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1213
|
+
agentName: agentDisplayNames[visibleWorkstreamAgentId ?? defaultLeadAgentId],
|
|
1117
1214
|
workstreamId: workstreamIdString,
|
|
1118
1215
|
workstreamTitle: latestWorkstreamRecord.title ?? workstream.title,
|
|
1119
1216
|
workstreamMode: workstream.mode,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ChatMessage } from '@lota-sdk/shared
|
|
1
|
+
import type { ChatMessage } from '@lota-sdk/shared'
|
|
2
2
|
import { createUIMessageStream } from 'ai'
|
|
3
3
|
|
|
4
|
+
import { lotaDebugLogger } from '../config/debug-logger'
|
|
4
5
|
import { hasApprovalRespondedParts, isApprovalContinuationRequest } from '../runtime/approval-continuation'
|
|
5
6
|
import { wrapResponseWithKeepalive } from '../utils/sse-keepalive'
|
|
6
7
|
import { prepareWorkstreamRunCore } from './workstream-turn-preparation'
|
|
@@ -10,30 +11,54 @@ export { hasApprovalRespondedParts, isApprovalContinuationRequest }
|
|
|
10
11
|
export { wrapResponseWithKeepalive }
|
|
11
12
|
|
|
12
13
|
export async function createWorkstreamApprovalContinuationStream(params: WorkstreamApprovalContinuationParams) {
|
|
14
|
+
const timer = lotaDebugLogger.timer('turn:approval-continuation')
|
|
13
15
|
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'approvalContinuation' })
|
|
16
|
+
timer.step('prepare')
|
|
14
17
|
|
|
15
18
|
return createUIMessageStream<ChatMessage>({
|
|
16
19
|
originalMessages: prepared.originalMessages,
|
|
17
20
|
onError: (error) => (error instanceof Error ? error.message : 'Approval continuation stream failed.'),
|
|
18
21
|
execute: async ({ writer }) => {
|
|
19
22
|
await prepared.run(writer)
|
|
23
|
+
timer.step('run')
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function createWorkstreamNativeToolApprovalStream(params: WorkstreamApprovalContinuationParams) {
|
|
29
|
+
const timer = lotaDebugLogger.timer('turn:native-tool-approval')
|
|
30
|
+
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'nativeToolApprovalTurn' })
|
|
31
|
+
timer.step('prepare')
|
|
32
|
+
|
|
33
|
+
return createUIMessageStream<ChatMessage>({
|
|
34
|
+
originalMessages: prepared.originalMessages,
|
|
35
|
+
onError: (error) => (error instanceof Error ? error.message : 'Native tool approval stream failed.'),
|
|
36
|
+
execute: async ({ writer }) => {
|
|
37
|
+
await prepared.run(writer)
|
|
38
|
+
timer.step('run')
|
|
20
39
|
},
|
|
21
40
|
})
|
|
22
41
|
}
|
|
23
42
|
|
|
24
43
|
export async function createWorkstreamTurnStream(params: WorkstreamTurnParams) {
|
|
44
|
+
const timer = lotaDebugLogger.timer('turn:user-turn')
|
|
25
45
|
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'userTurn' })
|
|
46
|
+
timer.step('prepare')
|
|
26
47
|
|
|
27
48
|
return createUIMessageStream<ChatMessage>({
|
|
28
49
|
originalMessages: prepared.originalMessages,
|
|
29
50
|
onError: (error) => (error instanceof Error ? error.message : 'Chat stream failed.'),
|
|
30
51
|
execute: async ({ writer }) => {
|
|
31
52
|
await prepared.run(writer)
|
|
53
|
+
timer.step('run')
|
|
32
54
|
},
|
|
33
55
|
})
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
export async function runWorkstreamTurnInBackground(params: WorkstreamTurnParams): Promise<void> {
|
|
59
|
+
const timer = lotaDebugLogger.timer('turn:background')
|
|
37
60
|
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'userTurn' })
|
|
61
|
+
timer.step('prepare')
|
|
38
62
|
await prepared.run()
|
|
63
|
+
timer.step('run')
|
|
39
64
|
}
|