@lota-sdk/core 0.1.14 → 0.1.16
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_identity.surql +0 -2
- package/infrastructure/schema/01_memory.surql +1 -1
- package/infrastructure/schema/02_execution_plan.surql +62 -1
- package/infrastructure/schema/03_learned_skill.surql +1 -1
- package/infrastructure/schema/06_playbook.surql +25 -0
- package/infrastructure/schema/07_institutional_memory.surql +13 -0
- package/infrastructure/schema/08_quality_metrics.surql +17 -0
- package/package.json +9 -8
- package/src/ai/definitions.ts +80 -2
- package/src/ai/embedding-cache.ts +7 -6
- package/src/ai/index.ts +0 -1
- package/src/bifrost/bifrost.ts +14 -14
- package/src/config/agent-defaults.ts +32 -22
- package/src/config/agent-types.ts +11 -0
- package/src/config/constants.ts +2 -14
- package/src/config/debug-logger.ts +5 -1
- package/src/config/index.ts +3 -0
- package/src/config/logger.ts +7 -9
- package/src/config/model-constants.ts +16 -34
- package/src/config/search.ts +1 -15
- package/src/create-runtime.ts +453 -0
- package/src/db/cursor-pagination.ts +3 -6
- package/src/db/index.ts +2 -0
- package/src/db/memory-store.rows.ts +7 -7
- package/src/db/memory-store.ts +24 -24
- package/src/db/memory.ts +18 -16
- package/src/db/schema-fingerprint.ts +1 -0
- package/src/db/service.ts +193 -122
- package/src/db/startup.ts +9 -13
- package/src/db/surreal-mutation.ts +43 -0
- package/src/db/tables.ts +7 -0
- package/src/db/workstream-message-row.ts +15 -0
- package/src/embeddings/provider.ts +1 -1
- package/src/index.ts +1 -1
- package/src/queues/context-compaction.queue.ts +17 -52
- package/src/queues/delayed-node-promotion.queue.ts +41 -0
- package/src/queues/document-processor.queue.ts +7 -7
- package/src/queues/index.ts +3 -0
- package/src/queues/memory-consolidation.queue.ts +18 -54
- package/src/queues/plan-scheduler.queue.ts +97 -0
- package/src/queues/post-chat-memory.queue.ts +15 -60
- package/src/queues/queue-factory.ts +100 -0
- package/src/queues/recent-activity-title-refinement.queue.ts +15 -54
- package/src/queues/regular-chat-memory-digest.queue.ts +16 -55
- package/src/queues/skill-extraction.queue.ts +15 -50
- package/src/queues/workstream-title-generation.queue.ts +15 -51
- package/src/redis/connection.ts +12 -3
- package/src/redis/index.ts +2 -1
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +41 -8
- package/src/redis/stream-context.ts +11 -0
- package/src/runtime/agent-runtime-policy.ts +106 -21
- package/src/runtime/agent-stream-helpers.ts +2 -1
- package/src/runtime/approval-continuation.ts +12 -6
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction-runtime.ts +7 -5
- package/src/runtime/context-compaction.ts +40 -97
- package/src/runtime/execution-plan.ts +23 -19
- package/src/runtime/graph-designer.ts +15 -0
- package/src/runtime/helper-model.ts +10 -196
- package/src/runtime/index.ts +14 -1
- package/src/runtime/llm-content.ts +1 -1
- package/src/runtime/memory-block.ts +11 -12
- package/src/runtime/memory-pipeline.ts +26 -10
- package/src/runtime/plugin-resolution.ts +35 -0
- package/src/runtime/plugin-types.ts +73 -1
- package/src/runtime/retrieval-adapters.ts +1 -1
- package/src/runtime/runtime-config.ts +25 -12
- package/src/runtime/runtime-extensions.ts +91 -15
- package/src/runtime/runtime-worker-registry.ts +6 -0
- package/src/runtime/team-consultation-orchestrator.ts +45 -28
- package/src/runtime/team-consultation-prompts.ts +11 -2
- package/src/runtime/title-helpers.ts +11 -4
- package/src/runtime/workstream-chat-helpers.ts +6 -7
- package/src/runtime/workstream-routing-policy.ts +0 -30
- package/src/runtime/workstream-state.ts +17 -7
- package/src/services/adaptive-playbook.service.ts +152 -0
- package/src/services/agent-executor.service.ts +293 -0
- package/src/services/artifact-provenance.service.ts +172 -0
- package/src/services/attachment.service.ts +7 -12
- package/src/services/context-compaction.service.ts +75 -58
- package/src/services/context-enrichment.service.ts +33 -0
- package/src/services/coordination-registry.service.ts +117 -0
- package/src/services/document-chunk.service.ts +38 -33
- package/src/services/domain-agent-executor.service.ts +71 -0
- package/src/services/execution-plan.service.ts +271 -50
- package/src/services/feedback-loop.service.ts +96 -0
- package/src/services/global-orchestrator.service.ts +148 -0
- package/src/services/index.ts +26 -0
- package/src/services/institutional-memory.service.ts +145 -0
- package/src/services/learned-skill.service.ts +30 -15
- package/src/services/memory-assessment.service.ts +3 -2
- package/src/services/{memory.utils.ts → memory-utils.ts} +4 -13
- package/src/services/memory.service.ts +55 -69
- package/src/services/monitoring-window.service.ts +86 -0
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/node-workspace.service.ts +155 -0
- package/src/services/notification.service.ts +39 -0
- package/src/services/organization-member.service.ts +12 -5
- package/src/services/organization.service.ts +5 -5
- package/src/services/ownership-dispatcher.service.ts +403 -0
- package/src/services/plan-approval.service.ts +1 -1
- package/src/services/plan-artifact.service.ts +1 -0
- package/src/services/plan-builder.service.ts +1 -0
- package/src/services/plan-checkpoint.service.ts +30 -2
- package/src/services/plan-compiler.service.ts +5 -0
- package/src/services/plan-coordination.service.ts +152 -0
- package/src/services/plan-cycle.service.ts +284 -0
- package/src/services/plan-deadline.service.ts +287 -0
- package/src/services/plan-executor.service.ts +386 -58
- package/src/services/plan-helpers.ts +15 -0
- package/src/services/plan-run.service.ts +41 -7
- package/src/services/plan-scheduler.service.ts +240 -0
- package/src/services/plan-template.service.ts +117 -0
- package/src/services/plan-validator.service.ts +87 -20
- package/src/services/plan-workspace.service.ts +83 -0
- package/src/services/playbook-registry.service.ts +67 -0
- package/src/services/plugin-executor.service.ts +103 -0
- package/src/services/quality-metrics.service.ts +132 -0
- package/src/services/recent-activity-title.service.ts +3 -10
- package/src/services/recent-activity.service.ts +33 -43
- package/src/services/skill-resolver.service.ts +19 -0
- package/src/services/system-executor.service.ts +105 -0
- package/src/services/workstream-message.service.ts +29 -41
- package/src/services/workstream-plan-registry.service.ts +22 -0
- package/src/services/workstream-title.service.ts +3 -9
- package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts} +428 -373
- package/src/services/workstream-turn.ts +2 -2
- package/src/services/workstream.service.ts +55 -65
- package/src/services/workstream.types.ts +10 -19
- package/src/services/write-intent-validator.service.ts +81 -0
- package/src/storage/attachment-parser.ts +1 -1
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/storage/{attachments.utils.ts → attachment-utils.ts} +2 -5
- package/src/storage/generated-document-storage.service.ts +3 -2
- package/src/storage/index.ts +2 -2
- package/src/system-agents/{context-compacter.agent.ts → context-compaction.agent.ts} +4 -4
- package/src/system-agents/delegated-agent-factory.ts +5 -2
- package/src/system-agents/index.ts +8 -0
- package/src/system-agents/memory-reranker.agent.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
- package/src/tools/execution-plan.tool.ts +17 -19
- package/src/tools/fetch-webpage.tool.ts +20 -18
- package/src/tools/index.ts +2 -3
- package/src/tools/read-file-parts.tool.ts +1 -1
- package/src/tools/search-web.tool.ts +18 -15
- package/src/tools/{search-tools.ts → search.tool.ts} +1 -1
- package/src/tools/team-think.tool.ts +14 -8
- package/src/tools/{tool-contract.ts → tool-contracts.ts} +9 -2
- package/src/utils/async.ts +3 -2
- package/src/utils/date-time.ts +4 -32
- package/src/utils/env.ts +8 -0
- package/src/utils/errors.ts +47 -0
- package/src/utils/hono-error-handler.ts +1 -2
- package/src/utils/index.ts +19 -2
- package/src/utils/string.ts +128 -1
- package/src/workers/bootstrap.ts +2 -2
- package/src/workers/index.ts +1 -0
- package/src/workers/memory-consolidation.worker.ts +12 -12
- package/src/workers/regular-chat-memory-digest.helpers.ts +2 -7
- package/src/workers/regular-chat-memory-digest.runner.ts +11 -105
- package/src/workers/skill-extraction.runner.ts +8 -102
- package/src/workers/utils/file-section-chunker.ts +6 -3
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/sandbox-error.ts +11 -2
- package/src/workers/utils/workstream-message-query.ts +97 -0
- package/src/workers/worker-utils.ts +6 -2
- package/src/runtime/retrieval-pipeline.ts +0 -3
- package/src/runtime.ts +0 -387
- package/src/tools/log-hello-world.tool.ts +0 -17
- package/src/utils/error.ts +0 -10
- /package/src/services/{context-compaction-runtime.ts → context-compaction-runtime.singleton.ts} +0 -0
- /package/src/storage/{attachments.types.ts → attachment-types.ts} +0 -0
package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts}
RENAMED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
WORKSTREAM,
|
|
2
3
|
baseChatMessageSchema,
|
|
3
4
|
CONSULT_SPECIALIST_TOOL_NAME,
|
|
4
5
|
CONSULT_TEAM_TOOL_NAME,
|
|
5
6
|
ConsultSpecialistArgsSchema,
|
|
6
|
-
|
|
7
|
+
dataPartsSchemas,
|
|
7
8
|
messageMetadataSchema,
|
|
8
9
|
toTimestamp,
|
|
9
10
|
withMessageCreatedAt,
|
|
10
11
|
} from '@lota-sdk/shared'
|
|
11
12
|
import type { ChatMessage, MessageMetadata } from '@lota-sdk/shared'
|
|
12
13
|
import { convertToModelMessages, readUIMessageStream, stepCountIs, tool as createTool, validateUIMessages } from 'ai'
|
|
13
|
-
import type { PrepareStepFunction, StopCondition, ToolSet, UIMessageStreamWriter } from 'ai'
|
|
14
|
+
import type { PrepareStepFunction, StopCondition, ToolLoopAgent, ToolSet, UIMessageStreamWriter } from 'ai'
|
|
14
15
|
import type { z } from 'zod'
|
|
15
16
|
|
|
17
|
+
import type { CoreWorkstreamProfile } from '../config/agent-defaults'
|
|
16
18
|
import {
|
|
17
19
|
agentDisplayNames,
|
|
18
20
|
buildAgentTools,
|
|
@@ -20,7 +22,6 @@ import {
|
|
|
20
22
|
getLeadAgentId,
|
|
21
23
|
getCoreWorkstreamProfile,
|
|
22
24
|
getAgentRuntimeConfig,
|
|
23
|
-
pluginRuntime,
|
|
24
25
|
} from '../config/agent-defaults'
|
|
25
26
|
import { lotaDebugLogger } from '../config/debug-logger'
|
|
26
27
|
import { aiLogger } from '../config/logger'
|
|
@@ -45,7 +46,7 @@ import { buildModelInputMessagesWithUploadMetadata, buildReadableUploadMetadataT
|
|
|
45
46
|
import { hasMessageContent } from '../runtime/chat-message'
|
|
46
47
|
import { waitForCompactionIfNeeded } from '../runtime/chat-run-orchestration'
|
|
47
48
|
import { parseWorkstreamState } from '../runtime/context-compaction'
|
|
48
|
-
import {
|
|
49
|
+
import { CONTEXT_WINDOW_TOKENS } from '../runtime/context-compaction-constants'
|
|
49
50
|
import { createExecutionPlanInstructionSectionCache } from '../runtime/execution-plan'
|
|
50
51
|
import { mergeInstructionSections } from '../runtime/instruction-sections'
|
|
51
52
|
import {
|
|
@@ -54,6 +55,7 @@ import {
|
|
|
54
55
|
shouldEnqueueOnboardingPostChatMemory,
|
|
55
56
|
shouldEnqueueRegularDigestForWorkstream,
|
|
56
57
|
} from '../runtime/memory-digest-policy'
|
|
58
|
+
import { buildIndexedRepositoriesContext, getPluginService } from '../runtime/plugin-resolution'
|
|
57
59
|
import { getRuntimeAdapters, getToolProviders, getTurnHooks } from '../runtime/runtime-extensions'
|
|
58
60
|
import { shouldEnqueueSkillExtraction } from '../runtime/skill-extraction-policy'
|
|
59
61
|
import { finalizeTurnRun } from '../runtime/turn-lifecycle'
|
|
@@ -81,16 +83,15 @@ import { toIsoDateTimeString } from '../utils/date-time'
|
|
|
81
83
|
import { AppError } from '../utils/errors'
|
|
82
84
|
import { attachmentService } from './attachment.service'
|
|
83
85
|
import { listReadableUploadsFromChatMessages } from './chat-attachments.service'
|
|
84
|
-
import { contextCompactionRuntime } from './context-compaction-runtime'
|
|
86
|
+
import { contextCompactionRuntime } from './context-compaction-runtime.singleton'
|
|
85
87
|
import { executionPlanService } from './execution-plan.service'
|
|
86
88
|
import { learnedSkillService } from './learned-skill.service'
|
|
87
89
|
import { memoryService } from './memory.service'
|
|
90
|
+
import { planRunService } from './plan-run.service'
|
|
88
91
|
import { recentActivityService } from './recent-activity.service'
|
|
89
92
|
import { workstreamMessageService } from './workstream-message.service'
|
|
90
93
|
import { workstreamService } from './workstream.service'
|
|
91
94
|
|
|
92
|
-
type AgentRuntimeConfig = Record<string, unknown>
|
|
93
|
-
type AgentFactory = Record<string, (...args: unknown[]) => Record<string, (...args: unknown[]) => unknown>>
|
|
94
95
|
type ChatStreamChunk = Parameters<UIMessageStreamWriter<ChatMessage>['write']>[0]
|
|
95
96
|
|
|
96
97
|
interface UIMessageStreamResult {
|
|
@@ -105,45 +106,8 @@ function hasUIMessageStream(value: unknown): value is UIMessageStreamResult {
|
|
|
105
106
|
)
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
function getPluginService(path: string[]): ((...args: unknown[]) => unknown) | undefined {
|
|
109
|
-
let current: unknown = pluginRuntime
|
|
110
|
-
let owner: unknown = undefined
|
|
111
|
-
for (const key of path) {
|
|
112
|
-
if (current === null || current === undefined || typeof current !== 'object') return undefined
|
|
113
|
-
owner = current
|
|
114
|
-
current = (current as Record<string, unknown>)[key]
|
|
115
|
-
}
|
|
116
|
-
if (typeof current !== 'function') {
|
|
117
|
-
return undefined
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return owner && typeof owner === 'object'
|
|
121
|
-
? (current as (...args: unknown[]) => unknown).bind(owner)
|
|
122
|
-
: (current as (...args: unknown[]) => unknown)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async function buildIndexedRepositoriesContext(
|
|
126
|
-
workspaceId: string,
|
|
127
|
-
): Promise<{ provideRepoTool: boolean; defaultSectionsByAgent: Record<string, unknown>; context: string }> {
|
|
128
|
-
const buildContext = getRuntimeAdapters().workstream?.buildIndexedRepositoriesContext
|
|
129
|
-
if (!buildContext) {
|
|
130
|
-
return { provideRepoTool: false, defaultSectionsByAgent: {}, context: '' }
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const context = await buildContext(workspaceId)
|
|
134
|
-
return {
|
|
135
|
-
provideRepoTool: context.provideRepoTool,
|
|
136
|
-
defaultSectionsByAgent: context.defaultSectionsByAgent,
|
|
137
|
-
context: context.context ?? '',
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
109
|
const PRESEEDED_MEMORY_LOOKUP_LIMIT = 3
|
|
142
110
|
|
|
143
|
-
function parsePersistedWorkstreamState(value: unknown): WorkstreamState | null {
|
|
144
|
-
return parseWorkstreamState(value)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
111
|
function stripExecutionPlanFieldsFromWorkstreamState(
|
|
148
112
|
state: WorkstreamState | null | undefined,
|
|
149
113
|
hasExecutionPlan: boolean,
|
|
@@ -154,7 +118,7 @@ function stripExecutionPlanFieldsFromWorkstreamState(
|
|
|
154
118
|
}
|
|
155
119
|
|
|
156
120
|
async function waitForWorkstreamCompactionIfNeeded(workstreamId: RecordIdRef): Promise<WorkstreamRecord> {
|
|
157
|
-
return
|
|
121
|
+
return waitForCompactionIfNeeded({
|
|
158
122
|
entityId: recordIdToString(workstreamId, TABLES.WORKSTREAM),
|
|
159
123
|
entityLabel: 'Workstream',
|
|
160
124
|
loadEntity: () => workstreamService.getById(workstreamId),
|
|
@@ -231,7 +195,7 @@ export interface WorkstreamTurnParams {
|
|
|
231
195
|
userRef: RecordIdRef
|
|
232
196
|
userName?: string | null
|
|
233
197
|
inputMessage: ChatMessage
|
|
234
|
-
|
|
198
|
+
skipInputMessagePersistence?: boolean
|
|
235
199
|
abortSignal?: AbortSignal
|
|
236
200
|
streamId?: string
|
|
237
201
|
}
|
|
@@ -256,7 +220,7 @@ type WorkstreamRunCoreParams = {
|
|
|
256
220
|
abortSignal?: AbortSignal
|
|
257
221
|
streamId?: string
|
|
258
222
|
} & (
|
|
259
|
-
| { kind: 'userTurn'; inputMessage: ChatMessage;
|
|
223
|
+
| { kind: 'userTurn'; inputMessage: ChatMessage; skipInputMessagePersistence?: boolean }
|
|
260
224
|
| { kind: 'approvalContinuation'; approvalMessages: ChatMessage[] }
|
|
261
225
|
| { kind: 'nativeToolApprovalTurn'; approvalMessages: ChatMessage[] }
|
|
262
226
|
)
|
|
@@ -300,6 +264,338 @@ function buildRecentActivityChatSystemTitle(params: {
|
|
|
300
264
|
return params.workstream.title.trim() || 'Workstream update'
|
|
301
265
|
}
|
|
302
266
|
|
|
267
|
+
interface StreamAgentResponseContext {
|
|
268
|
+
turnHooks: ReturnType<typeof getTurnHooks>
|
|
269
|
+
workstream: NormalizedWorkstream
|
|
270
|
+
workstreamRef: RecordIdRef
|
|
271
|
+
orgRef: RecordIdRef
|
|
272
|
+
userRef: RecordIdRef
|
|
273
|
+
userName?: string | null
|
|
274
|
+
onboardingActive: boolean
|
|
275
|
+
linearInstalled: boolean
|
|
276
|
+
githubInstalled: boolean
|
|
277
|
+
reasoningProfileName: string
|
|
278
|
+
buildContextResult: Record<string, unknown> | null
|
|
279
|
+
getExecutionPlanInstructionSections: () => Promise<string[] | undefined>
|
|
280
|
+
getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
|
|
281
|
+
getWorkstreamStateSection: () => Promise<string | undefined>
|
|
282
|
+
getLearnedSkillsSection: (agentId: string) => Promise<string | undefined>
|
|
283
|
+
promptContext: { systemWorkspaceDetails?: string }
|
|
284
|
+
retrievedKnowledgeSection: string | undefined
|
|
285
|
+
memoryBlock: string
|
|
286
|
+
hookInstructionSections: string[]
|
|
287
|
+
runAbortSignal: AbortSignal
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
interface StreamAgentResponseParams {
|
|
291
|
+
agentId: string
|
|
292
|
+
mode: 'direct' | 'fixedWorkstreamMode' | 'workstreamMode'
|
|
293
|
+
messages: ChatMessage[]
|
|
294
|
+
tools: ToolSet
|
|
295
|
+
observer: {
|
|
296
|
+
run: <T>(fn: () => T | Promise<T>) => Promise<T>
|
|
297
|
+
recordError: (error: unknown) => void
|
|
298
|
+
recordAbort: (error: unknown) => void
|
|
299
|
+
}
|
|
300
|
+
skills?: string[]
|
|
301
|
+
additionalInstructionSections?: string[]
|
|
302
|
+
writer?: UIMessageStreamWriter<ChatMessage>
|
|
303
|
+
stopWhen?: StopCondition<ToolSet> | Array<StopCondition<ToolSet>>
|
|
304
|
+
prepareStep?: PrepareStepFunction<ToolSet>
|
|
305
|
+
abortSignal?: AbortSignal
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function streamAgentResponse(
|
|
309
|
+
ctx: StreamAgentResponseContext,
|
|
310
|
+
streamParams: StreamAgentResponseParams,
|
|
311
|
+
): Promise<ChatMessage> {
|
|
312
|
+
const agentTimer = lotaDebugLogger.timer(`agent:${streamParams.agentId}`)
|
|
313
|
+
const executionPlanInstructionSections = await ctx.getExecutionPlanInstructionSections()
|
|
314
|
+
agentTimer.step('get-execution-plan')
|
|
315
|
+
const agentResolution = asRecord(
|
|
316
|
+
await ctx.turnHooks.resolveAgent?.({
|
|
317
|
+
agentId: streamParams.agentId,
|
|
318
|
+
mode: streamParams.mode,
|
|
319
|
+
workstream: ctx.workstream,
|
|
320
|
+
workstreamRef: ctx.workstreamRef,
|
|
321
|
+
orgRef: ctx.orgRef,
|
|
322
|
+
userRef: ctx.userRef,
|
|
323
|
+
userName: ctx.userName,
|
|
324
|
+
onboardingActive: ctx.onboardingActive,
|
|
325
|
+
linearInstalled: ctx.linearInstalled,
|
|
326
|
+
githubInstalled: ctx.githubInstalled,
|
|
327
|
+
reasoningProfile: ctx.reasoningProfileName,
|
|
328
|
+
skills: streamParams.skills,
|
|
329
|
+
additionalInstructionSections: streamParams.additionalInstructionSections,
|
|
330
|
+
context: ctx.buildContextResult,
|
|
331
|
+
}),
|
|
332
|
+
)
|
|
333
|
+
agentTimer.step('hook:resolveAgent')
|
|
334
|
+
const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? streamParams.agentId
|
|
335
|
+
const [preSeededMemoriesSection, workstreamStateSection, learnedSkillsSection] = await Promise.all([
|
|
336
|
+
ctx.getPreSeededMemoriesSection(resolvedAgentId),
|
|
337
|
+
ctx.getWorkstreamStateSection(),
|
|
338
|
+
ctx.getLearnedSkillsSection(resolvedAgentId),
|
|
339
|
+
])
|
|
340
|
+
agentTimer.step('parallel-fetch(memories+state+skills)')
|
|
341
|
+
const config = getAgentRuntimeConfig({
|
|
342
|
+
agentId: resolvedAgentId,
|
|
343
|
+
workstreamMode: ctx.workstream.mode,
|
|
344
|
+
mode: streamParams.mode,
|
|
345
|
+
skills: streamParams.skills,
|
|
346
|
+
onboardingActive: ctx.onboardingActive,
|
|
347
|
+
linearInstalled: ctx.linearInstalled,
|
|
348
|
+
reasoningProfile: ctx.reasoningProfileName,
|
|
349
|
+
systemWorkspaceDetails: ctx.promptContext.systemWorkspaceDetails,
|
|
350
|
+
preSeededMemoriesSection,
|
|
351
|
+
retrievedKnowledgeSection: ctx.retrievedKnowledgeSection,
|
|
352
|
+
workstreamMemoryBlock: ctx.memoryBlock,
|
|
353
|
+
workstreamStateSection,
|
|
354
|
+
learnedSkillsSection,
|
|
355
|
+
additionalInstructionSections: mergeInstructionSections(
|
|
356
|
+
executionPlanInstructionSections,
|
|
357
|
+
streamParams.additionalInstructionSections,
|
|
358
|
+
ctx.hookInstructionSections,
|
|
359
|
+
readInstructionSections(agentResolution?.additionalInstructionSections),
|
|
360
|
+
optionalInstructionSection(agentResolution?.extraInstructions),
|
|
361
|
+
),
|
|
362
|
+
context: ctx.buildContextResult,
|
|
363
|
+
}) as Record<string, unknown>
|
|
364
|
+
agentTimer.step('build-agent-config')
|
|
365
|
+
const modelMessages = await convertToModelMessages(streamParams.messages, { ignoreIncompleteToolCalls: true })
|
|
366
|
+
agentTimer.step('convert-model-messages')
|
|
367
|
+
const agent = createAgent[config.id as string]({
|
|
368
|
+
mode: streamParams.mode,
|
|
369
|
+
tools: streamParams.tools,
|
|
370
|
+
extraInstructions: config.extraInstructions,
|
|
371
|
+
stopWhen: (agentResolution?.stopWhen as StopCondition<ToolSet> | Array<StopCondition<ToolSet>> | undefined) ??
|
|
372
|
+
streamParams.stopWhen ?? [stepCountIs(config.maxSteps as number)],
|
|
373
|
+
prepareStep: (agentResolution?.prepareStep as PrepareStepFunction<ToolSet> | undefined) ?? streamParams.prepareStep,
|
|
374
|
+
}) as ToolLoopAgent<never, ToolSet>
|
|
375
|
+
const agentAbortSignal = streamParams.abortSignal ?? ctx.runAbortSignal
|
|
376
|
+
agentTimer.step('agent-construction')
|
|
377
|
+
|
|
378
|
+
let result: unknown
|
|
379
|
+
try {
|
|
380
|
+
result = await streamParams.observer.run(() =>
|
|
381
|
+
agent.stream({ messages: modelMessages, abortSignal: agentAbortSignal }),
|
|
382
|
+
)
|
|
383
|
+
agentTimer.step('agent.stream()-resolved')
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (agentAbortSignal.aborted) {
|
|
386
|
+
streamParams.observer.recordAbort(error)
|
|
387
|
+
} else {
|
|
388
|
+
streamParams.observer.recordError(error)
|
|
389
|
+
}
|
|
390
|
+
throw error
|
|
391
|
+
}
|
|
392
|
+
if (!hasUIMessageStream(result)) {
|
|
393
|
+
throw new Error(`Agent run for ${resolvedAgentId} did not expose a UI message stream.`)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let responseMessage: ChatMessage | null = null
|
|
397
|
+
let resolveFinishedStream!: () => void
|
|
398
|
+
const finishedStream = new Promise<void>((resolve) => {
|
|
399
|
+
resolveFinishedStream = resolve
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
const uiStream = result.toUIMessageStream({
|
|
403
|
+
generateMessageId: () => Bun.randomUUIDv7(),
|
|
404
|
+
originalMessages: streamParams.messages,
|
|
405
|
+
sendReasoning: true,
|
|
406
|
+
sendSources: true,
|
|
407
|
+
messageMetadata: createAgentMessageMetadata({ agentId: resolvedAgentId, agentName: config.displayName as string }),
|
|
408
|
+
onFinish: ({ responseMessage: finishedResponseMessage }: { responseMessage: ChatMessage }) => {
|
|
409
|
+
responseMessage = withMessageCreatedAt(finishedResponseMessage, Date.now())
|
|
410
|
+
resolveFinishedStream()
|
|
411
|
+
},
|
|
412
|
+
}) as ReadableStream<ChatStreamChunk>
|
|
413
|
+
const reader = uiStream.getReader()
|
|
414
|
+
let firstChunkLogged = false
|
|
415
|
+
try {
|
|
416
|
+
for (;;) {
|
|
417
|
+
const { done, value } = await reader.read()
|
|
418
|
+
if (done) break
|
|
419
|
+
if (!firstChunkLogged) {
|
|
420
|
+
agentTimer.step('first-stream-chunk')
|
|
421
|
+
firstChunkLogged = true
|
|
422
|
+
}
|
|
423
|
+
if (streamParams.writer) {
|
|
424
|
+
streamParams.writer.write(value)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} finally {
|
|
428
|
+
reader.releaseLock()
|
|
429
|
+
}
|
|
430
|
+
agentTimer.step('stream-complete')
|
|
431
|
+
|
|
432
|
+
await finishedStream
|
|
433
|
+
// responseMessage is set inside the stream callback — linter cannot track cross-callback assignment
|
|
434
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
435
|
+
if (responseMessage === null) {
|
|
436
|
+
throw new Error(`Agent run for ${resolvedAgentId} did not produce a response message.`)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
for (const toolError of collectToolOutputErrors({ responseMessage: responseMessage })) {
|
|
440
|
+
aiLogger.error`Tool execution failed (agent=${resolvedAgentId}, tool=${toolError.toolName}, toolCallId=${toolError.toolCallId}): ${toolError.errorText}`
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return responseMessage
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
interface PostTurnSideEffectsParams {
|
|
447
|
+
workstream: NormalizedWorkstream
|
|
448
|
+
workstreamRef: RecordIdRef
|
|
449
|
+
orgRef: RecordIdRef
|
|
450
|
+
userRef: RecordIdRef
|
|
451
|
+
userName?: string | null
|
|
452
|
+
orgIdString: string
|
|
453
|
+
workstreamIdString: string
|
|
454
|
+
onboardingActive: boolean
|
|
455
|
+
workspace: unknown
|
|
456
|
+
allAssistantMessages: ChatMessage[]
|
|
457
|
+
referenceUserMessage: ChatMessage | undefined
|
|
458
|
+
referenceUserMessageId: string
|
|
459
|
+
recentHistory: ChatMessage[]
|
|
460
|
+
listReadableUploads: () => ReturnType<typeof listReadableUploadsFromChatMessages>
|
|
461
|
+
memoryBlock: string
|
|
462
|
+
visibleWorkstreamAgentId: string | null | undefined
|
|
463
|
+
defaultLeadAgentId: string
|
|
464
|
+
latestWorkstreamRecord: WorkstreamRecord
|
|
465
|
+
latestPersistedState: WorkstreamState | null
|
|
466
|
+
turnHooks: ReturnType<typeof getTurnHooks>
|
|
467
|
+
buildContextResult: Record<string, unknown> | null
|
|
468
|
+
isUserTurn: boolean
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function runPostTurnSideEffects(params: PostTurnSideEffectsParams): Promise<void> {
|
|
472
|
+
const turnCount = await workstreamService.incrementTurnCount(params.workstreamRef)
|
|
473
|
+
const agentMessages = buildAgentHistoryMessages(params.allAssistantMessages)
|
|
474
|
+
const historyMessagesForMemory = appendPersistedWorkstreamContextToHistoryMessages(
|
|
475
|
+
toHistoryMessages(params.recentHistory),
|
|
476
|
+
{ compactionSummary: params.latestWorkstreamRecord.compactionSummary, persistedState: params.latestPersistedState },
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
const userMessageText = params.referenceUserMessage ? extractMessageText(params.referenceUserMessage).trim() : ''
|
|
480
|
+
const readableUploads = params.listReadableUploads()
|
|
481
|
+
const attachmentMetadataContext = buildReadableUploadMetadataContext(readableUploads)
|
|
482
|
+
const hasAttachmentContext = Boolean(attachmentMetadataContext)
|
|
483
|
+
const shouldExtractMemory = params.onboardingActive
|
|
484
|
+
? shouldEnqueueOnboardingPostChatMemory({
|
|
485
|
+
onboardingActive: params.onboardingActive,
|
|
486
|
+
userMessageText,
|
|
487
|
+
hasAttachmentContext,
|
|
488
|
+
agentMessageCount: agentMessages.length,
|
|
489
|
+
})
|
|
490
|
+
: shouldEnqueueMemoryExtraction({ onboardingActive: params.onboardingActive, turnCount }) &&
|
|
491
|
+
userMessageText.length > 0
|
|
492
|
+
|
|
493
|
+
if (shouldExtractMemory) {
|
|
494
|
+
const memoryUserMessage = userMessageText || 'User uploaded attachment(s).'
|
|
495
|
+
await safeEnqueue(
|
|
496
|
+
() =>
|
|
497
|
+
enqueuePostChatMemory({
|
|
498
|
+
orgId: params.orgIdString,
|
|
499
|
+
workstreamId: params.workstreamIdString,
|
|
500
|
+
sourceId: params.referenceUserMessageId,
|
|
501
|
+
onboardStatus: readOptionalString((params.workspace as { onboardStatus?: unknown }).onboardStatus),
|
|
502
|
+
userMessage: memoryUserMessage,
|
|
503
|
+
historyMessages: historyMessagesForMemory,
|
|
504
|
+
agentMessages,
|
|
505
|
+
memoryBlock: params.memoryBlock.trim() ? params.memoryBlock : undefined,
|
|
506
|
+
attachmentContext: attachmentMetadataContext,
|
|
507
|
+
}),
|
|
508
|
+
{ operationName: 'post-chat memory extraction enqueue' },
|
|
509
|
+
)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (params.isUserTurn && params.referenceUserMessage) {
|
|
513
|
+
const conversationSummary = buildConversationSummary({
|
|
514
|
+
userMessageText,
|
|
515
|
+
assistantMessages: params.allAssistantMessages,
|
|
516
|
+
})
|
|
517
|
+
if (conversationSummary) {
|
|
518
|
+
const effectiveAgentId = params.visibleWorkstreamAgentId ?? params.defaultLeadAgentId
|
|
519
|
+
const recentActivityResult = await recentActivityService.recordEvent({
|
|
520
|
+
orgId: params.orgRef,
|
|
521
|
+
userId: params.userRef,
|
|
522
|
+
source: 'system',
|
|
523
|
+
event: {
|
|
524
|
+
sourceEventId: `chat-turn:${params.referenceUserMessageId}`,
|
|
525
|
+
kind: 'chat.turn.completed',
|
|
526
|
+
targetKind: 'workstream',
|
|
527
|
+
targetId: params.workstreamIdString,
|
|
528
|
+
mergeKey: `workstream:${params.workstreamIdString}`,
|
|
529
|
+
title: buildRecentActivityChatSystemTitle({
|
|
530
|
+
workstream: params.workstream,
|
|
531
|
+
visibleAgentId: effectiveAgentId,
|
|
532
|
+
}),
|
|
533
|
+
sourceLabel: agentDisplayNames[effectiveAgentId],
|
|
534
|
+
deepLink: buildRecentActivityChatDeepLink({
|
|
535
|
+
workstream: params.workstream,
|
|
536
|
+
workstreamId: params.workstreamIdString,
|
|
537
|
+
visibleAgentId: effectiveAgentId,
|
|
538
|
+
}),
|
|
539
|
+
metadata: {
|
|
540
|
+
agentId: effectiveAgentId,
|
|
541
|
+
agentName: agentDisplayNames[effectiveAgentId],
|
|
542
|
+
workstreamId: params.workstreamIdString,
|
|
543
|
+
workstreamTitle: params.latestWorkstreamRecord.title ?? params.workstream.title,
|
|
544
|
+
workstreamMode: params.workstream.mode,
|
|
545
|
+
...(params.workstream.coreType ? { coreType: params.workstream.coreType } : {}),
|
|
546
|
+
userMessageText,
|
|
547
|
+
assistantSummary: conversationSummary,
|
|
548
|
+
messageId: params.referenceUserMessageId,
|
|
549
|
+
},
|
|
550
|
+
occurredAt: toIsoDateTimeString(params.referenceUserMessage.metadata?.createdAt ?? Date.now()),
|
|
551
|
+
},
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
await safeEnqueue(
|
|
555
|
+
async () => {
|
|
556
|
+
const enqueuePostChatOrgAction = getRuntimeAdapters().queues?.enqueuePostChatOrgAction
|
|
557
|
+
if (!enqueuePostChatOrgAction) {
|
|
558
|
+
return
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
await enqueuePostChatOrgAction({
|
|
562
|
+
orgId: params.orgIdString,
|
|
563
|
+
workstreamId: params.workstreamIdString,
|
|
564
|
+
sourceId: params.referenceUserMessageId,
|
|
565
|
+
sourceCreatedAt: params.referenceUserMessage?.metadata?.createdAt ?? Date.now(),
|
|
566
|
+
conversationSummary,
|
|
567
|
+
})
|
|
568
|
+
},
|
|
569
|
+
{ operationName: 'post-chat org action enqueue' },
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
if (recentActivityService.isMeaningfulRefinementCandidate(recentActivityResult.item)) {
|
|
573
|
+
await safeEnqueue(() => enqueueRecentActivityTitleRefinement({ activityId: recentActivityResult.item.id }), {
|
|
574
|
+
operationName: 'recent activity title refinement enqueue',
|
|
575
|
+
})
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (shouldEnqueueRegularDigestForWorkstream({ onboardingActive: params.onboardingActive, turnCount })) {
|
|
581
|
+
await safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: params.orgIdString }), {
|
|
582
|
+
operationName: 'regular chat memory digest enqueue',
|
|
583
|
+
})
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (shouldEnqueueSkillExtraction({ onboardingActive: params.onboardingActive, turnCount })) {
|
|
587
|
+
await safeEnqueue(() => enqueueSkillExtraction({ orgId: params.orgIdString }), {
|
|
588
|
+
operationName: 'skill extraction enqueue',
|
|
589
|
+
})
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (shouldEnqueueMemoryConsolidation({ onboardingActive: params.onboardingActive, turnCount })) {
|
|
593
|
+
await safeEnqueue(() => enqueueMemoryConsolidation({ scopeId: params.orgIdString }), {
|
|
594
|
+
operationName: 'memory consolidation enqueue',
|
|
595
|
+
})
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
303
599
|
export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams): Promise<PreparedWorkstreamTurn> {
|
|
304
600
|
const { workstream, workstreamRef, orgRef, userRef, userName } = params
|
|
305
601
|
const runtimeAdapters = getRuntimeAdapters()
|
|
@@ -320,11 +616,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
320
616
|
})
|
|
321
617
|
|
|
322
618
|
let inputMessage: ChatMessage | undefined
|
|
323
|
-
const shouldPersistInputMessage = params.kind === 'userTurn' ? params.
|
|
619
|
+
const shouldPersistInputMessage = params.kind === 'userTurn' ? params.skipInputMessagePersistence !== true : false
|
|
324
620
|
const shouldProcessPostRunSideEffects =
|
|
325
621
|
params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn' || shouldPersistInputMessage
|
|
326
622
|
if (params.kind === 'userTurn') {
|
|
327
|
-
inputMessage = hydrateMessageFileUrls(withMessageCreatedAt(params.inputMessage))
|
|
623
|
+
inputMessage = hydrateMessageFileUrls(withMessageCreatedAt(params.inputMessage, Date.now()))
|
|
328
624
|
if (inputMessage.role !== 'user') {
|
|
329
625
|
throw new WorkstreamTurnError('Only user messages can be submitted to the workstream runtime.', 400)
|
|
330
626
|
}
|
|
@@ -358,7 +654,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
358
654
|
timer.step('persist-approval-message')
|
|
359
655
|
}
|
|
360
656
|
|
|
361
|
-
const initialWorkstreamState =
|
|
657
|
+
const initialWorkstreamState = parseWorkstreamState(workstreamRecord.state)
|
|
362
658
|
const persistedCompactionCursor = toOptionalTrimmedString(workstreamRecord.lastCompactedMessageId) ?? undefined
|
|
363
659
|
const persistedLiveHistoryPromise = workstreamMessageService.listMessagesAfterCursor(
|
|
364
660
|
workstreamRef,
|
|
@@ -370,7 +666,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
370
666
|
if (inputMessage) {
|
|
371
667
|
userMessage = {
|
|
372
668
|
...inputMessage,
|
|
373
|
-
id: inputMessage.id
|
|
669
|
+
id: inputMessage.id,
|
|
374
670
|
role: 'user',
|
|
375
671
|
parts: inputMessage.parts,
|
|
376
672
|
metadata: { ...inputMessage.metadata, createdAt: toTimestamp(inputMessage.metadata?.createdAt) },
|
|
@@ -395,14 +691,14 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
395
691
|
: validateUIMessages<ChatMessage>({
|
|
396
692
|
messages: persistedLiveHistory,
|
|
397
693
|
metadataSchema: messageMetadataSchema,
|
|
398
|
-
dataSchemas:
|
|
694
|
+
dataSchemas: dataPartsSchemas,
|
|
399
695
|
}).then((messages) => messages.map(hydrateMessageFileUrls)),
|
|
400
696
|
persistedRecentHistory.length === 0
|
|
401
697
|
? Promise.resolve([] as ChatMessage[])
|
|
402
698
|
: validateUIMessages<ChatMessage>({
|
|
403
699
|
messages: persistedRecentHistory,
|
|
404
700
|
metadataSchema: messageMetadataSchema,
|
|
405
|
-
dataSchemas:
|
|
701
|
+
dataSchemas: dataPartsSchemas,
|
|
406
702
|
}).then((messages) => messages.map(hydrateMessageFileUrls)),
|
|
407
703
|
])
|
|
408
704
|
timer.step('validate+hydrate-history')
|
|
@@ -413,7 +709,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
413
709
|
}
|
|
414
710
|
|
|
415
711
|
const originalMessages = userMessage ? upsertChatHistoryMessage(liveHistory, userMessage) : liveHistory
|
|
416
|
-
|
|
712
|
+
let allAssistantMessages: ChatMessage[] = []
|
|
417
713
|
const referenceUserMessage =
|
|
418
714
|
params.kind === 'userTurn' && !shouldPersistInputMessage
|
|
419
715
|
? [...liveHistory].reverse().find((m) => m.role === 'user')
|
|
@@ -425,6 +721,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
425
721
|
workstream.mode === 'group' &&
|
|
426
722
|
!workstream.core &&
|
|
427
723
|
workstreamRecord.nameGenerated !== true &&
|
|
724
|
+
workstreamRecord.title === WORKSTREAM.DEFAULT_TITLE &&
|
|
428
725
|
messageText.length > 0
|
|
429
726
|
) {
|
|
430
727
|
void safeEnqueue(
|
|
@@ -437,14 +734,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
437
734
|
if (workstream.core && !workstream.coreType) {
|
|
438
735
|
throw new WorkstreamTurnError('Core workstreams require a core type.', 400)
|
|
439
736
|
}
|
|
440
|
-
const coreWorkstreamProfile:
|
|
441
|
-
workstream.core && workstream.coreType
|
|
442
|
-
? (getCoreWorkstreamProfile(workstream.coreType) as unknown as {
|
|
443
|
-
config: { agentId: string }
|
|
444
|
-
instructions: string
|
|
445
|
-
skills?: string[]
|
|
446
|
-
})
|
|
447
|
-
: null
|
|
737
|
+
const coreWorkstreamProfile: CoreWorkstreamProfile | null =
|
|
738
|
+
workstream.core && workstream.coreType ? getCoreWorkstreamProfile(workstream.coreType) : null
|
|
448
739
|
const defaultLeadAgentId = getLeadAgentId()
|
|
449
740
|
const visibleWorkstreamAgentId =
|
|
450
741
|
workstream.mode === 'direct' ? workstream.agentId : (coreWorkstreamProfile?.config.agentId ?? defaultLeadAgentId)
|
|
@@ -569,18 +860,21 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
569
860
|
let workstreamState = initialWorkstreamState
|
|
570
861
|
const executionPlanInstructionSectionCache = createExecutionPlanInstructionSectionCache({
|
|
571
862
|
disabled: onboardingActive,
|
|
572
|
-
|
|
863
|
+
loadPlans: async () => {
|
|
864
|
+
const runs = await planRunService.getActiveRunRecords(workstreamRef)
|
|
865
|
+
return Promise.all(runs.map((run) => planRunService.toSerializablePlan(run)))
|
|
866
|
+
},
|
|
573
867
|
})
|
|
574
|
-
const
|
|
868
|
+
const getExecutionPlans = async () => await executionPlanInstructionSectionCache.getPlans()
|
|
575
869
|
const getExecutionPlanInstructionSections = async (): Promise<string[] | undefined> =>
|
|
576
870
|
await executionPlanInstructionSectionCache.getSections()
|
|
577
871
|
const invalidateExecutionPlanInstructionSections = () => {
|
|
578
872
|
executionPlanInstructionSectionCache.invalidate()
|
|
579
873
|
}
|
|
580
874
|
const getWorkstreamStateSection = async (): Promise<string | undefined> => {
|
|
581
|
-
const
|
|
875
|
+
const executionPlans = await getExecutionPlans()
|
|
582
876
|
return contextCompactionRuntime.formatWorkstreamStateForPrompt(
|
|
583
|
-
stripExecutionPlanFieldsFromWorkstreamState(workstreamState,
|
|
877
|
+
stripExecutionPlanFieldsFromWorkstreamState(workstreamState, executionPlans.length > 0),
|
|
584
878
|
)
|
|
585
879
|
}
|
|
586
880
|
const respondedBy = recordIdToString(userRef, TABLES.USER)
|
|
@@ -626,7 +920,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
626
920
|
|
|
627
921
|
const section = await learnedSkillService
|
|
628
922
|
.retrieveForTurn({ orgId: orgIdString, agentId, query: messageText, limit: 3, minConfidence: 0.6 })
|
|
629
|
-
.catch((error
|
|
923
|
+
.catch((error) => {
|
|
630
924
|
aiLogger.warn`Failed to retrieve learned skills for ${agentId}: ${error}`
|
|
631
925
|
return undefined
|
|
632
926
|
})
|
|
@@ -691,181 +985,39 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
691
985
|
})
|
|
692
986
|
|
|
693
987
|
const commitAssistantResponse = async (response: ChatMessage, agentId: string, agentName: string) => {
|
|
694
|
-
const committed = withMessageCreatedAt(
|
|
695
|
-
...response,
|
|
696
|
-
|
|
697
|
-
|
|
988
|
+
const committed = withMessageCreatedAt(
|
|
989
|
+
{ ...response, metadata: { ...response.metadata, ...buildAgentMetadataPatch(agentId, agentName) } },
|
|
990
|
+
Date.now(),
|
|
991
|
+
)
|
|
698
992
|
|
|
699
993
|
await workstreamMessageService.upsertMessages({ workstreamId: workstreamRef, messages: [committed] })
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
currentMessages[currentMessageIndex] = committed
|
|
703
|
-
} else {
|
|
704
|
-
currentMessages = [...currentMessages, committed]
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const assistantIndex = allAssistantMessages.findIndex((item) => item.id === committed.id)
|
|
708
|
-
if (assistantIndex >= 0) {
|
|
709
|
-
allAssistantMessages[assistantIndex] = committed
|
|
710
|
-
} else {
|
|
711
|
-
allAssistantMessages.push(committed)
|
|
712
|
-
}
|
|
994
|
+
currentMessages = upsertChatHistoryMessage(currentMessages, committed)
|
|
995
|
+
allAssistantMessages = upsertChatHistoryMessage(allAssistantMessages, committed)
|
|
713
996
|
|
|
714
997
|
return committed
|
|
715
998
|
}
|
|
716
999
|
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
workstreamRef,
|
|
739
|
-
orgRef,
|
|
740
|
-
userRef,
|
|
741
|
-
userName,
|
|
742
|
-
onboardingActive,
|
|
743
|
-
linearInstalled,
|
|
744
|
-
githubInstalled,
|
|
745
|
-
reasoningProfile: reasoningProfile.name,
|
|
746
|
-
skills: streamParams.skills,
|
|
747
|
-
additionalInstructionSections: streamParams.additionalInstructionSections,
|
|
748
|
-
context: buildContextResult,
|
|
749
|
-
}),
|
|
750
|
-
)
|
|
751
|
-
agentTimer.step('hook:resolveAgent')
|
|
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)')
|
|
759
|
-
const config = getAgentRuntimeConfig({
|
|
760
|
-
agentId: resolvedAgentId,
|
|
761
|
-
workstreamMode: workstream.mode,
|
|
762
|
-
mode: streamParams.mode,
|
|
763
|
-
skills: streamParams.skills,
|
|
764
|
-
onboardingActive,
|
|
765
|
-
linearInstalled,
|
|
766
|
-
reasoningProfile: reasoningProfile.name,
|
|
767
|
-
systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
|
|
768
|
-
preSeededMemoriesSection,
|
|
769
|
-
retrievedKnowledgeSection,
|
|
770
|
-
workstreamMemoryBlock: memoryBlock,
|
|
771
|
-
workstreamStateSection,
|
|
772
|
-
learnedSkillsSection,
|
|
773
|
-
additionalInstructionSections: mergeInstructionSections(
|
|
774
|
-
executionPlanInstructionSections,
|
|
775
|
-
streamParams.additionalInstructionSections,
|
|
776
|
-
hookInstructionSections,
|
|
777
|
-
readInstructionSections(agentResolution?.additionalInstructionSections),
|
|
778
|
-
optionalInstructionSection(agentResolution?.extraInstructions),
|
|
779
|
-
),
|
|
780
|
-
context: buildContextResult,
|
|
781
|
-
}) as AgentRuntimeConfig
|
|
782
|
-
agentTimer.step('build-agent-config')
|
|
783
|
-
const modelMessages = await convertToModelMessages(streamParams.messages, {
|
|
784
|
-
ignoreIncompleteToolCalls: true,
|
|
785
|
-
})
|
|
786
|
-
agentTimer.step('convert-model-messages')
|
|
787
|
-
const agent = (createAgent as unknown as AgentFactory)[config.id as string]({
|
|
788
|
-
mode: streamParams.mode,
|
|
789
|
-
tools: streamParams.tools,
|
|
790
|
-
extraInstructions: config.extraInstructions,
|
|
791
|
-
stopWhen: (agentResolution?.stopWhen as
|
|
792
|
-
| StopCondition<ToolSet>
|
|
793
|
-
| Array<StopCondition<ToolSet>>
|
|
794
|
-
| undefined) ??
|
|
795
|
-
streamParams.stopWhen ?? [stepCountIs(config.maxSteps as number)],
|
|
796
|
-
prepareStep:
|
|
797
|
-
(agentResolution?.prepareStep as PrepareStepFunction<ToolSet> | undefined) ?? streamParams.prepareStep,
|
|
798
|
-
})
|
|
799
|
-
const agentAbortSignal = streamParams.abortSignal ?? runAbort.signal
|
|
800
|
-
agentTimer.step('agent-construction')
|
|
801
|
-
|
|
802
|
-
let result: unknown
|
|
803
|
-
try {
|
|
804
|
-
result = await streamParams.observer.run(() =>
|
|
805
|
-
agent.stream({ messages: modelMessages, abortSignal: agentAbortSignal }),
|
|
806
|
-
)
|
|
807
|
-
agentTimer.step('agent.stream()-resolved')
|
|
808
|
-
} catch (error) {
|
|
809
|
-
if (agentAbortSignal.aborted) {
|
|
810
|
-
streamParams.observer.recordAbort(error)
|
|
811
|
-
} else {
|
|
812
|
-
streamParams.observer.recordError(error)
|
|
813
|
-
}
|
|
814
|
-
throw error
|
|
815
|
-
}
|
|
816
|
-
if (!hasUIMessageStream(result)) {
|
|
817
|
-
throw new Error(`Agent run for ${resolvedAgentId} did not expose a UI message stream.`)
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
let responseMessage: ChatMessage | null = null
|
|
821
|
-
let resolveFinishedStream!: () => void
|
|
822
|
-
const finishedStream = new Promise<void>((resolve) => {
|
|
823
|
-
resolveFinishedStream = resolve
|
|
824
|
-
})
|
|
825
|
-
|
|
826
|
-
const uiStream = result.toUIMessageStream({
|
|
827
|
-
generateMessageId: () => Bun.randomUUIDv7(),
|
|
828
|
-
originalMessages: streamParams.messages,
|
|
829
|
-
sendReasoning: true,
|
|
830
|
-
sendSources: true,
|
|
831
|
-
messageMetadata: createAgentMessageMetadata({
|
|
832
|
-
agentId: resolvedAgentId,
|
|
833
|
-
agentName: config.displayName as string,
|
|
834
|
-
}),
|
|
835
|
-
onFinish: ({ responseMessage: finishedResponseMessage }: { responseMessage: ChatMessage }) => {
|
|
836
|
-
responseMessage = withMessageCreatedAt(finishedResponseMessage)
|
|
837
|
-
resolveFinishedStream()
|
|
838
|
-
},
|
|
839
|
-
}) as ReadableStream<ChatStreamChunk>
|
|
840
|
-
const reader = uiStream.getReader()
|
|
841
|
-
let firstChunkLogged = false
|
|
842
|
-
try {
|
|
843
|
-
for (;;) {
|
|
844
|
-
const { done, value } = await reader.read()
|
|
845
|
-
if (done) break
|
|
846
|
-
if (!firstChunkLogged) {
|
|
847
|
-
agentTimer.step('first-stream-chunk')
|
|
848
|
-
firstChunkLogged = true
|
|
849
|
-
}
|
|
850
|
-
if (streamParams.writer) {
|
|
851
|
-
streamParams.writer.write(value)
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
} finally {
|
|
855
|
-
reader.releaseLock()
|
|
856
|
-
}
|
|
857
|
-
agentTimer.step('stream-complete')
|
|
858
|
-
|
|
859
|
-
const finalizedResponseMessage = await finishedStream.then(() => responseMessage)
|
|
860
|
-
if (finalizedResponseMessage === null) {
|
|
861
|
-
throw new Error(`Agent run for ${resolvedAgentId} did not produce a response message.`)
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
for (const toolError of collectToolOutputErrors({ responseMessage: finalizedResponseMessage })) {
|
|
865
|
-
aiLogger.warn`Tool execution failed (agent=${resolvedAgentId}, tool=${toolError.toolName}, toolCallId=${toolError.toolCallId}): ${toolError.errorText}`
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
return finalizedResponseMessage
|
|
1000
|
+
const streamCtx: StreamAgentResponseContext = {
|
|
1001
|
+
turnHooks,
|
|
1002
|
+
workstream,
|
|
1003
|
+
workstreamRef,
|
|
1004
|
+
orgRef,
|
|
1005
|
+
userRef,
|
|
1006
|
+
userName,
|
|
1007
|
+
onboardingActive,
|
|
1008
|
+
linearInstalled,
|
|
1009
|
+
githubInstalled,
|
|
1010
|
+
reasoningProfileName: reasoningProfile.name,
|
|
1011
|
+
buildContextResult,
|
|
1012
|
+
getExecutionPlanInstructionSections,
|
|
1013
|
+
getPreSeededMemoriesSection,
|
|
1014
|
+
getWorkstreamStateSection,
|
|
1015
|
+
getLearnedSkillsSection,
|
|
1016
|
+
promptContext,
|
|
1017
|
+
retrievedKnowledgeSection,
|
|
1018
|
+
memoryBlock,
|
|
1019
|
+
hookInstructionSections,
|
|
1020
|
+
runAbortSignal: runAbort.signal,
|
|
869
1021
|
}
|
|
870
1022
|
|
|
871
1023
|
const runVisibleAgent = async (runParams: {
|
|
@@ -878,6 +1030,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
878
1030
|
}): Promise<ChatMessage> => {
|
|
879
1031
|
const visibleTimer = lotaDebugLogger.timer(`visible:${runParams.agentId}`)
|
|
880
1032
|
let runMemoryBlock = memoryBlock
|
|
1033
|
+
const includeExecutionPlanTools = runParams.mode !== 'fixedWorkstreamMode' && !onboardingActive
|
|
881
1034
|
const tools: ToolSet = {
|
|
882
1035
|
...((await buildAgentTools({
|
|
883
1036
|
agentId: runParams.agentId,
|
|
@@ -899,7 +1052,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
899
1052
|
runMemoryBlock = value
|
|
900
1053
|
},
|
|
901
1054
|
availableUploads: listReadableUploads(runParams.extraMessages),
|
|
902
|
-
includeExecutionPlanTools
|
|
1055
|
+
includeExecutionPlanTools,
|
|
903
1056
|
onExecutionPlanChanged: invalidateExecutionPlanInstructionSections,
|
|
904
1057
|
context: buildContextResult,
|
|
905
1058
|
})) as ToolSet),
|
|
@@ -907,7 +1060,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
907
1060
|
...runParams.extraTools,
|
|
908
1061
|
}
|
|
909
1062
|
visibleTimer.step('build-agent-tools')
|
|
910
|
-
|
|
1063
|
+
streamCtx.memoryBlock = memoryBlock
|
|
1064
|
+
const responseMessage = await streamAgentResponse(streamCtx, {
|
|
911
1065
|
agentId: runParams.agentId,
|
|
912
1066
|
mode: runParams.mode,
|
|
913
1067
|
messages: buildRunInputMessages(runParams.extraMessages),
|
|
@@ -921,7 +1075,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
921
1075
|
visibleTimer.step('stream-agent-response')
|
|
922
1076
|
memoryBlock = runMemoryBlock
|
|
923
1077
|
|
|
924
|
-
return
|
|
1078
|
+
return commitAssistantResponse(
|
|
925
1079
|
responseMessage,
|
|
926
1080
|
runParams.agentId,
|
|
927
1081
|
agentDisplayNames[runParams.agentId] ?? runParams.agentId,
|
|
@@ -996,14 +1150,14 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
996
1150
|
hookInstructionSections,
|
|
997
1151
|
),
|
|
998
1152
|
context: buildContextResult,
|
|
999
|
-
}) as
|
|
1153
|
+
}) as Record<string, unknown>
|
|
1000
1154
|
const observer = createObserver(agentId)
|
|
1001
|
-
const agent =
|
|
1155
|
+
const agent = createAgent[specialistConfig.id as string]({
|
|
1002
1156
|
mode: 'fixedWorkstreamMode',
|
|
1003
1157
|
tools: { ...(specialistTools as ToolSet), ...toolProviders },
|
|
1004
1158
|
extraInstructions: specialistConfig.extraInstructions,
|
|
1005
1159
|
stopWhen: [stepCountIs(specialistConfig.maxSteps as number)],
|
|
1006
|
-
})
|
|
1160
|
+
}) as ToolLoopAgent<never, ToolSet>
|
|
1007
1161
|
const modelMessages = await convertToModelMessages(buildRunInputMessages([specialistTaskMessage]), {
|
|
1008
1162
|
ignoreIncompleteToolCalls: true,
|
|
1009
1163
|
})
|
|
@@ -1039,7 +1193,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1039
1193
|
}),
|
|
1040
1194
|
}) as ReadableStream<never>,
|
|
1041
1195
|
})) {
|
|
1042
|
-
finalMessage = withMessageCreatedAt(message)
|
|
1196
|
+
finalMessage = withMessageCreatedAt(message, Date.now())
|
|
1043
1197
|
yield finalMessage
|
|
1044
1198
|
}
|
|
1045
1199
|
|
|
@@ -1115,7 +1269,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1115
1269
|
} finally {
|
|
1116
1270
|
try {
|
|
1117
1271
|
const latestWorkstreamRecord = await workstreamService.getById(workstreamRef)
|
|
1118
|
-
const latestPersistedState =
|
|
1272
|
+
const latestPersistedState = parseWorkstreamState(latestWorkstreamRecord.state)
|
|
1119
1273
|
|
|
1120
1274
|
await finalizeTurnRun({
|
|
1121
1275
|
serverRunId,
|
|
@@ -1126,14 +1280,15 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1126
1280
|
contextCompactionRuntime.shouldCompactHistory({
|
|
1127
1281
|
summaryText,
|
|
1128
1282
|
liveMessages: messages,
|
|
1129
|
-
contextSize:
|
|
1283
|
+
contextSize: CONTEXT_WINDOW_TOKENS,
|
|
1130
1284
|
}),
|
|
1131
|
-
enqueueCompaction: () =>
|
|
1132
|
-
enqueueContextCompaction({
|
|
1285
|
+
enqueueCompaction: async () => {
|
|
1286
|
+
await enqueueContextCompaction({
|
|
1133
1287
|
domain: 'workstream',
|
|
1134
1288
|
entityId: workstreamIdString,
|
|
1135
|
-
contextSize:
|
|
1136
|
-
})
|
|
1289
|
+
contextSize: CONTEXT_WINDOW_TOKENS,
|
|
1290
|
+
})
|
|
1291
|
+
},
|
|
1137
1292
|
unregisterRun: (runId) => chatRunRegistry.unregister(runId),
|
|
1138
1293
|
clearActiveRunId: (runId) => workstreamService.clearActiveRunIdIfMatches(workstreamRef, runId),
|
|
1139
1294
|
disposeAbort: () => runAbort.dispose(),
|
|
@@ -1143,130 +1298,30 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1143
1298
|
})
|
|
1144
1299
|
|
|
1145
1300
|
if (allAssistantMessages.length > 0 && shouldProcessPostRunSideEffects) {
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
enqueuePostChatMemory({
|
|
1171
|
-
orgId: orgIdString,
|
|
1172
|
-
workstreamId: workstreamIdString,
|
|
1173
|
-
sourceId: referenceUserMessageId,
|
|
1174
|
-
onboardStatus: readOptionalString((workspace as { onboardStatus?: unknown }).onboardStatus),
|
|
1175
|
-
userMessage: memoryUserMessage,
|
|
1176
|
-
historyMessages: historyMessagesForMemory,
|
|
1177
|
-
agentMessages,
|
|
1178
|
-
memoryBlock: memoryBlock.trim() ? memoryBlock : undefined,
|
|
1179
|
-
attachmentContext: attachmentMetadataContext,
|
|
1180
|
-
}),
|
|
1181
|
-
{ operationName: 'post-chat memory extraction enqueue' },
|
|
1182
|
-
)
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
if (params.kind === 'userTurn' && referenceUserMessage) {
|
|
1186
|
-
const conversationSummary = buildConversationSummary({
|
|
1187
|
-
userMessageText,
|
|
1188
|
-
assistantMessages: allAssistantMessages,
|
|
1189
|
-
})
|
|
1190
|
-
if (conversationSummary) {
|
|
1191
|
-
const recentActivityResult = await recentActivityService.recordEvent({
|
|
1192
|
-
orgId: orgRef,
|
|
1193
|
-
userId: userRef,
|
|
1194
|
-
source: 'system',
|
|
1195
|
-
event: {
|
|
1196
|
-
sourceEventId: `chat-turn:${referenceUserMessageId}`,
|
|
1197
|
-
kind: 'chat.turn.completed',
|
|
1198
|
-
targetKind: 'workstream',
|
|
1199
|
-
targetId: workstreamIdString,
|
|
1200
|
-
mergeKey: `workstream:${workstreamIdString}`,
|
|
1201
|
-
title: buildRecentActivityChatSystemTitle({
|
|
1202
|
-
workstream,
|
|
1203
|
-
visibleAgentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1204
|
-
}),
|
|
1205
|
-
sourceLabel: agentDisplayNames[visibleWorkstreamAgentId ?? defaultLeadAgentId],
|
|
1206
|
-
deepLink: buildRecentActivityChatDeepLink({
|
|
1207
|
-
workstream,
|
|
1208
|
-
workstreamId: workstreamIdString,
|
|
1209
|
-
visibleAgentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1210
|
-
}),
|
|
1211
|
-
metadata: {
|
|
1212
|
-
agentId: visibleWorkstreamAgentId ?? defaultLeadAgentId,
|
|
1213
|
-
agentName: agentDisplayNames[visibleWorkstreamAgentId ?? defaultLeadAgentId],
|
|
1214
|
-
workstreamId: workstreamIdString,
|
|
1215
|
-
workstreamTitle: latestWorkstreamRecord.title ?? workstream.title,
|
|
1216
|
-
workstreamMode: workstream.mode,
|
|
1217
|
-
...(workstream.coreType ? { coreType: workstream.coreType } : {}),
|
|
1218
|
-
userMessageText,
|
|
1219
|
-
assistantSummary: conversationSummary,
|
|
1220
|
-
messageId: referenceUserMessageId,
|
|
1221
|
-
},
|
|
1222
|
-
occurredAt: toIsoDateTimeString(referenceUserMessage.metadata?.createdAt ?? Date.now()),
|
|
1223
|
-
},
|
|
1224
|
-
})
|
|
1225
|
-
|
|
1226
|
-
await safeEnqueue(
|
|
1227
|
-
async () => {
|
|
1228
|
-
const enqueuePostChatOrgAction = getRuntimeAdapters().queues?.enqueuePostChatOrgAction
|
|
1229
|
-
if (!enqueuePostChatOrgAction) {
|
|
1230
|
-
return
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
await enqueuePostChatOrgAction({
|
|
1234
|
-
orgId: orgIdString,
|
|
1235
|
-
workstreamId: workstreamIdString,
|
|
1236
|
-
sourceId: referenceUserMessageId,
|
|
1237
|
-
sourceCreatedAt: referenceUserMessage.metadata?.createdAt ?? Date.now(),
|
|
1238
|
-
conversationSummary,
|
|
1239
|
-
})
|
|
1240
|
-
},
|
|
1241
|
-
{ operationName: 'post-chat org action enqueue' },
|
|
1242
|
-
)
|
|
1243
|
-
|
|
1244
|
-
if (recentActivityService.isMeaningfulRefinementCandidate(recentActivityResult.item)) {
|
|
1245
|
-
await safeEnqueue(
|
|
1246
|
-
() => enqueueRecentActivityTitleRefinement({ activityId: recentActivityResult.item.id }),
|
|
1247
|
-
{ operationName: 'recent activity title refinement enqueue' },
|
|
1248
|
-
)
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
if (shouldEnqueueRegularDigestForWorkstream({ onboardingActive, turnCount })) {
|
|
1254
|
-
await safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: orgIdString }), {
|
|
1255
|
-
operationName: 'regular chat memory digest enqueue',
|
|
1256
|
-
})
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
if (shouldEnqueueSkillExtraction({ onboardingActive, turnCount })) {
|
|
1260
|
-
await safeEnqueue(() => enqueueSkillExtraction({ orgId: orgIdString }), {
|
|
1261
|
-
operationName: 'skill extraction enqueue',
|
|
1262
|
-
})
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
if (shouldEnqueueMemoryConsolidation({ onboardingActive, turnCount })) {
|
|
1266
|
-
await safeEnqueue(() => enqueueMemoryConsolidation({ scopeId: orgIdString }), {
|
|
1267
|
-
operationName: 'memory consolidation enqueue',
|
|
1268
|
-
})
|
|
1269
|
-
}
|
|
1301
|
+
await runPostTurnSideEffects({
|
|
1302
|
+
workstream,
|
|
1303
|
+
workstreamRef,
|
|
1304
|
+
orgRef,
|
|
1305
|
+
userRef,
|
|
1306
|
+
userName,
|
|
1307
|
+
orgIdString,
|
|
1308
|
+
workstreamIdString,
|
|
1309
|
+
onboardingActive,
|
|
1310
|
+
workspace,
|
|
1311
|
+
allAssistantMessages,
|
|
1312
|
+
referenceUserMessage,
|
|
1313
|
+
referenceUserMessageId,
|
|
1314
|
+
recentHistory,
|
|
1315
|
+
listReadableUploads: () => listReadableUploads(),
|
|
1316
|
+
memoryBlock,
|
|
1317
|
+
visibleWorkstreamAgentId,
|
|
1318
|
+
defaultLeadAgentId,
|
|
1319
|
+
latestWorkstreamRecord,
|
|
1320
|
+
latestPersistedState,
|
|
1321
|
+
turnHooks,
|
|
1322
|
+
buildContextResult,
|
|
1323
|
+
isUserTurn: params.kind === 'userTurn',
|
|
1324
|
+
})
|
|
1270
1325
|
}
|
|
1271
1326
|
|
|
1272
1327
|
if (allAssistantMessages.length > 0) {
|