@lota-sdk/core 0.1.8 → 0.1.11
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 +2 -1
- package/infrastructure/schema/02_execution_plan.surql +202 -52
- package/package.json +4 -2
- package/src/bifrost/bifrost.ts +94 -25
- package/src/config/model-constants.ts +8 -6
- package/src/db/memory-store.ts +3 -71
- package/src/db/service.ts +42 -2
- package/src/db/tables.ts +9 -2
- package/src/embeddings/provider.ts +92 -21
- package/src/index.ts +6 -0
- package/src/redis/stream-context.ts +44 -0
- package/src/runtime/approval-continuation.ts +59 -0
- package/src/runtime/chat-request-routing.ts +5 -1
- package/src/runtime/execution-plan.ts +21 -14
- package/src/runtime/turn-lifecycle.ts +14 -6
- package/src/runtime/workstream-chat-helpers.ts +5 -5
- package/src/services/context-compaction.service.ts +6 -2
- package/src/services/document-chunk.service.ts +2 -2
- package/src/services/execution-plan.service.ts +579 -786
- package/src/services/learned-skill.service.ts +2 -2
- package/src/services/plan-approval.service.ts +83 -0
- package/src/services/plan-artifact.service.ts +45 -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 +1623 -0
- package/src/services/plan-run.service.ts +422 -0
- package/src/services/plan-validator.service.ts +760 -0
- package/src/services/workstream-turn-preparation.ts +70 -196
- package/src/services/workstream-turn.ts +12 -0
- package/src/services/workstream.service.ts +24 -182
- package/src/services/workstream.types.ts +2 -65
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +20 -46
- package/src/tools/log-hello-world.tool.ts +17 -0
- package/src/workers/skill-extraction.runner.ts +2 -2
- package/src/services/workstream-change-tracker.service.ts +0 -313
- package/src/system-agents/workstream-tracker.agent.ts +0 -58
|
@@ -40,7 +40,7 @@ import { hasApprovalRespondedParts } from '../runtime/approval-continuation'
|
|
|
40
40
|
import { buildModelInputMessagesWithUploadMetadata, buildReadableUploadMetadataText } from '../runtime/chat-attachments'
|
|
41
41
|
import { hasMessageContent } from '../runtime/chat-message'
|
|
42
42
|
import { waitForCompactionIfNeeded } from '../runtime/chat-run-orchestration'
|
|
43
|
-
import {
|
|
43
|
+
import { parseWorkstreamState } from '../runtime/context-compaction'
|
|
44
44
|
import { CONTEXT_SIZE } from '../runtime/context-compaction-constants'
|
|
45
45
|
import { createExecutionPlanInstructionSectionCache } from '../runtime/execution-plan'
|
|
46
46
|
import { mergeInstructionSections } from '../runtime/instruction-sections'
|
|
@@ -54,13 +54,12 @@ import { getRuntimeAdapters, getToolProviders, getTurnHooks } from '../runtime/r
|
|
|
54
54
|
import { shouldEnqueueSkillExtraction } from '../runtime/skill-extraction-policy'
|
|
55
55
|
import { finalizeTurnRun } from '../runtime/turn-lifecycle'
|
|
56
56
|
import {
|
|
57
|
-
|
|
57
|
+
appendPersistedWorkstreamContextToHistoryMessages,
|
|
58
58
|
buildAgentHistoryMessages,
|
|
59
59
|
buildConversationSummary,
|
|
60
60
|
buildReadableUploadMetadataContext,
|
|
61
61
|
collectToolOutputErrors,
|
|
62
62
|
extractMessageText,
|
|
63
|
-
extractTrackerMessageText,
|
|
64
63
|
toHistoryMessages,
|
|
65
64
|
toOptionalTrimmedString,
|
|
66
65
|
} from '../runtime/workstream-chat-helpers'
|
|
@@ -69,8 +68,7 @@ import {
|
|
|
69
68
|
classifyPolicyClasses,
|
|
70
69
|
resolveReasoningProfile,
|
|
71
70
|
} from '../runtime/workstream-routing-policy'
|
|
72
|
-
import {
|
|
73
|
-
import type { WorkstreamState, WorkstreamStateDelta } from '../runtime/workstream-state'
|
|
71
|
+
import type { WorkstreamState } from '../runtime/workstream-state'
|
|
74
72
|
import { chatRunRegistry } from '../services/chat-run-registry.service'
|
|
75
73
|
import type { NormalizedWorkstream, WorkstreamRecord } from '../services/workstream.types'
|
|
76
74
|
import { createTeamThinkTool } from '../tools/team-think.tool'
|
|
@@ -84,7 +82,6 @@ import { executionPlanService } from './execution-plan.service'
|
|
|
84
82
|
import { learnedSkillService } from './learned-skill.service'
|
|
85
83
|
import { memoryService } from './memory.service'
|
|
86
84
|
import { recentActivityService } from './recent-activity.service'
|
|
87
|
-
import { updateWorkstreamChangeTracker } from './workstream-change-tracker.service'
|
|
88
85
|
import { workstreamMessageService } from './workstream-message.service'
|
|
89
86
|
import { workstreamService } from './workstream.service'
|
|
90
87
|
|
|
@@ -143,58 +140,13 @@ export function parsePersistedWorkstreamState(value: unknown): WorkstreamState |
|
|
|
143
140
|
return parseWorkstreamState(value)
|
|
144
141
|
}
|
|
145
142
|
|
|
146
|
-
export function
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
export function stripExecutionPlanFieldsFromWorkstreamState(
|
|
144
|
+
state: WorkstreamState | null | undefined,
|
|
145
|
+
hasExecutionPlan: boolean,
|
|
146
|
+
): WorkstreamState | null | undefined {
|
|
147
|
+
if (!state || !hasExecutionPlan) return state
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
return new URL(normalized)
|
|
153
|
-
} catch {
|
|
154
|
-
return null
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export function taskRequestsWebsiteRefresh(task: string, forceRefresh: boolean): boolean {
|
|
159
|
-
return forceRefresh || /\b(refresh|re-run|rerun|run again|extract again|re-extract|overwrite|recrawl)\b/i.test(task)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function buildInspectWebsiteTrackerTitle(input: unknown): string {
|
|
163
|
-
if (!input || typeof input !== 'object') return 'Inspect website intelligence'
|
|
164
|
-
|
|
165
|
-
const task = typeof (input as { task?: unknown }).task === 'string' ? (input as { task: string }).task.trim() : ''
|
|
166
|
-
const forceRefresh = (input as { forceRefresh?: unknown }).forceRefresh === true
|
|
167
|
-
const hostname = extractFirstAbsoluteUrl(task)?.hostname.replace(/^www\./, '') ?? null
|
|
168
|
-
|
|
169
|
-
if (hostname && taskRequestsWebsiteRefresh(task, forceRefresh)) {
|
|
170
|
-
return `Overwrite website-intelligence artifacts for ${hostname}`
|
|
171
|
-
}
|
|
172
|
-
if (hostname) {
|
|
173
|
-
return `Inspect website intelligence for ${hostname}`
|
|
174
|
-
}
|
|
175
|
-
return taskRequestsWebsiteRefresh(task, forceRefresh)
|
|
176
|
-
? 'Overwrite website-intelligence artifacts'
|
|
177
|
-
: 'Inspect website intelligence'
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function emitTransientWorkstreamTrackerState(params: {
|
|
181
|
-
writer?: UIMessageStreamWriter<ChatMessage>
|
|
182
|
-
workstreamRecord: WorkstreamRecord
|
|
183
|
-
existingState: WorkstreamState
|
|
184
|
-
delta: WorkstreamStateDelta
|
|
185
|
-
}): WorkstreamState {
|
|
186
|
-
const nextState = mergeStateDelta(params.existingState, params.delta, () => Date.now())
|
|
187
|
-
|
|
188
|
-
if (params.writer) {
|
|
189
|
-
params.writer.write({
|
|
190
|
-
type: 'data-workstreamTracker',
|
|
191
|
-
data: workstreamService.toPublicWorkstreamDetail({ ...params.workstreamRecord, state: nextState })
|
|
192
|
-
.workstreamState as unknown as Record<string, unknown>,
|
|
193
|
-
transient: true,
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return nextState
|
|
149
|
+
return { ...state, currentPlan: null, tasks: [], artifacts: [] }
|
|
198
150
|
}
|
|
199
151
|
|
|
200
152
|
export async function waitForWorkstreamCompactionIfNeeded(workstreamId: RecordIdRef): Promise<WorkstreamRecord> {
|
|
@@ -277,6 +229,7 @@ export interface WorkstreamTurnParams {
|
|
|
277
229
|
inputMessage: ChatMessage
|
|
278
230
|
persistInputMessage?: boolean
|
|
279
231
|
abortSignal?: AbortSignal
|
|
232
|
+
streamId?: string
|
|
280
233
|
}
|
|
281
234
|
|
|
282
235
|
export interface WorkstreamApprovalContinuationParams {
|
|
@@ -287,6 +240,7 @@ export interface WorkstreamApprovalContinuationParams {
|
|
|
287
240
|
userName?: string | null
|
|
288
241
|
approvalMessages: ChatMessage[]
|
|
289
242
|
abortSignal?: AbortSignal
|
|
243
|
+
streamId?: string
|
|
290
244
|
}
|
|
291
245
|
|
|
292
246
|
export type WorkstreamRunCoreParams = {
|
|
@@ -296,9 +250,11 @@ export type WorkstreamRunCoreParams = {
|
|
|
296
250
|
userRef: RecordIdRef
|
|
297
251
|
userName?: string | null
|
|
298
252
|
abortSignal?: AbortSignal
|
|
253
|
+
streamId?: string
|
|
299
254
|
} & (
|
|
300
255
|
| { kind: 'userTurn'; inputMessage: ChatMessage; persistInputMessage?: boolean }
|
|
301
256
|
| { kind: 'approvalContinuation'; approvalMessages: ChatMessage[] }
|
|
257
|
+
| { kind: 'nativeToolApprovalTurn'; approvalMessages: ChatMessage[] }
|
|
302
258
|
)
|
|
303
259
|
|
|
304
260
|
export interface PreparedWorkstreamTurn {
|
|
@@ -364,7 +320,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
364
320
|
|
|
365
321
|
let inputMessage: ChatMessage | undefined
|
|
366
322
|
const shouldPersistInputMessage = params.kind === 'userTurn' ? params.persistInputMessage !== false : false
|
|
367
|
-
const shouldProcessPostRunSideEffects =
|
|
323
|
+
const shouldProcessPostRunSideEffects =
|
|
324
|
+
params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn' || shouldPersistInputMessage
|
|
368
325
|
if (params.kind === 'userTurn') {
|
|
369
326
|
inputMessage = hydrateMessageFileUrls(withMessageCreatedAt(params.inputMessage))
|
|
370
327
|
if (inputMessage.role !== 'user') {
|
|
@@ -383,7 +340,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
383
340
|
throw new WorkstreamTurnError('A chat run is already active.', 409)
|
|
384
341
|
}
|
|
385
342
|
|
|
386
|
-
if (params.kind === 'approvalContinuation') {
|
|
343
|
+
if (params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn') {
|
|
387
344
|
const approvedAssistantMessage = [...params.approvalMessages]
|
|
388
345
|
.reverse()
|
|
389
346
|
.find((m) => m.role === 'assistant' && hasApprovalRespondedParts(m))
|
|
@@ -395,9 +352,10 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
395
352
|
|
|
396
353
|
const workspacePromise = workspaceProvider ? workspaceProvider.getWorkspace(orgRef) : Promise.resolve({})
|
|
397
354
|
const initialWorkstreamState = parsePersistedWorkstreamState(workstreamRecord.state)
|
|
355
|
+
const persistedCompactionCursor = toOptionalTrimmedString(workstreamRecord.lastCompactedMessageId) ?? undefined
|
|
398
356
|
const persistedLiveHistoryPromise = workstreamMessageService.listMessagesAfterCursor(
|
|
399
357
|
workstreamRef,
|
|
400
|
-
|
|
358
|
+
persistedCompactionCursor,
|
|
401
359
|
)
|
|
402
360
|
const persistedRecentHistoryPromise = workstreamMessageService.listRecentMessages(workstreamRef, 64)
|
|
403
361
|
|
|
@@ -595,11 +553,38 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
595
553
|
disabled: onboardingActive,
|
|
596
554
|
loadPlan: async () => await executionPlanService.getActivePlanForWorkstream(workstreamRef),
|
|
597
555
|
})
|
|
556
|
+
const getExecutionPlan = async () => await executionPlanInstructionSectionCache.getPlan()
|
|
598
557
|
const getExecutionPlanInstructionSections = async (): Promise<string[] | undefined> =>
|
|
599
558
|
await executionPlanInstructionSectionCache.getSections()
|
|
600
559
|
const invalidateExecutionPlanInstructionSections = () => {
|
|
601
560
|
executionPlanInstructionSectionCache.invalidate()
|
|
602
561
|
}
|
|
562
|
+
const getWorkstreamStateSection = async (): Promise<string | undefined> => {
|
|
563
|
+
const executionPlan = await getExecutionPlan()
|
|
564
|
+
return contextCompactionRuntime.formatWorkstreamStateForPrompt(
|
|
565
|
+
stripExecutionPlanFieldsFromWorkstreamState(workstreamState, Boolean(executionPlan)),
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
const respondedBy = recordIdToString(userRef, TABLES.USER)
|
|
569
|
+
if (params.kind === 'approvalContinuation') {
|
|
570
|
+
const appliedApproval = await executionPlanService.applyApprovalResponseFromMessages({
|
|
571
|
+
workstreamId: workstreamRef,
|
|
572
|
+
approvalMessages: params.approvalMessages,
|
|
573
|
+
respondedBy,
|
|
574
|
+
})
|
|
575
|
+
if (appliedApproval) {
|
|
576
|
+
invalidateExecutionPlanInstructionSections()
|
|
577
|
+
}
|
|
578
|
+
} else if (userMessage) {
|
|
579
|
+
const appliedHumanInput = await executionPlanService.applyHumanInputFromUserMessage({
|
|
580
|
+
workstreamId: workstreamRef,
|
|
581
|
+
message: userMessage,
|
|
582
|
+
respondedBy,
|
|
583
|
+
})
|
|
584
|
+
if (appliedHumanInput) {
|
|
585
|
+
invalidateExecutionPlanInstructionSections()
|
|
586
|
+
}
|
|
587
|
+
}
|
|
603
588
|
const preSeededMemoriesByAgent = new Map<string, string | undefined>()
|
|
604
589
|
const getPreSeededMemoriesSection = async (agentId: string): Promise<string | undefined> => {
|
|
605
590
|
if (preSeededMemoriesByAgent.has(agentId)) {
|
|
@@ -630,9 +615,12 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
630
615
|
return section
|
|
631
616
|
}
|
|
632
617
|
|
|
633
|
-
const
|
|
618
|
+
const persistedCompactionSummary =
|
|
619
|
+
persistedCompactionCursor && typeof workstreamRecord.compactionSummary === 'string'
|
|
620
|
+
? workstreamRecord.compactionSummary
|
|
621
|
+
: ''
|
|
634
622
|
const messagesForContext = userMessage ? upsertChatHistoryMessage(liveHistory, userMessage) : liveHistory
|
|
635
|
-
let currentMessages = contextCompactionRuntime.prependSummaryMessage(
|
|
623
|
+
let currentMessages = contextCompactionRuntime.prependSummaryMessage(persistedCompactionSummary, messagesForContext)
|
|
636
624
|
const referenceUserMessageId = referenceUserMessage?.id ?? ''
|
|
637
625
|
const listReadableUploads = (extraMessages: ChatMessage[] = []) =>
|
|
638
626
|
listReadableUploadsFromChatMessages({
|
|
@@ -654,6 +642,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
654
642
|
const serverRunId = Bun.randomUUIDv7()
|
|
655
643
|
const runAbort = createServerRunAbortController(params.abortSignal)
|
|
656
644
|
await workstreamService.setActiveRunId(workstreamRef, serverRunId)
|
|
645
|
+
if (params.streamId) {
|
|
646
|
+
await workstreamService.setActiveStreamId(workstreamRef, params.streamId)
|
|
647
|
+
}
|
|
657
648
|
chatRunRegistry.register(serverRunId, runAbort.controller)
|
|
658
649
|
|
|
659
650
|
try {
|
|
@@ -745,7 +736,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
745
736
|
preSeededMemoriesSection: await getPreSeededMemoriesSection(resolvedAgentId),
|
|
746
737
|
retrievedKnowledgeSection,
|
|
747
738
|
workstreamMemoryBlock: memoryBlock,
|
|
748
|
-
workstreamStateSection:
|
|
739
|
+
workstreamStateSection: await getWorkstreamStateSection(),
|
|
749
740
|
learnedSkillsSection: await getLearnedSkillsSection(resolvedAgentId),
|
|
750
741
|
additionalInstructionSections: mergeInstructionSections(
|
|
751
742
|
executionPlanInstructionSections,
|
|
@@ -811,8 +802,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
811
802
|
},
|
|
812
803
|
}) as ReadableStream<ChatStreamChunk>
|
|
813
804
|
const reader = uiStream.getReader()
|
|
814
|
-
let liveTrackerState: WorkstreamState = workstreamState ?? parseWorkstreamState(null)
|
|
815
|
-
const liveTrackedToolCalls = new Map<string, string>()
|
|
816
805
|
try {
|
|
817
806
|
for (;;) {
|
|
818
807
|
const { done, value } = await reader.read()
|
|
@@ -820,71 +809,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
820
809
|
if (streamParams.writer) {
|
|
821
810
|
streamParams.writer.write(value)
|
|
822
811
|
}
|
|
823
|
-
if (value.type === 'tool-input-available' && value.toolName === 'inspectWebsite') {
|
|
824
|
-
const title = buildInspectWebsiteTrackerTitle(value.input)
|
|
825
|
-
liveTrackedToolCalls.set(value.toolCallId, title)
|
|
826
|
-
liveTrackerState = emitTransientWorkstreamTrackerState({
|
|
827
|
-
writer: streamParams.writer,
|
|
828
|
-
workstreamRecord,
|
|
829
|
-
existingState: liveTrackerState,
|
|
830
|
-
delta: {
|
|
831
|
-
taskUpdates: [
|
|
832
|
-
{
|
|
833
|
-
title,
|
|
834
|
-
status: 'in-progress',
|
|
835
|
-
owner: streamParams.agentId,
|
|
836
|
-
externalId: value.toolCallId,
|
|
837
|
-
sourceMessageIds: [],
|
|
838
|
-
},
|
|
839
|
-
],
|
|
840
|
-
},
|
|
841
|
-
})
|
|
842
|
-
}
|
|
843
|
-
if (
|
|
844
|
-
value.type === 'tool-output-available' &&
|
|
845
|
-
value.preliminary !== true &&
|
|
846
|
-
liveTrackedToolCalls.has(value.toolCallId)
|
|
847
|
-
) {
|
|
848
|
-
liveTrackerState = emitTransientWorkstreamTrackerState({
|
|
849
|
-
writer: streamParams.writer,
|
|
850
|
-
workstreamRecord,
|
|
851
|
-
existingState: liveTrackerState,
|
|
852
|
-
delta: {
|
|
853
|
-
taskUpdates: [
|
|
854
|
-
{
|
|
855
|
-
title: liveTrackedToolCalls.get(value.toolCallId) ?? 'Inspect website intelligence',
|
|
856
|
-
status: 'done',
|
|
857
|
-
owner: streamParams.agentId,
|
|
858
|
-
externalId: value.toolCallId,
|
|
859
|
-
sourceMessageIds: [],
|
|
860
|
-
},
|
|
861
|
-
],
|
|
862
|
-
},
|
|
863
|
-
})
|
|
864
|
-
liveTrackedToolCalls.delete(value.toolCallId)
|
|
865
|
-
}
|
|
866
|
-
if (
|
|
867
|
-
(value.type === 'tool-output-error' || value.type === 'tool-output-denied') &&
|
|
868
|
-
liveTrackedToolCalls.has(value.toolCallId)
|
|
869
|
-
) {
|
|
870
|
-
liveTrackerState = emitTransientWorkstreamTrackerState({
|
|
871
|
-
writer: streamParams.writer,
|
|
872
|
-
workstreamRecord,
|
|
873
|
-
existingState: liveTrackerState,
|
|
874
|
-
delta: {
|
|
875
|
-
taskUpdates: [
|
|
876
|
-
{
|
|
877
|
-
title: liveTrackedToolCalls.get(value.toolCallId) ?? 'Inspect website intelligence',
|
|
878
|
-
status: 'blocked',
|
|
879
|
-
owner: streamParams.agentId,
|
|
880
|
-
externalId: value.toolCallId,
|
|
881
|
-
sourceMessageIds: [],
|
|
882
|
-
},
|
|
883
|
-
],
|
|
884
|
-
},
|
|
885
|
-
})
|
|
886
|
-
liveTrackedToolCalls.delete(value.toolCallId)
|
|
887
|
-
}
|
|
888
812
|
}
|
|
889
813
|
} finally {
|
|
890
814
|
reader.releaseLock()
|
|
@@ -959,6 +883,12 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
959
883
|
)
|
|
960
884
|
}
|
|
961
885
|
|
|
886
|
+
// Execution-plan approval continuations mutate plan state and persist the approval message,
|
|
887
|
+
// but they do not begin a new visible agent turn.
|
|
888
|
+
if (params.kind === 'approvalContinuation') {
|
|
889
|
+
return
|
|
890
|
+
}
|
|
891
|
+
|
|
962
892
|
const consultSpecialistTool = createTool({
|
|
963
893
|
description: 'Consult one specialist teammate for domain-specific guidance before replying to the user.',
|
|
964
894
|
inputSchema: ConsultSpecialistArgsSchema,
|
|
@@ -1003,7 +933,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1003
933
|
preSeededMemoriesSection: await getPreSeededMemoriesSection(agentId),
|
|
1004
934
|
retrievedKnowledgeSection,
|
|
1005
935
|
workstreamMemoryBlock: specialistMemoryBlock,
|
|
1006
|
-
workstreamStateSection:
|
|
936
|
+
workstreamStateSection: await getWorkstreamStateSection(),
|
|
1007
937
|
learnedSkillsSection: await getLearnedSkillsSection(agentId),
|
|
1008
938
|
additionalInstructionSections: mergeInstructionSections(
|
|
1009
939
|
specialistExecutionPlanInstructionSections,
|
|
@@ -1115,17 +1045,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1115
1045
|
throw new WorkstreamTurnError('Direct workstreams require an assigned agent.', 400)
|
|
1116
1046
|
}
|
|
1117
1047
|
await runVisibleAgent({ agentId: workstream.agentId, mode: 'direct' })
|
|
1118
|
-
} else if (params.kind === 'userTurn') {
|
|
1119
|
-
await runVisibleAgent({
|
|
1120
|
-
agentId: visibleWorkstreamAgentId ?? 'chief',
|
|
1121
|
-
mode: 'workstreamMode',
|
|
1122
|
-
skills: coreWorkstreamProfile?.skills ? [...coreWorkstreamProfile.skills] : undefined,
|
|
1123
|
-
additionalInstructionSections: mergeInstructionSections(coreInstructionSections, hookInstructionSections),
|
|
1124
|
-
extraTools: {
|
|
1125
|
-
[CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool,
|
|
1126
|
-
...(teamThinkTool ? { [CONSULT_TEAM_TOOL_NAME]: teamThinkTool } : {}),
|
|
1127
|
-
},
|
|
1128
|
-
})
|
|
1129
1048
|
} else {
|
|
1130
1049
|
await runVisibleAgent({
|
|
1131
1050
|
agentId: visibleWorkstreamAgentId ?? 'chief',
|
|
@@ -1141,9 +1060,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1141
1060
|
} finally {
|
|
1142
1061
|
try {
|
|
1143
1062
|
const latestWorkstreamRecord = await workstreamService.getById(workstreamRef)
|
|
1144
|
-
const latestPersistedState =
|
|
1145
|
-
? WorkstreamStateSchema.parse(latestWorkstreamRecord.state)
|
|
1146
|
-
: null
|
|
1063
|
+
const latestPersistedState = parsePersistedWorkstreamState(latestWorkstreamRecord.state)
|
|
1147
1064
|
|
|
1148
1065
|
await finalizeTurnRun({
|
|
1149
1066
|
serverRunId,
|
|
@@ -1165,60 +1082,17 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1165
1082
|
unregisterRun: (runId) => chatRunRegistry.unregister(runId),
|
|
1166
1083
|
clearActiveRunId: (runId) => workstreamService.clearActiveRunIdIfMatches(workstreamRef, runId),
|
|
1167
1084
|
disposeAbort: () => runAbort.dispose(),
|
|
1085
|
+
activeStreamId: params.streamId,
|
|
1086
|
+
clearActiveStreamId: (streamId) =>
|
|
1087
|
+
workstreamService.clearActiveStreamIdIfMatches(workstreamRef, streamId),
|
|
1168
1088
|
})
|
|
1169
1089
|
|
|
1170
|
-
let trackerAwareWorkstreamRecord = latestWorkstreamRecord
|
|
1171
|
-
let trackerAwarePersistedState = latestPersistedState
|
|
1172
|
-
|
|
1173
|
-
if (!onboardingActive && allAssistantMessages.length > 0) {
|
|
1174
|
-
const activeExecutionPlan = await executionPlanService.getActivePlanForWorkstream(workstreamRef)
|
|
1175
|
-
const trackerUpdated = await updateWorkstreamChangeTracker({
|
|
1176
|
-
workstreamId: workstreamRef,
|
|
1177
|
-
title: latestWorkstreamRecord.title ?? workstream.title,
|
|
1178
|
-
mode: workstream.mode,
|
|
1179
|
-
...(workstream.coreType ? { coreType: workstream.coreType } : {}),
|
|
1180
|
-
...(visibleWorkstreamAgentId ? { visibleAgentId: visibleWorkstreamAgentId } : {}),
|
|
1181
|
-
hasActiveExecutionPlan: activeExecutionPlan !== null,
|
|
1182
|
-
previousSummary:
|
|
1183
|
-
typeof latestWorkstreamRecord.chatSummary === 'string' ? latestWorkstreamRecord.chatSummary : null,
|
|
1184
|
-
existingState: latestPersistedState,
|
|
1185
|
-
userMessageText: referenceUserMessage ? extractMessageText(referenceUserMessage).trim() : null,
|
|
1186
|
-
assistantMessages: allAssistantMessages
|
|
1187
|
-
.map((message) => {
|
|
1188
|
-
const text = extractTrackerMessageText(message).trim()
|
|
1189
|
-
if (!text) return null
|
|
1190
|
-
const label =
|
|
1191
|
-
typeof message.metadata?.agentName === 'string' && message.metadata.agentName.trim().length > 0
|
|
1192
|
-
? message.metadata.agentName.trim()
|
|
1193
|
-
: typeof message.metadata?.agentId === 'string' && message.metadata.agentId.trim().length > 0
|
|
1194
|
-
? message.metadata.agentId.trim()
|
|
1195
|
-
: (visibleWorkstreamAgentId ?? 'Assistant')
|
|
1196
|
-
return { label, text }
|
|
1197
|
-
})
|
|
1198
|
-
.filter((message): message is { label: string; text: string } => Boolean(message)),
|
|
1199
|
-
})
|
|
1200
|
-
|
|
1201
|
-
if (trackerUpdated) {
|
|
1202
|
-
const trackedWorkstreamRecord = await workstreamService.getWorkstreamRecord(workstreamRef)
|
|
1203
|
-
trackerAwareWorkstreamRecord = trackedWorkstreamRecord
|
|
1204
|
-
trackerAwarePersistedState = parsePersistedWorkstreamState(trackedWorkstreamRecord.state)
|
|
1205
|
-
if (writer) {
|
|
1206
|
-
writer.write({
|
|
1207
|
-
type: 'data-workstreamTracker',
|
|
1208
|
-
data: workstreamService.toPublicWorkstreamDetail(trackedWorkstreamRecord)
|
|
1209
|
-
.workstreamState as unknown as Record<string, unknown>,
|
|
1210
|
-
transient: true,
|
|
1211
|
-
})
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
1090
|
if (allAssistantMessages.length > 0 && shouldProcessPostRunSideEffects) {
|
|
1217
1091
|
const turnCount = await workstreamService.incrementTurnCount(workstreamRef)
|
|
1218
1092
|
const agentMessages = buildAgentHistoryMessages(allAssistantMessages)
|
|
1219
|
-
const historyMessagesForMemory =
|
|
1093
|
+
const historyMessagesForMemory = appendPersistedWorkstreamContextToHistoryMessages(
|
|
1220
1094
|
toHistoryMessages(recentHistory),
|
|
1221
|
-
{
|
|
1095
|
+
{ compactionSummary: latestWorkstreamRecord.compactionSummary, persistedState: latestPersistedState },
|
|
1222
1096
|
)
|
|
1223
1097
|
|
|
1224
1098
|
const userMessageText = referenceUserMessage ? extractMessageText(referenceUserMessage).trim() : ''
|
|
@@ -1283,7 +1157,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1283
1157
|
agentId: visibleWorkstreamAgentId ?? 'chief',
|
|
1284
1158
|
agentName: agentDisplayNames[visibleWorkstreamAgentId ?? 'chief'],
|
|
1285
1159
|
workstreamId: workstreamIdString,
|
|
1286
|
-
workstreamTitle:
|
|
1160
|
+
workstreamTitle: latestWorkstreamRecord.title ?? workstream.title,
|
|
1287
1161
|
workstreamMode: workstream.mode,
|
|
1288
1162
|
...(workstream.coreType ? { coreType: workstream.coreType } : {}),
|
|
1289
1163
|
userMessageText,
|
|
@@ -1350,8 +1224,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1350
1224
|
onboardingActive,
|
|
1351
1225
|
referenceUserMessage,
|
|
1352
1226
|
assistantMessages: allAssistantMessages,
|
|
1353
|
-
latestWorkstreamRecord
|
|
1354
|
-
latestPersistedState
|
|
1227
|
+
latestWorkstreamRecord,
|
|
1228
|
+
latestPersistedState,
|
|
1355
1229
|
context: buildContextResult,
|
|
1356
1230
|
})
|
|
1357
1231
|
}
|
|
@@ -21,6 +21,18 @@ export async function createWorkstreamApprovalContinuationStream(params: Workstr
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export async function createWorkstreamNativeToolApprovalStream(params: WorkstreamApprovalContinuationParams) {
|
|
25
|
+
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'nativeToolApprovalTurn' })
|
|
26
|
+
|
|
27
|
+
return createUIMessageStream<ChatMessage>({
|
|
28
|
+
originalMessages: prepared.originalMessages,
|
|
29
|
+
onError: (error) => (error instanceof Error ? error.message : 'Native tool approval stream failed.'),
|
|
30
|
+
execute: async ({ writer }) => {
|
|
31
|
+
await prepared.run(writer)
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export async function createWorkstreamTurnStream(params: WorkstreamTurnParams) {
|
|
25
37
|
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'userTurn' })
|
|
26
38
|
|