@lota-sdk/core 0.1.24 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/ai/definitions.ts +5 -59
- package/src/ai-gateway/ai-gateway.ts +36 -28
- package/src/ai-gateway/cache-headers.ts +9 -0
- package/src/config/model-constants.ts +6 -2
- package/src/create-runtime.ts +1 -17
- package/src/db/memory-types.ts +13 -8
- package/src/db/memory.ts +74 -53
- package/src/queues/autonomous-job.queue.ts +1 -8
- package/src/queues/context-compaction.queue.ts +2 -2
- package/src/queues/index.ts +2 -6
- package/src/queues/organization-learning.queue.ts +78 -0
- package/src/queues/plan-agent-heartbeat.queue.ts +10 -16
- package/src/queues/title-generation.queue.ts +62 -0
- package/src/runtime/agent-prompt-context.ts +0 -18
- package/src/runtime/agent-runtime-policy.ts +9 -2
- package/src/runtime/context-compaction-constants.ts +4 -2
- package/src/runtime/context-compaction.ts +135 -118
- package/src/runtime/memory-pipeline.ts +70 -1
- package/src/runtime/memory-prompts-fact.ts +16 -0
- package/src/runtime/plugin-resolution.ts +3 -2
- package/src/runtime/plugin-types.ts +1 -42
- package/src/runtime/post-turn-side-effects.ts +212 -0
- package/src/runtime/runtime-config.ts +0 -13
- package/src/runtime/runtime-extensions.ts +10 -16
- package/src/runtime/runtime-worker-registry.ts +8 -19
- package/src/runtime/social-chat-agent-runner.ts +119 -0
- package/src/runtime/social-chat-history.ts +110 -0
- package/src/runtime/social-chat-prompts.ts +58 -0
- package/src/runtime/social-chat.ts +104 -340
- package/src/runtime/specialist-runner.ts +18 -0
- package/src/runtime/workstream-chat-helpers.ts +19 -0
- package/src/runtime/workstream-plan-turn.ts +195 -0
- package/src/runtime/workstream-state.ts +11 -8
- package/src/runtime/workstream-turn-context.ts +183 -0
- package/src/services/autonomous-job.service.ts +1 -8
- package/src/services/execution-plan.service.ts +205 -334
- package/src/services/index.ts +1 -4
- package/src/services/memory.service.ts +54 -44
- package/src/services/ownership-dispatcher.service.ts +2 -19
- package/src/services/plan-completion-side-effects.ts +80 -0
- package/src/services/plan-event-delivery.service.ts +1 -1
- package/src/services/plan-executor.service.ts +42 -190
- package/src/services/plan-node-spec.ts +60 -0
- package/src/services/plan-run-data.ts +88 -0
- package/src/services/plan-validator.service.ts +10 -8
- package/src/services/workstream-constants.ts +2 -0
- package/src/services/workstream-title.service.ts +1 -1
- package/src/services/workstream-turn-preparation.service.ts +208 -715
- package/src/services/workstream.service.ts +162 -192
- package/src/services/workstream.types.ts +12 -44
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -0
- package/src/tools/execution-plan.tool.ts +7 -6
- package/src/tools/remember-memory.tool.ts +7 -10
- 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 +1 -1
- package/src/utils/autonomous-job-ids.ts +7 -0
- package/src/workers/organization-learning.worker.ts +31 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +9 -3
- package/src/workers/skill-extraction.runner.ts +2 -2
- package/src/queues/recent-activity-title-refinement.queue.ts +0 -30
- package/src/queues/regular-chat-memory-digest.config.ts +0 -12
- package/src/queues/regular-chat-memory-digest.queue.ts +0 -34
- package/src/queues/skill-extraction.config.ts +0 -9
- package/src/queues/skill-extraction.queue.ts +0 -27
- package/src/queues/workstream-title-generation.queue.ts +0 -33
- package/src/services/context-enrichment.service.ts +0 -33
- package/src/services/coordination-registry.service.ts +0 -117
- package/src/services/domain-agent-executor.service.ts +0 -71
- package/src/services/memory-assessment.service.ts +0 -44
- package/src/services/playbook-registry.service.ts +0 -67
- package/src/workers/regular-chat-memory-digest.worker.ts +0 -22
- package/src/workers/skill-extraction.worker.ts +0 -22
|
@@ -11,17 +11,9 @@ import {
|
|
|
11
11
|
toTimestamp,
|
|
12
12
|
withMessageCreatedAt,
|
|
13
13
|
} from '@lota-sdk/shared'
|
|
14
|
-
import type {
|
|
15
|
-
|
|
16
|
-
MessageMetadata,
|
|
17
|
-
PlanArtifactSubmission,
|
|
18
|
-
PlanNodeHandoffContext,
|
|
19
|
-
PlanNodeRunRecord,
|
|
20
|
-
PlanNodeSpecRecord,
|
|
21
|
-
} from '@lota-sdk/shared'
|
|
22
|
-
import { convertToModelMessages, readUIMessageStream, stepCountIs, tool as createTool, validateUIMessages } from 'ai'
|
|
14
|
+
import type { ChatMessage, ConsultSpecialistArgs, MessageMetadata, PlanNodeSpecRecord } from '@lota-sdk/shared'
|
|
15
|
+
import { convertToModelMessages, stepCountIs, tool as createTool, validateUIMessages } from 'ai'
|
|
23
16
|
import type { PrepareStepFunction, StopCondition, ToolLoopAgent, ToolSet, UIMessageStreamWriter } from 'ai'
|
|
24
|
-
import type { z } from 'zod'
|
|
25
17
|
|
|
26
18
|
import type { CoreWorkstreamProfile } from '../config/agent-defaults'
|
|
27
19
|
import {
|
|
@@ -38,17 +30,8 @@ import type { RecordIdRef } from '../db/record-id'
|
|
|
38
30
|
import { recordIdToString } from '../db/record-id'
|
|
39
31
|
import { TABLES } from '../db/tables'
|
|
40
32
|
import { enqueueContextCompaction } from '../queues/context-compaction.queue'
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import { enqueueRecentActivityTitleRefinement } from '../queues/recent-activity-title-refinement.queue'
|
|
44
|
-
import { enqueueRegularChatMemoryDigest } from '../queues/regular-chat-memory-digest.queue'
|
|
45
|
-
import { enqueueSkillExtraction } from '../queues/skill-extraction.queue'
|
|
46
|
-
import { enqueueWorkstreamTitleGeneration } from '../queues/workstream-title-generation.queue'
|
|
47
|
-
import { buildAgentPromptContext } from '../runtime/agent-prompt-context'
|
|
48
|
-
import {
|
|
49
|
-
OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES,
|
|
50
|
-
buildCompletionCheckStructuredOutputHints,
|
|
51
|
-
} from '../runtime/agent-runtime-policy'
|
|
33
|
+
import { enqueueWorkstreamTitleGeneration } from '../queues/title-generation.queue'
|
|
34
|
+
import { OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES } from '../runtime/agent-runtime-policy'
|
|
52
35
|
import {
|
|
53
36
|
buildSpecialistTaskMessage,
|
|
54
37
|
createAgentMessageMetadata,
|
|
@@ -62,32 +45,30 @@ import { parseWorkstreamState } from '../runtime/context-compaction'
|
|
|
62
45
|
import { CONTEXT_WINDOW_TOKENS } from '../runtime/context-compaction-constants'
|
|
63
46
|
import { createExecutionPlanInstructionSectionCache } from '../runtime/execution-plan'
|
|
64
47
|
import { mergeInstructionSections } from '../runtime/instruction-sections'
|
|
65
|
-
import {
|
|
66
|
-
shouldEnqueueMemoryConsolidation,
|
|
67
|
-
shouldEnqueueMemoryExtraction,
|
|
68
|
-
shouldEnqueueOnboardingPostChatMemory,
|
|
69
|
-
shouldEnqueueRegularDigestForWorkstream,
|
|
70
|
-
} from '../runtime/memory-digest-policy'
|
|
71
|
-
import { buildIndexedRepositoriesContext, getPluginService } from '../runtime/plugin-resolution'
|
|
48
|
+
import { runPostTurnSideEffects } from '../runtime/post-turn-side-effects'
|
|
72
49
|
import { getRuntimeAdapters, getToolProviders, getTurnHooks } from '../runtime/runtime-extensions'
|
|
73
|
-
import {
|
|
50
|
+
import { runSpecialistSession } from '../runtime/specialist-runner'
|
|
74
51
|
import { finalizeTurnRun } from '../runtime/turn-lifecycle'
|
|
75
52
|
import {
|
|
76
|
-
|
|
77
|
-
buildAgentHistoryMessages,
|
|
78
|
-
buildConversationSummary,
|
|
79
|
-
buildReadableUploadMetadataContext,
|
|
53
|
+
asRecord,
|
|
80
54
|
collectToolOutputErrors,
|
|
81
55
|
extractMessageText,
|
|
82
|
-
|
|
56
|
+
readInstructionSections,
|
|
57
|
+
readOptionalString,
|
|
83
58
|
toOptionalTrimmedString,
|
|
84
59
|
} from '../runtime/workstream-chat-helpers'
|
|
60
|
+
import {
|
|
61
|
+
buildPlanTurnInstructionSections,
|
|
62
|
+
buildPlanTurnPromptMessage,
|
|
63
|
+
buildPlanTurnSubmitToolDescription,
|
|
64
|
+
} from '../runtime/workstream-plan-turn'
|
|
65
|
+
import type { WorkstreamPlanTurnContext } from '../runtime/workstream-plan-turn'
|
|
85
66
|
import type { WorkstreamState } from '../runtime/workstream-state'
|
|
67
|
+
import { assembleWorkstreamTurnContext } from '../runtime/workstream-turn-context'
|
|
86
68
|
import { chatRunRegistry } from '../services/chat-run-registry.service'
|
|
87
69
|
import type { NormalizedWorkstream, WorkstreamRecord } from '../services/workstream.types'
|
|
88
70
|
import { createTeamThinkTool } from '../tools/team-think.tool'
|
|
89
71
|
import { safeEnqueue } from '../utils/async'
|
|
90
|
-
import { toIsoDateTimeString } from '../utils/date-time'
|
|
91
72
|
import { AppError } from '../utils/errors'
|
|
92
73
|
import { attachmentService } from './attachment.service'
|
|
93
74
|
import { listReadableUploadsFromChatMessages } from './chat-attachments.service'
|
|
@@ -96,7 +77,6 @@ import { executionPlanService } from './execution-plan.service'
|
|
|
96
77
|
import { learnedSkillService } from './learned-skill.service'
|
|
97
78
|
import { memoryService } from './memory.service'
|
|
98
79
|
import { planRunService } from './plan-run.service'
|
|
99
|
-
import { recentActivityService } from './recent-activity.service'
|
|
100
80
|
import { workstreamMessageService } from './workstream-message.service'
|
|
101
81
|
import { ActiveWorkstreamRunConflictError, workstreamService } from './workstream.service'
|
|
102
82
|
|
|
@@ -168,205 +148,11 @@ class WorkstreamTurnError extends AppError {
|
|
|
168
148
|
}
|
|
169
149
|
}
|
|
170
150
|
|
|
171
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
172
|
-
return value && typeof value === 'object' ? (value as Record<string, unknown>) : null
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function readOptionalString(value: unknown): string | undefined {
|
|
176
|
-
return typeof value === 'string' && value.trim().length > 0 ? value : undefined
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function readOptionalBoolean(value: unknown): boolean | undefined {
|
|
180
|
-
return typeof value === 'boolean' ? value : undefined
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function readInstructionSections(value: unknown): string[] {
|
|
184
|
-
if (!Array.isArray(value)) {
|
|
185
|
-
return []
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return value
|
|
189
|
-
.filter((section): section is string => typeof section === 'string')
|
|
190
|
-
.map((section) => section.trim())
|
|
191
|
-
.filter((section) => section.length > 0)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
151
|
function optionalInstructionSection(value: unknown): string[] | undefined {
|
|
195
152
|
const section = readOptionalString(value)
|
|
196
153
|
return section ? [section] : undefined
|
|
197
154
|
}
|
|
198
155
|
|
|
199
|
-
export interface PlanTurnUpstreamHandoff {
|
|
200
|
-
nodeId: string
|
|
201
|
-
label: string
|
|
202
|
-
ownerRef: string
|
|
203
|
-
ownerType: PlanNodeSpecRecord['owner']['executorType']
|
|
204
|
-
handoffContext: PlanNodeHandoffContext
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export interface WorkstreamPlanTurnContext {
|
|
208
|
-
runId: string
|
|
209
|
-
nodeId: string
|
|
210
|
-
planTitle: string
|
|
211
|
-
nodeSpec: PlanNodeSpecRecord
|
|
212
|
-
nodeRun: PlanNodeRunRecord
|
|
213
|
-
resolvedInput: Record<string, unknown>
|
|
214
|
-
inputArtifacts: PlanArtifactSubmission[]
|
|
215
|
-
upstreamHandoffs: PlanTurnUpstreamHandoff[]
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function buildPlanTurnExecutionSection(planTurn: WorkstreamPlanTurnContext): string {
|
|
219
|
-
const payload = {
|
|
220
|
-
runId: planTurn.runId,
|
|
221
|
-
planTitle: planTurn.planTitle,
|
|
222
|
-
node: {
|
|
223
|
-
id: planTurn.nodeSpec.nodeId,
|
|
224
|
-
label: planTurn.nodeSpec.label,
|
|
225
|
-
owner: planTurn.nodeSpec.owner,
|
|
226
|
-
objective: planTurn.nodeSpec.objective,
|
|
227
|
-
instructions: planTurn.nodeSpec.instructions,
|
|
228
|
-
outputSchemaRef: planTurn.nodeSpec.outputSchemaRef ?? null,
|
|
229
|
-
deliverables: planTurn.nodeSpec.deliverables,
|
|
230
|
-
successCriteria: planTurn.nodeSpec.successCriteria,
|
|
231
|
-
completionChecks: planTurn.nodeSpec.completionChecks,
|
|
232
|
-
toolPolicy: planTurn.nodeSpec.toolPolicy,
|
|
233
|
-
contextPolicy: planTurn.nodeSpec.contextPolicy,
|
|
234
|
-
},
|
|
235
|
-
resolvedInput: planTurn.resolvedInput,
|
|
236
|
-
inputArtifacts: planTurn.inputArtifacts,
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return [
|
|
240
|
-
'<plan-turn-execution>',
|
|
241
|
-
'The runtime has activated a visible execution-plan node inside this workstream.',
|
|
242
|
-
`Complete node "${planTurn.nodeSpec.label}" for plan "${planTurn.planTitle}".`,
|
|
243
|
-
'Use only the node contract, resolved input, input artifacts, and upstream handoff context provided here.',
|
|
244
|
-
'Do not ask the user for more input and do not rely on unstated external context.',
|
|
245
|
-
'Do not submit placeholders, partial work, or speculative outputs.',
|
|
246
|
-
'Before submitting, satisfy every required deliverable, success criterion, and completion check for this node.',
|
|
247
|
-
'Deliverables must use the exact artifact names and kinds declared in the node contract.',
|
|
248
|
-
'If a deliverable declares schemaRef, include the same schemaRef and a payload that satisfies that schema.',
|
|
249
|
-
'If outputSchemaRef is declared, structuredOutput must satisfy that schema before you submit.',
|
|
250
|
-
`When finished, call ${SUBMIT_PLAN_TURN_RESULT_TOOL_NAME} exactly once.`,
|
|
251
|
-
'Always include durable handoffContext for downstream nodes when you submit the final result.',
|
|
252
|
-
'Do not ask the user for confirmation and do not create or replace execution plans in this turn.',
|
|
253
|
-
JSON.stringify(payload, null, 2),
|
|
254
|
-
'</plan-turn-execution>',
|
|
255
|
-
].join('\n')
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function describePlanTurnDeliverable(deliverable: PlanNodeSpecRecord['deliverables'][number]): string {
|
|
259
|
-
return [
|
|
260
|
-
`- ${deliverable.name}`,
|
|
261
|
-
`kind=${deliverable.kind}`,
|
|
262
|
-
deliverable.required ? 'required' : 'optional',
|
|
263
|
-
deliverable.schemaRef ? `schemaRef=${deliverable.schemaRef}` : undefined,
|
|
264
|
-
deliverable.description ? `description=${deliverable.description}` : undefined,
|
|
265
|
-
]
|
|
266
|
-
.filter(Boolean)
|
|
267
|
-
.join(' | ')
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function describePlanTurnCompletionCheck(check: PlanNodeSpecRecord['completionChecks'][number]): string {
|
|
271
|
-
return [
|
|
272
|
-
`- ${check.description}`,
|
|
273
|
-
`type=${check.type}`,
|
|
274
|
-
check.blocking ? 'blocking' : 'warning',
|
|
275
|
-
Object.keys(check.config).length > 0 ? `config=${JSON.stringify(check.config)}` : undefined,
|
|
276
|
-
]
|
|
277
|
-
.filter(Boolean)
|
|
278
|
-
.join(' | ')
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function buildPlanTurnResultContractSection(planTurn: WorkstreamPlanTurnContext): string {
|
|
282
|
-
const requiredDeliverables = planTurn.nodeSpec.deliverables.filter((deliverable) => deliverable.required)
|
|
283
|
-
const completionCheckOutputHints = buildCompletionCheckStructuredOutputHints(planTurn.nodeSpec)
|
|
284
|
-
const deliverableLines =
|
|
285
|
-
planTurn.nodeSpec.deliverables.length > 0
|
|
286
|
-
? planTurn.nodeSpec.deliverables.map(describePlanTurnDeliverable)
|
|
287
|
-
: ['- none']
|
|
288
|
-
const completionCheckLines =
|
|
289
|
-
planTurn.nodeSpec.completionChecks.length > 0
|
|
290
|
-
? planTurn.nodeSpec.completionChecks.map(describePlanTurnCompletionCheck)
|
|
291
|
-
: ['- none']
|
|
292
|
-
|
|
293
|
-
return [
|
|
294
|
-
'<plan-turn-result-contract>',
|
|
295
|
-
`Call ${SUBMIT_PLAN_TURN_RESULT_TOOL_NAME} exactly once with a result object that passes node validation.`,
|
|
296
|
-
'Validation is strict. Missing required artifacts, schema mismatches, or failed completion checks will fail the node run.',
|
|
297
|
-
`Required artifacts: ${requiredDeliverables.length > 0 ? requiredDeliverables.map((deliverable) => deliverable.name).join(', ') : 'none'}`,
|
|
298
|
-
`Structured output: ${
|
|
299
|
-
planTurn.nodeSpec.outputSchemaRef
|
|
300
|
-
? `required and must match schema "${planTurn.nodeSpec.outputSchemaRef}"`
|
|
301
|
-
: 'optional unless needed by a completion check'
|
|
302
|
-
}`,
|
|
303
|
-
'Deliverables:',
|
|
304
|
-
...deliverableLines,
|
|
305
|
-
'Completion checks:',
|
|
306
|
-
...completionCheckLines,
|
|
307
|
-
...(completionCheckOutputHints.length > 0
|
|
308
|
-
? ['Structured output fields required by completion checks:', ...completionCheckOutputHints]
|
|
309
|
-
: []),
|
|
310
|
-
'Include notes with a concise completion summary grounded in the submitted artifacts and structuredOutput.',
|
|
311
|
-
'Always include handoffContext for downstream execution with a durable summary, key decisions, open questions, risks, recommendations, and references when relevant.',
|
|
312
|
-
'</plan-turn-result-contract>',
|
|
313
|
-
].join('\n')
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function buildPlanTurnSubmitToolDescription(planTurn: WorkstreamPlanTurnContext): string {
|
|
317
|
-
const requiredArtifacts =
|
|
318
|
-
planTurn.nodeSpec.deliverables
|
|
319
|
-
.filter((deliverable) => deliverable.required)
|
|
320
|
-
.map((deliverable) => `${deliverable.name} (${deliverable.kind})`)
|
|
321
|
-
.join(', ') || 'none'
|
|
322
|
-
|
|
323
|
-
return [
|
|
324
|
-
'Submit the final result for the active plan-triggered node turn.',
|
|
325
|
-
'Call this exactly once when the node output is complete.',
|
|
326
|
-
`Required artifacts: ${requiredArtifacts}.`,
|
|
327
|
-
`Structured output: ${
|
|
328
|
-
planTurn.nodeSpec.outputSchemaRef ? `must satisfy ${planTurn.nodeSpec.outputSchemaRef}` : 'optional'
|
|
329
|
-
}.`,
|
|
330
|
-
'Do not submit partial results. Include durable handoffContext for downstream nodes.',
|
|
331
|
-
].join(' ')
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function buildPlanTurnPromptMessage(planTurn: WorkstreamPlanTurnContext): ChatMessage {
|
|
335
|
-
return {
|
|
336
|
-
id: Bun.randomUUIDv7(),
|
|
337
|
-
role: 'user',
|
|
338
|
-
parts: [
|
|
339
|
-
{
|
|
340
|
-
type: 'text',
|
|
341
|
-
text: `Execute the active plan node "${planTurn.nodeSpec.label}" now and submit the result with ${SUBMIT_PLAN_TURN_RESULT_TOOL_NAME}.`,
|
|
342
|
-
},
|
|
343
|
-
],
|
|
344
|
-
metadata: { trigger: 'plan-turn', planRunId: planTurn.runId, planNodeId: planTurn.nodeId, createdAt: Date.now() },
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function buildUpstreamHandoffSection(upstreamHandoffs: PlanTurnUpstreamHandoff[]): string | undefined {
|
|
349
|
-
if (upstreamHandoffs.length === 0) {
|
|
350
|
-
return undefined
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return [
|
|
354
|
-
'<upstream-handoff>',
|
|
355
|
-
JSON.stringify(
|
|
356
|
-
upstreamHandoffs.map((handoff) => ({
|
|
357
|
-
nodeId: handoff.nodeId,
|
|
358
|
-
label: handoff.label,
|
|
359
|
-
ownerRef: handoff.ownerRef,
|
|
360
|
-
ownerType: handoff.ownerType,
|
|
361
|
-
handoffContext: handoff.handoffContext,
|
|
362
|
-
})),
|
|
363
|
-
null,
|
|
364
|
-
2,
|
|
365
|
-
),
|
|
366
|
-
'</upstream-handoff>',
|
|
367
|
-
].join('\n')
|
|
368
|
-
}
|
|
369
|
-
|
|
370
156
|
function applyPlanTurnToolPolicy(tools: ToolSet, nodeSpec: PlanNodeSpecRecord): ToolSet {
|
|
371
157
|
const blockedToolNames = new Set([...OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES, ...nodeSpec.toolPolicy.deny])
|
|
372
158
|
const allowList = nodeSpec.toolPolicy.allow.length > 0 ? new Set(nodeSpec.toolPolicy.allow) : null
|
|
@@ -452,29 +238,6 @@ function upsertChatHistoryMessage(messages: ChatMessage[], nextMessage: ChatMess
|
|
|
452
238
|
return nextMessages
|
|
453
239
|
}
|
|
454
240
|
|
|
455
|
-
function buildRecentActivityChatDeepLink(params: {
|
|
456
|
-
workstream: NormalizedWorkstream
|
|
457
|
-
workstreamId: string
|
|
458
|
-
visibleAgentId: string
|
|
459
|
-
}): { route: string; search: Record<string, string> } {
|
|
460
|
-
if (params.workstream.mode === 'direct') {
|
|
461
|
-
return { route: 'direct-workstream', search: { workstreamId: params.workstreamId, agentId: params.visibleAgentId } }
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return { route: 'group-workstream', search: { workstreamId: params.workstreamId } }
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function buildRecentActivityChatSystemTitle(params: {
|
|
468
|
-
workstream: NormalizedWorkstream
|
|
469
|
-
visibleAgentId: string
|
|
470
|
-
}): string {
|
|
471
|
-
if (params.workstream.mode === 'direct') {
|
|
472
|
-
return `Conversation with ${agentDisplayNames[params.visibleAgentId]}`
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return params.workstream.title.trim() || 'Workstream update'
|
|
476
|
-
}
|
|
477
|
-
|
|
478
241
|
interface StreamAgentResponseContext {
|
|
479
242
|
turnHooks: ReturnType<typeof getTurnHooks>
|
|
480
243
|
workstream: NormalizedWorkstream
|
|
@@ -489,7 +252,7 @@ interface StreamAgentResponseContext {
|
|
|
489
252
|
getExecutionPlanInstructionSections: () => Promise<string[] | undefined>
|
|
490
253
|
getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
|
|
491
254
|
getWorkstreamStateSection: () => Promise<string | undefined>
|
|
492
|
-
getLearnedSkillsSection: (agentId: string) => Promise<string | undefined>
|
|
255
|
+
getLearnedSkillsSection: (agentId: string, queryText?: string) => Promise<string | undefined>
|
|
493
256
|
promptContext: { systemWorkspaceDetails?: string }
|
|
494
257
|
retrievedKnowledgeSection: string | undefined
|
|
495
258
|
memoryBlock: string
|
|
@@ -509,6 +272,7 @@ interface StreamAgentResponseParams {
|
|
|
509
272
|
}
|
|
510
273
|
skills?: string[]
|
|
511
274
|
additionalInstructionSections?: string[]
|
|
275
|
+
includeExecutionPlanTools?: boolean
|
|
512
276
|
writer?: UIMessageStreamWriter<ChatMessage>
|
|
513
277
|
stopWhen?: StopCondition<ToolSet> | Array<StopCondition<ToolSet>>
|
|
514
278
|
prepareStep?: PrepareStepFunction<ToolSet>
|
|
@@ -541,12 +305,27 @@ async function streamAgentResponse(
|
|
|
541
305
|
)
|
|
542
306
|
agentTimer.step('hook:resolveAgent')
|
|
543
307
|
const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? streamParams.agentId
|
|
308
|
+
const latestUserMessage = [...streamParams.messages].reverse().find((message) => message.role === 'user')
|
|
309
|
+
const latestUserMessageText = latestUserMessage ? extractMessageText(latestUserMessage).trim() : undefined
|
|
544
310
|
const [preSeededMemoriesSection, workstreamStateSection, learnedSkillsSection] = await Promise.all([
|
|
545
311
|
ctx.getPreSeededMemoriesSection(resolvedAgentId),
|
|
546
312
|
ctx.getWorkstreamStateSection(),
|
|
547
|
-
ctx.getLearnedSkillsSection(resolvedAgentId),
|
|
313
|
+
ctx.getLearnedSkillsSection(resolvedAgentId, latestUserMessageText),
|
|
548
314
|
])
|
|
549
315
|
agentTimer.step('parallel-fetch(memories+state+skills)')
|
|
316
|
+
const toolNames = new Set(Object.keys(streamParams.tools))
|
|
317
|
+
const hasRetrievalTools = [
|
|
318
|
+
'memorySearch',
|
|
319
|
+
'conversationSearch',
|
|
320
|
+
'queryKnowledge',
|
|
321
|
+
'researchTopic',
|
|
322
|
+
'fetchWebpage',
|
|
323
|
+
'inspectWebsite',
|
|
324
|
+
].some((toolName) => toolNames.has(toolName))
|
|
325
|
+
const hasDomainRoutingSkills =
|
|
326
|
+
(streamParams.skills ?? []).some((skill) => skill.startsWith('cpo-') || skill.startsWith('mentor-')) ||
|
|
327
|
+
resolvedAgentId === 'cpo' ||
|
|
328
|
+
resolvedAgentId === 'mentor'
|
|
550
329
|
const config = getAgentRuntimeConfig({
|
|
551
330
|
agentId: resolvedAgentId,
|
|
552
331
|
workstreamMode: ctx.workstream.mode,
|
|
@@ -560,6 +339,12 @@ async function streamAgentResponse(
|
|
|
560
339
|
workstreamMemoryBlock: ctx.memoryBlock,
|
|
561
340
|
workstreamStateSection,
|
|
562
341
|
learnedSkillsSection,
|
|
342
|
+
userMessageText: latestUserMessageText,
|
|
343
|
+
ruleOptions: {
|
|
344
|
+
includeExecutionPlanRule: streamParams.includeExecutionPlanTools,
|
|
345
|
+
includeMemr3Rule: hasRetrievalTools,
|
|
346
|
+
includeDomainReasoningFallbackRule: hasDomainRoutingSkills,
|
|
347
|
+
},
|
|
563
348
|
additionalInstructionSections: mergeInstructionSections(
|
|
564
349
|
executionPlanInstructionSections,
|
|
565
350
|
streamParams.additionalInstructionSections,
|
|
@@ -651,165 +436,12 @@ async function streamAgentResponse(
|
|
|
651
436
|
return responseMessage
|
|
652
437
|
}
|
|
653
438
|
|
|
654
|
-
interface PostTurnSideEffectsParams {
|
|
655
|
-
workstream: NormalizedWorkstream
|
|
656
|
-
workstreamRef: RecordIdRef
|
|
657
|
-
orgRef: RecordIdRef
|
|
658
|
-
userRef: RecordIdRef
|
|
659
|
-
userName?: string | null
|
|
660
|
-
orgIdString: string
|
|
661
|
-
workstreamIdString: string
|
|
662
|
-
onboardingActive: boolean
|
|
663
|
-
workspace: unknown
|
|
664
|
-
allAssistantMessages: ChatMessage[]
|
|
665
|
-
referenceUserMessage: ChatMessage | undefined
|
|
666
|
-
referenceUserMessageId: string
|
|
667
|
-
recentHistory: ChatMessage[]
|
|
668
|
-
listReadableUploads: () => ReturnType<typeof listReadableUploadsFromChatMessages>
|
|
669
|
-
memoryBlock: string
|
|
670
|
-
visibleWorkstreamAgentId: string | null | undefined
|
|
671
|
-
defaultLeadAgentId: string
|
|
672
|
-
latestWorkstreamRecord: WorkstreamRecord
|
|
673
|
-
latestPersistedState: WorkstreamState | null
|
|
674
|
-
turnHooks: ReturnType<typeof getTurnHooks>
|
|
675
|
-
buildContextResult: Record<string, unknown> | null
|
|
676
|
-
isUserTurn: boolean
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
async function runPostTurnSideEffects(params: PostTurnSideEffectsParams): Promise<void> {
|
|
680
|
-
const turnCount = await workstreamService.incrementTurnCount(params.workstreamRef)
|
|
681
|
-
const agentMessages = buildAgentHistoryMessages(params.allAssistantMessages)
|
|
682
|
-
const historyMessagesForMemory = appendPersistedWorkstreamContextToHistoryMessages(
|
|
683
|
-
toHistoryMessages(params.recentHistory),
|
|
684
|
-
{ compactionSummary: params.latestWorkstreamRecord.compactionSummary, persistedState: params.latestPersistedState },
|
|
685
|
-
)
|
|
686
|
-
|
|
687
|
-
const userMessageText = params.referenceUserMessage ? extractMessageText(params.referenceUserMessage).trim() : ''
|
|
688
|
-
const readableUploads = params.listReadableUploads()
|
|
689
|
-
const attachmentMetadataContext = buildReadableUploadMetadataContext(readableUploads)
|
|
690
|
-
const hasAttachmentContext = Boolean(attachmentMetadataContext)
|
|
691
|
-
const shouldExtractMemory = params.onboardingActive
|
|
692
|
-
? shouldEnqueueOnboardingPostChatMemory({
|
|
693
|
-
onboardingActive: params.onboardingActive,
|
|
694
|
-
userMessageText,
|
|
695
|
-
hasAttachmentContext,
|
|
696
|
-
agentMessageCount: agentMessages.length,
|
|
697
|
-
})
|
|
698
|
-
: shouldEnqueueMemoryExtraction({ onboardingActive: params.onboardingActive, turnCount }) &&
|
|
699
|
-
userMessageText.length > 0
|
|
700
|
-
|
|
701
|
-
if (shouldExtractMemory) {
|
|
702
|
-
const memoryUserMessage = userMessageText || 'User uploaded attachment(s).'
|
|
703
|
-
await safeEnqueue(
|
|
704
|
-
() =>
|
|
705
|
-
enqueuePostChatMemory({
|
|
706
|
-
orgId: params.orgIdString,
|
|
707
|
-
workstreamId: params.workstreamIdString,
|
|
708
|
-
sourceId: params.referenceUserMessageId,
|
|
709
|
-
onboardStatus: readOptionalString((params.workspace as { onboardStatus?: unknown }).onboardStatus),
|
|
710
|
-
userMessage: memoryUserMessage,
|
|
711
|
-
historyMessages: historyMessagesForMemory,
|
|
712
|
-
agentMessages,
|
|
713
|
-
memoryBlock: params.memoryBlock.trim() ? params.memoryBlock : undefined,
|
|
714
|
-
attachmentContext: attachmentMetadataContext,
|
|
715
|
-
}),
|
|
716
|
-
{ operationName: 'post-chat memory extraction enqueue' },
|
|
717
|
-
)
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
if (params.isUserTurn && params.referenceUserMessage) {
|
|
721
|
-
const conversationSummary = buildConversationSummary({
|
|
722
|
-
userMessageText,
|
|
723
|
-
assistantMessages: params.allAssistantMessages,
|
|
724
|
-
})
|
|
725
|
-
if (conversationSummary) {
|
|
726
|
-
const effectiveAgentId = params.visibleWorkstreamAgentId ?? params.defaultLeadAgentId
|
|
727
|
-
const recentActivityResult = await recentActivityService.recordEvent({
|
|
728
|
-
orgId: params.orgRef,
|
|
729
|
-
userId: params.userRef,
|
|
730
|
-
source: 'system',
|
|
731
|
-
event: {
|
|
732
|
-
sourceEventId: `chat-turn:${params.referenceUserMessageId}`,
|
|
733
|
-
kind: 'chat.turn.completed',
|
|
734
|
-
targetKind: 'workstream',
|
|
735
|
-
targetId: params.workstreamIdString,
|
|
736
|
-
mergeKey: `workstream:${params.workstreamIdString}`,
|
|
737
|
-
title: buildRecentActivityChatSystemTitle({
|
|
738
|
-
workstream: params.workstream,
|
|
739
|
-
visibleAgentId: effectiveAgentId,
|
|
740
|
-
}),
|
|
741
|
-
sourceLabel: agentDisplayNames[effectiveAgentId],
|
|
742
|
-
deepLink: buildRecentActivityChatDeepLink({
|
|
743
|
-
workstream: params.workstream,
|
|
744
|
-
workstreamId: params.workstreamIdString,
|
|
745
|
-
visibleAgentId: effectiveAgentId,
|
|
746
|
-
}),
|
|
747
|
-
metadata: {
|
|
748
|
-
agentId: effectiveAgentId,
|
|
749
|
-
agentName: agentDisplayNames[effectiveAgentId],
|
|
750
|
-
workstreamId: params.workstreamIdString,
|
|
751
|
-
workstreamTitle: params.latestWorkstreamRecord.title ?? params.workstream.title,
|
|
752
|
-
workstreamMode: params.workstream.mode,
|
|
753
|
-
...(params.workstream.coreType ? { coreType: params.workstream.coreType } : {}),
|
|
754
|
-
userMessageText,
|
|
755
|
-
assistantSummary: conversationSummary,
|
|
756
|
-
messageId: params.referenceUserMessageId,
|
|
757
|
-
},
|
|
758
|
-
occurredAt: toIsoDateTimeString(params.referenceUserMessage.metadata?.createdAt ?? Date.now()),
|
|
759
|
-
},
|
|
760
|
-
})
|
|
761
|
-
|
|
762
|
-
await safeEnqueue(
|
|
763
|
-
async () => {
|
|
764
|
-
const enqueuePostChatOrgAction = getRuntimeAdapters().queues?.enqueuePostChatOrgAction
|
|
765
|
-
if (!enqueuePostChatOrgAction) {
|
|
766
|
-
return
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
await enqueuePostChatOrgAction({
|
|
770
|
-
orgId: params.orgIdString,
|
|
771
|
-
workstreamId: params.workstreamIdString,
|
|
772
|
-
sourceId: params.referenceUserMessageId,
|
|
773
|
-
sourceCreatedAt: params.referenceUserMessage?.metadata?.createdAt ?? Date.now(),
|
|
774
|
-
conversationSummary,
|
|
775
|
-
})
|
|
776
|
-
},
|
|
777
|
-
{ operationName: 'post-chat org action enqueue' },
|
|
778
|
-
)
|
|
779
|
-
|
|
780
|
-
if (recentActivityService.isMeaningfulRefinementCandidate(recentActivityResult.item)) {
|
|
781
|
-
await safeEnqueue(() => enqueueRecentActivityTitleRefinement({ activityId: recentActivityResult.item.id }), {
|
|
782
|
-
operationName: 'recent activity title refinement enqueue',
|
|
783
|
-
})
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
if (shouldEnqueueRegularDigestForWorkstream({ onboardingActive: params.onboardingActive, turnCount })) {
|
|
789
|
-
await safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: params.orgIdString }), {
|
|
790
|
-
operationName: 'regular chat memory digest enqueue',
|
|
791
|
-
})
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
if (shouldEnqueueSkillExtraction({ onboardingActive: params.onboardingActive, turnCount })) {
|
|
795
|
-
await safeEnqueue(() => enqueueSkillExtraction({ orgId: params.orgIdString }), {
|
|
796
|
-
operationName: 'skill extraction enqueue',
|
|
797
|
-
})
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
if (shouldEnqueueMemoryConsolidation({ onboardingActive: params.onboardingActive, turnCount })) {
|
|
801
|
-
await safeEnqueue(() => enqueueMemoryConsolidation({ scopeId: params.orgIdString }), {
|
|
802
|
-
operationName: 'memory consolidation enqueue',
|
|
803
|
-
})
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
439
|
export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams): Promise<PreparedWorkstreamTurn> {
|
|
808
440
|
const { workstream, workstreamRef, orgRef, userRef, userName } = params
|
|
809
441
|
const runtimeAdapters = getRuntimeAdapters()
|
|
810
442
|
const turnHooks = getTurnHooks()
|
|
811
443
|
const toolProviders = getToolProviders()
|
|
812
|
-
const workspaceProvider = runtimeAdapters.
|
|
444
|
+
const workspaceProvider = runtimeAdapters.workspaceProvider
|
|
813
445
|
const orgIdString = recordIdToString(orgRef, TABLES.ORGANIZATION)
|
|
814
446
|
const userIdString = recordIdToString(userRef, TABLES.USER)
|
|
815
447
|
const workstreamIdString = recordIdToString(workstreamRef, TABLES.WORKSTREAM)
|
|
@@ -843,8 +475,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
843
475
|
|
|
844
476
|
const timer = lotaDebugLogger.timer('prepare')
|
|
845
477
|
|
|
846
|
-
// Start workspace fetch early
|
|
847
|
-
const workspacePromise =
|
|
478
|
+
// Start workspace fetch early unless approval handling will short-circuit the turn.
|
|
479
|
+
const workspacePromise =
|
|
480
|
+
params.kind !== 'approvalContinuation' && workspaceProvider
|
|
481
|
+
? workspaceProvider.getWorkspace(orgRef)
|
|
482
|
+
: Promise.resolve({})
|
|
848
483
|
|
|
849
484
|
const workstreamRecord = await waitForWorkstreamCompactionIfNeeded(workstreamRef)
|
|
850
485
|
timer.step('compaction-gate')
|
|
@@ -875,7 +510,27 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
875
510
|
workstreamRef,
|
|
876
511
|
persistedCompactionCursor,
|
|
877
512
|
)
|
|
878
|
-
|
|
513
|
+
let recentHistoryPromise: Promise<ChatMessage[]> | null = null
|
|
514
|
+
const loadRecentHistory = async (): Promise<ChatMessage[]> => {
|
|
515
|
+
if (!recentHistoryPromise) {
|
|
516
|
+
recentHistoryPromise = workstreamMessageService
|
|
517
|
+
.listRecentMessages(workstreamRef, 64)
|
|
518
|
+
.then(async (persistedRecentHistory) => {
|
|
519
|
+
if (persistedRecentHistory.length === 0) {
|
|
520
|
+
return [] as ChatMessage[]
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const messages = await validateUIMessages<ChatMessage>({
|
|
524
|
+
messages: persistedRecentHistory,
|
|
525
|
+
metadataSchema: messageMetadataSchema,
|
|
526
|
+
dataSchemas: dataPartsSchemas,
|
|
527
|
+
})
|
|
528
|
+
return messages.map(hydrateMessageFileUrls)
|
|
529
|
+
})
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return await recentHistoryPromise
|
|
533
|
+
}
|
|
879
534
|
|
|
880
535
|
let userMessage: ChatMessage | undefined
|
|
881
536
|
if (inputMessage) {
|
|
@@ -888,34 +543,15 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
888
543
|
}
|
|
889
544
|
}
|
|
890
545
|
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
? await workspaceProvider.readProfileProjectionState?.(workspace)
|
|
901
|
-
: undefined
|
|
902
|
-
timer.step('workspace-profile-state')
|
|
903
|
-
const [liveHistory, recentHistory] = await Promise.all([
|
|
904
|
-
persistedLiveHistory.length === 0
|
|
905
|
-
? Promise.resolve([] as ChatMessage[])
|
|
906
|
-
: validateUIMessages<ChatMessage>({
|
|
907
|
-
messages: persistedLiveHistory,
|
|
908
|
-
metadataSchema: messageMetadataSchema,
|
|
909
|
-
dataSchemas: dataPartsSchemas,
|
|
910
|
-
}).then((messages) => messages.map(hydrateMessageFileUrls)),
|
|
911
|
-
persistedRecentHistory.length === 0
|
|
912
|
-
? Promise.resolve([] as ChatMessage[])
|
|
913
|
-
: validateUIMessages<ChatMessage>({
|
|
914
|
-
messages: persistedRecentHistory,
|
|
915
|
-
metadataSchema: messageMetadataSchema,
|
|
916
|
-
dataSchemas: dataPartsSchemas,
|
|
917
|
-
}).then((messages) => messages.map(hydrateMessageFileUrls)),
|
|
918
|
-
])
|
|
546
|
+
const persistedLiveHistory = await persistedLiveHistoryPromise
|
|
547
|
+
timer.step('fetch-history')
|
|
548
|
+
const liveHistory = await (persistedLiveHistory.length === 0
|
|
549
|
+
? Promise.resolve([] as ChatMessage[])
|
|
550
|
+
: validateUIMessages<ChatMessage>({
|
|
551
|
+
messages: persistedLiveHistory,
|
|
552
|
+
metadataSchema: messageMetadataSchema,
|
|
553
|
+
dataSchemas: dataPartsSchemas,
|
|
554
|
+
}).then((messages) => messages.map(hydrateMessageFileUrls)))
|
|
919
555
|
timer.step('validate+hydrate-history')
|
|
920
556
|
|
|
921
557
|
if (userMessage && shouldPersistInputMessage) {
|
|
@@ -938,6 +574,18 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
938
574
|
? extractMessageText(referenceUserMessage).trim()
|
|
939
575
|
: ''
|
|
940
576
|
|
|
577
|
+
const respondedBy = recordIdToString(userRef, TABLES.USER)
|
|
578
|
+
if (params.kind === 'approvalContinuation') {
|
|
579
|
+
await executionPlanService.applyApprovalResponseFromMessages({
|
|
580
|
+
workstreamId: workstreamRef,
|
|
581
|
+
approvalMessages: params.approvalMessages,
|
|
582
|
+
respondedBy,
|
|
583
|
+
})
|
|
584
|
+
timer.step('approval-continuation')
|
|
585
|
+
|
|
586
|
+
return { originalMessages, run: async () => ({ inputMessageId: referenceUserMessage?.id, assistantMessages: [] }) }
|
|
587
|
+
}
|
|
588
|
+
|
|
941
589
|
if (
|
|
942
590
|
params.kind === 'userTurn' &&
|
|
943
591
|
workstream.mode === 'group' &&
|
|
@@ -952,7 +600,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
952
600
|
)
|
|
953
601
|
}
|
|
954
602
|
|
|
955
|
-
const onboardingActive = workspaceLifecycleState?.bootstrapActive ?? false
|
|
956
603
|
if (workstream.core && !workstream.coreType) {
|
|
957
604
|
throw new WorkstreamTurnError('Core workstreams require a core type.', 400)
|
|
958
605
|
}
|
|
@@ -963,112 +610,31 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
963
610
|
params.agentIdOverride ??
|
|
964
611
|
(workstream.mode === 'direct' ? workstream.agentId : (coreWorkstreamProfile?.config.agentId ?? defaultLeadAgentId))
|
|
965
612
|
const coreInstructionSections = coreWorkstreamProfile ? [coreWorkstreamProfile.instructions] : undefined
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
const [linearInstallation, githubInstallation, indexedRepoContext, recentDomainEvents, promptSummary] =
|
|
980
|
-
await Promise.all([
|
|
981
|
-
getLinearInstallationByOrgId ? (getLinearInstallationByOrgId(orgRef) as Promise<unknown>) : Promise.resolve(null),
|
|
982
|
-
getGithubInstallationForOrganization
|
|
983
|
-
? (getGithubInstallationForOrganization(orgIdString) as Promise<unknown>)
|
|
984
|
-
: Promise.resolve(null),
|
|
985
|
-
buildIndexedRepositoriesContext(orgIdString),
|
|
986
|
-
workspaceProvider?.listRecentDomainEvents?.(orgRef, 5) ?? Promise.resolve([] as Array<Record<string, unknown>>),
|
|
987
|
-
workspaceProvider?.buildPromptSummary
|
|
988
|
-
? workspaceProvider.buildPromptSummary(orgRef).catch(() => undefined)
|
|
989
|
-
: Promise.resolve(undefined),
|
|
990
|
-
])
|
|
991
|
-
timer.step('parallel-context-fetch(plugins+repos+events+summary)')
|
|
992
|
-
let linearInstalled = Boolean(linearInstallation)
|
|
993
|
-
let githubInstalled = Boolean(githubInstallation)
|
|
994
|
-
let promptContext = buildAgentPromptContext({
|
|
995
|
-
workspaceName: workspaceProfileState?.workspaceName ?? readOptionalString((workspace as { name?: unknown }).name),
|
|
996
|
-
summaryBlock: workspaceProfileState?.summaryBlock,
|
|
997
|
-
structuredProfile: workspaceProfileState?.structuredProfile,
|
|
998
|
-
promptSummary,
|
|
999
|
-
userName: userName ?? undefined,
|
|
1000
|
-
recentDomainEvents,
|
|
613
|
+
const assembledContext = await assembleWorkstreamTurnContext({
|
|
614
|
+
workstream,
|
|
615
|
+
workstreamRef,
|
|
616
|
+
orgRef,
|
|
617
|
+
userRef,
|
|
618
|
+
userName,
|
|
619
|
+
orgIdString,
|
|
620
|
+
userIdString,
|
|
621
|
+
messageText,
|
|
622
|
+
workspacePromise,
|
|
623
|
+
workspaceProvider,
|
|
624
|
+
turnHooks,
|
|
625
|
+
onStep: (step) => timer.step(step),
|
|
1001
626
|
})
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
workstream,
|
|
1014
|
-
workstreamRef,
|
|
1015
|
-
orgRef,
|
|
1016
|
-
userRef,
|
|
1017
|
-
userName,
|
|
1018
|
-
workspace,
|
|
1019
|
-
onboardingActive,
|
|
1020
|
-
messageText,
|
|
1021
|
-
linearInstalled,
|
|
1022
|
-
githubInstalled,
|
|
1023
|
-
indexedRepoContext,
|
|
1024
|
-
promptContext,
|
|
1025
|
-
workspaceLifecycleState,
|
|
1026
|
-
workspaceProfileState,
|
|
1027
|
-
promptSummary,
|
|
1028
|
-
recentDomainEvents,
|
|
1029
|
-
retrievedKnowledgeSection,
|
|
1030
|
-
}),
|
|
1031
|
-
)
|
|
1032
|
-
timer.step('hook:buildContext')
|
|
1033
|
-
const buildContextPromptDetails = readOptionalString(buildContextResult?.systemWorkspaceDetails)
|
|
1034
|
-
if (buildContextPromptDetails) {
|
|
1035
|
-
promptContext = { systemWorkspaceDetails: buildContextPromptDetails }
|
|
1036
|
-
}
|
|
1037
|
-
const buildContextRetrievedKnowledge = readOptionalString(buildContextResult?.retrievedKnowledgeSection)
|
|
1038
|
-
if (buildContextRetrievedKnowledge !== undefined) {
|
|
1039
|
-
retrievedKnowledgeSection = buildContextRetrievedKnowledge
|
|
1040
|
-
}
|
|
1041
|
-
const buildContextLinearInstalled = readOptionalBoolean(buildContextResult?.linearInstalled)
|
|
1042
|
-
if (buildContextLinearInstalled !== undefined) {
|
|
1043
|
-
linearInstalled = buildContextLinearInstalled
|
|
1044
|
-
}
|
|
1045
|
-
const buildContextGithubInstalled = readOptionalBoolean(buildContextResult?.githubInstalled)
|
|
1046
|
-
if (buildContextGithubInstalled !== undefined) {
|
|
1047
|
-
githubInstalled = buildContextGithubInstalled
|
|
1048
|
-
}
|
|
1049
|
-
const hookInstructionSections = readInstructionSections(
|
|
1050
|
-
await turnHooks.buildExtraInstructionSections?.({
|
|
1051
|
-
workstream,
|
|
1052
|
-
workstreamRef,
|
|
1053
|
-
orgRef,
|
|
1054
|
-
userRef,
|
|
1055
|
-
userName,
|
|
1056
|
-
workspace,
|
|
1057
|
-
onboardingActive,
|
|
1058
|
-
messageText,
|
|
1059
|
-
linearInstalled,
|
|
1060
|
-
githubInstalled,
|
|
1061
|
-
indexedRepoContext,
|
|
1062
|
-
promptContext,
|
|
1063
|
-
workspaceLifecycleState,
|
|
1064
|
-
workspaceProfileState,
|
|
1065
|
-
promptSummary,
|
|
1066
|
-
recentDomainEvents,
|
|
1067
|
-
retrievedKnowledgeSection,
|
|
1068
|
-
context: buildContextResult,
|
|
1069
|
-
}),
|
|
1070
|
-
)
|
|
1071
|
-
timer.step('hook:buildExtraInstructionSections')
|
|
627
|
+
const {
|
|
628
|
+
workspace,
|
|
629
|
+
onboardingActive,
|
|
630
|
+
linearInstalled,
|
|
631
|
+
githubInstalled,
|
|
632
|
+
indexedRepoContext,
|
|
633
|
+
promptContext,
|
|
634
|
+
retrievedKnowledgeSection,
|
|
635
|
+
buildContextResult,
|
|
636
|
+
hookInstructionSections,
|
|
637
|
+
} = assembledContext
|
|
1072
638
|
|
|
1073
639
|
let memoryBlock = workstreamService.formatMemoryBlockForPrompt(workstreamRecord)
|
|
1074
640
|
let workstreamState = initialWorkstreamState
|
|
@@ -1091,17 +657,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1091
657
|
stripExecutionPlanFieldsFromWorkstreamState(workstreamState, executionPlans.length > 0),
|
|
1092
658
|
)
|
|
1093
659
|
}
|
|
1094
|
-
|
|
1095
|
-
if (params.kind === 'approvalContinuation') {
|
|
1096
|
-
const appliedApproval = await executionPlanService.applyApprovalResponseFromMessages({
|
|
1097
|
-
workstreamId: workstreamRef,
|
|
1098
|
-
approvalMessages: params.approvalMessages,
|
|
1099
|
-
respondedBy,
|
|
1100
|
-
})
|
|
1101
|
-
if (appliedApproval) {
|
|
1102
|
-
invalidateExecutionPlanInstructionSections()
|
|
1103
|
-
}
|
|
1104
|
-
} else if (userMessage) {
|
|
660
|
+
if (userMessage) {
|
|
1105
661
|
const appliedHumanInput = await executionPlanService.applyHumanInputFromUserMessage({
|
|
1106
662
|
workstreamId: workstreamRef,
|
|
1107
663
|
message: userMessage,
|
|
@@ -1112,6 +668,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1112
668
|
}
|
|
1113
669
|
}
|
|
1114
670
|
timer.step('execution-plan-input')
|
|
671
|
+
|
|
1115
672
|
const preSeededMemoriesByAgent = new Map<string, string | undefined>()
|
|
1116
673
|
const getPreSeededMemoriesSection = async (agentId: string): Promise<string | undefined> => {
|
|
1117
674
|
if (preSeededMemoriesByAgent.has(agentId)) {
|
|
@@ -1128,17 +685,18 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1128
685
|
}
|
|
1129
686
|
|
|
1130
687
|
const learnedSkillsByAgent = new Map<string, string | undefined>()
|
|
1131
|
-
const getLearnedSkillsSection = async (agentId: string): Promise<string | undefined> => {
|
|
688
|
+
const getLearnedSkillsSection = async (agentId: string, queryText = messageText): Promise<string | undefined> => {
|
|
1132
689
|
if (onboardingActive) return undefined
|
|
1133
|
-
|
|
690
|
+
const cacheKey = `${agentId}::${queryText}`
|
|
691
|
+
if (learnedSkillsByAgent.has(cacheKey)) return learnedSkillsByAgent.get(cacheKey)
|
|
1134
692
|
|
|
1135
693
|
const section = await learnedSkillService
|
|
1136
|
-
.retrieveForTurn({ orgId: orgIdString, agentId, query:
|
|
694
|
+
.retrieveForTurn({ orgId: orgIdString, agentId, query: queryText, limit: 3, minConfidence: 0.6 })
|
|
1137
695
|
.catch((error) => {
|
|
1138
696
|
aiLogger.warn`Failed to retrieve learned skills for ${agentId}: ${error}`
|
|
1139
697
|
return undefined
|
|
1140
698
|
})
|
|
1141
|
-
learnedSkillsByAgent.set(
|
|
699
|
+
learnedSkillsByAgent.set(cacheKey, section)
|
|
1142
700
|
return section
|
|
1143
701
|
}
|
|
1144
702
|
|
|
@@ -1161,6 +719,36 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1161
719
|
latestUserMessageId: referenceUserMessageId,
|
|
1162
720
|
uploadMetadataText: buildReadableUploadMetadataText(listReadableUploads(extraMessages)),
|
|
1163
721
|
})
|
|
722
|
+
const buildTurnToolParams = (toolParams: {
|
|
723
|
+
agentId: string
|
|
724
|
+
mode: 'direct' | 'fixedWorkstreamMode' | 'workstreamMode'
|
|
725
|
+
memoryBlock: string
|
|
726
|
+
onAppendMemoryBlock: (value: string) => void
|
|
727
|
+
extraMessages?: ChatMessage[]
|
|
728
|
+
skills?: string[]
|
|
729
|
+
includeExecutionPlanTools: boolean
|
|
730
|
+
}) => ({
|
|
731
|
+
agentId: toolParams.agentId,
|
|
732
|
+
orgId: orgRef,
|
|
733
|
+
userId: userRef,
|
|
734
|
+
userName: userName ?? 'there',
|
|
735
|
+
workstreamId: workstreamRef,
|
|
736
|
+
orgIdString,
|
|
737
|
+
workstreamMode: workstream.mode,
|
|
738
|
+
mode: toolParams.mode,
|
|
739
|
+
linearInstalled,
|
|
740
|
+
onboardingActive,
|
|
741
|
+
githubInstalled,
|
|
742
|
+
provideRepoTool: indexedRepoContext.provideRepoTool,
|
|
743
|
+
skills: toolParams.skills,
|
|
744
|
+
defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[toolParams.agentId],
|
|
745
|
+
memoryBlock: toolParams.memoryBlock,
|
|
746
|
+
onAppendMemoryBlock: toolParams.onAppendMemoryBlock,
|
|
747
|
+
availableUploads: listReadableUploads(toolParams.extraMessages),
|
|
748
|
+
includeExecutionPlanTools: toolParams.includeExecutionPlanTools,
|
|
749
|
+
onExecutionPlanChanged: invalidateExecutionPlanInstructionSections,
|
|
750
|
+
context: buildContextResult,
|
|
751
|
+
})
|
|
1164
752
|
|
|
1165
753
|
timer.step('preparation-complete')
|
|
1166
754
|
|
|
@@ -1171,10 +759,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1171
759
|
const runTimer = lotaDebugLogger.timer('run')
|
|
1172
760
|
const serverRunId = Bun.randomUUIDv7()
|
|
1173
761
|
const runAbort = createServerRunAbortController(params.abortSignal)
|
|
1174
|
-
await
|
|
1175
|
-
workstreamService.setActiveRunId(workstreamRef, serverRunId),
|
|
1176
|
-
params.streamId ? workstreamService.setActiveStreamId(workstreamRef, params.streamId) : undefined,
|
|
1177
|
-
])
|
|
762
|
+
await workstreamService.setActiveTurn(workstreamRef, serverRunId, params.streamId ?? null)
|
|
1178
763
|
chatRunRegistry.register(serverRunId, runAbort.controller)
|
|
1179
764
|
runTimer.step('set-active-run+stream')
|
|
1180
765
|
|
|
@@ -1254,30 +839,19 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1254
839
|
const includeExecutionPlanTools =
|
|
1255
840
|
runParams.includeExecutionPlanTools ?? (runParams.mode !== 'fixedWorkstreamMode' && !onboardingActive)
|
|
1256
841
|
const rawTools: ToolSet = {
|
|
1257
|
-
...((await buildAgentTools(
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
skills: runParams.skills,
|
|
1271
|
-
defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[runParams.agentId],
|
|
1272
|
-
memoryBlock: runMemoryBlock,
|
|
1273
|
-
onAppendMemoryBlock: (value: string) => {
|
|
1274
|
-
runMemoryBlock = value
|
|
1275
|
-
},
|
|
1276
|
-
availableUploads: listReadableUploads(runParams.extraMessages),
|
|
1277
|
-
includeExecutionPlanTools,
|
|
1278
|
-
onExecutionPlanChanged: invalidateExecutionPlanInstructionSections,
|
|
1279
|
-
context: buildContextResult,
|
|
1280
|
-
})) as ToolSet),
|
|
842
|
+
...((await buildAgentTools(
|
|
843
|
+
buildTurnToolParams({
|
|
844
|
+
agentId: runParams.agentId,
|
|
845
|
+
mode: runParams.mode,
|
|
846
|
+
skills: runParams.skills,
|
|
847
|
+
memoryBlock: runMemoryBlock,
|
|
848
|
+
onAppendMemoryBlock: (value: string) => {
|
|
849
|
+
runMemoryBlock = value
|
|
850
|
+
},
|
|
851
|
+
extraMessages: runParams.extraMessages,
|
|
852
|
+
includeExecutionPlanTools,
|
|
853
|
+
}),
|
|
854
|
+
)) as ToolSet),
|
|
1281
855
|
...toolProviders,
|
|
1282
856
|
...runParams.extraTools,
|
|
1283
857
|
}
|
|
@@ -1292,6 +866,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1292
866
|
observer: createObserver(runParams.agentId),
|
|
1293
867
|
skills: runParams.skills,
|
|
1294
868
|
additionalInstructionSections: runParams.additionalInstructionSections,
|
|
869
|
+
includeExecutionPlanTools,
|
|
1295
870
|
writer,
|
|
1296
871
|
})
|
|
1297
872
|
|
|
@@ -1306,12 +881,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1306
881
|
)
|
|
1307
882
|
}
|
|
1308
883
|
|
|
1309
|
-
// Execution-plan approval continuations mutate plan state and persist the approval message,
|
|
1310
|
-
// but they do not begin a new visible agent turn.
|
|
1311
|
-
if (params.kind === 'approvalContinuation') {
|
|
1312
|
-
return { inputMessageId: referenceUserMessage?.id, assistantMessages: [] }
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
884
|
if (params.kind === 'planTurn') {
|
|
1316
885
|
const planTurn = params.planTurn
|
|
1317
886
|
const submitPlanTurnNodeResultTool = createTool({
|
|
@@ -1330,10 +899,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1330
899
|
await runVisibleAgent({
|
|
1331
900
|
agentId: planTurn.nodeSpec.owner.ref,
|
|
1332
901
|
mode: workstream.mode === 'direct' ? 'direct' : 'workstreamMode',
|
|
1333
|
-
additionalInstructionSections:
|
|
1334
|
-
[buildPlanTurnExecutionSection(planTurn), buildPlanTurnResultContractSection(planTurn)],
|
|
1335
|
-
optionalInstructionSection(buildUpstreamHandoffSection(planTurn.upstreamHandoffs)),
|
|
1336
|
-
),
|
|
902
|
+
additionalInstructionSections: buildPlanTurnInstructionSections(planTurn),
|
|
1337
903
|
extraMessages: [buildPlanTurnPromptMessage(planTurn)],
|
|
1338
904
|
includeExecutionPlanTools: false,
|
|
1339
905
|
extraTools: { [SUBMIT_PLAN_TURN_RESULT_TOOL_NAME]: submitPlanTurnNodeResultTool },
|
|
@@ -1345,120 +911,43 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1345
911
|
description: 'Consult one specialist teammate for domain-specific guidance before replying to the user.',
|
|
1346
912
|
inputSchema: ConsultSpecialistArgsSchema,
|
|
1347
913
|
execute: async function* (
|
|
1348
|
-
{ agentId, task }:
|
|
914
|
+
{ agentId, task }: ConsultSpecialistArgs,
|
|
1349
915
|
{ abortSignal: toolAbortSignal }: { abortSignal?: AbortSignal },
|
|
1350
916
|
) {
|
|
1351
|
-
let specialistMemoryBlock = memoryBlock
|
|
1352
917
|
const specialistTaskMessage = buildSpecialistTaskMessage({ agentId, task })
|
|
1353
|
-
const specialistTools = await buildAgentTools({
|
|
1354
|
-
agentId,
|
|
1355
|
-
orgId: orgRef,
|
|
1356
|
-
userId: userRef,
|
|
1357
|
-
userName: userName ?? 'there',
|
|
1358
|
-
workstreamId: workstreamRef,
|
|
1359
|
-
orgIdString,
|
|
1360
|
-
workstreamMode: workstream.mode,
|
|
1361
|
-
mode: 'fixedWorkstreamMode',
|
|
1362
|
-
linearInstalled,
|
|
1363
|
-
onboardingActive,
|
|
1364
|
-
githubInstalled,
|
|
1365
|
-
provideRepoTool: indexedRepoContext.provideRepoTool,
|
|
1366
|
-
defaultRepoSections: indexedRepoContext.defaultSectionsByAgent[agentId],
|
|
1367
|
-
memoryBlock: specialistMemoryBlock,
|
|
1368
|
-
onAppendMemoryBlock: (value: string) => {
|
|
1369
|
-
specialistMemoryBlock = value
|
|
1370
|
-
},
|
|
1371
|
-
availableUploads: listReadableUploads([specialistTaskMessage]),
|
|
1372
|
-
includeExecutionPlanTools: false,
|
|
1373
|
-
context: buildContextResult,
|
|
1374
|
-
})
|
|
1375
|
-
|
|
1376
|
-
const [
|
|
1377
|
-
specialistExecutionPlanInstructionSections,
|
|
1378
|
-
specialistPreSeededMemories,
|
|
1379
|
-
specialistWorkstreamState,
|
|
1380
|
-
specialistLearnedSkills,
|
|
1381
|
-
] = await Promise.all([
|
|
1382
|
-
getExecutionPlanInstructionSections(),
|
|
1383
|
-
getPreSeededMemoriesSection(agentId),
|
|
1384
|
-
getWorkstreamStateSection(),
|
|
1385
|
-
getLearnedSkillsSection(agentId),
|
|
1386
|
-
])
|
|
1387
|
-
const specialistConfig = getAgentRuntimeConfig({
|
|
1388
|
-
agentId,
|
|
1389
|
-
workstreamMode: workstream.mode,
|
|
1390
|
-
mode: 'fixedWorkstreamMode',
|
|
1391
|
-
onboardingActive,
|
|
1392
|
-
linearInstalled,
|
|
1393
|
-
systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
|
|
1394
|
-
preSeededMemoriesSection: specialistPreSeededMemories,
|
|
1395
|
-
retrievedKnowledgeSection,
|
|
1396
|
-
workstreamMemoryBlock: specialistMemoryBlock,
|
|
1397
|
-
workstreamStateSection: specialistWorkstreamState,
|
|
1398
|
-
learnedSkillsSection: specialistLearnedSkills,
|
|
1399
|
-
additionalInstructionSections: mergeInstructionSections(
|
|
1400
|
-
specialistExecutionPlanInstructionSections,
|
|
1401
|
-
coreInstructionSections,
|
|
1402
|
-
hookInstructionSections,
|
|
1403
|
-
),
|
|
1404
|
-
context: buildContextResult,
|
|
1405
|
-
}) as Record<string, unknown>
|
|
1406
|
-
const observer = createObserver(agentId)
|
|
1407
|
-
const agent = createAgent[specialistConfig.id as string]({
|
|
1408
|
-
mode: 'fixedWorkstreamMode',
|
|
1409
|
-
tools: { ...(specialistTools as ToolSet), ...toolProviders },
|
|
1410
|
-
extraInstructions: specialistConfig.extraInstructions,
|
|
1411
|
-
stopWhen: [stepCountIs(specialistConfig.maxSteps as number)],
|
|
1412
|
-
}) as ToolLoopAgent<never, ToolSet>
|
|
1413
|
-
const modelMessages = await convertToModelMessages(buildRunInputMessages([specialistTaskMessage]), {
|
|
1414
|
-
ignoreIncompleteToolCalls: true,
|
|
1415
|
-
})
|
|
1416
918
|
const specialistAbortSignal = toolAbortSignal ?? runAbort.signal
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
yield finalMessage
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
if (!finalMessage) {
|
|
1453
|
-
throw new Error(`Specialist ${agentId} did not produce a response message.`)
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
for (const toolError of collectToolOutputErrors({ responseMessage: finalMessage })) {
|
|
1457
|
-
aiLogger.warn`Tool execution failed (agent=${agentId}, tool=${toolError.toolName}, toolCallId=${toolError.toolCallId}): ${toolError.errorText}`
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
memoryBlock = specialistMemoryBlock
|
|
1461
|
-
return finalMessage
|
|
919
|
+
const { result: finalMessage, memoryBlock: nextMemoryBlock } = await runSpecialistSession({
|
|
920
|
+
initialMemoryBlock: memoryBlock,
|
|
921
|
+
buildTools: async ({ memoryBlock: currentMemoryBlock, onAppendMemoryBlock }) =>
|
|
922
|
+
(await buildAgentTools(
|
|
923
|
+
buildTurnToolParams({
|
|
924
|
+
agentId,
|
|
925
|
+
mode: 'fixedWorkstreamMode',
|
|
926
|
+
memoryBlock: currentMemoryBlock,
|
|
927
|
+
onAppendMemoryBlock,
|
|
928
|
+
extraMessages: [specialistTaskMessage],
|
|
929
|
+
includeExecutionPlanTools: false,
|
|
930
|
+
}),
|
|
931
|
+
)) as ToolSet,
|
|
932
|
+
run: async ({ tools, memoryBlock: currentMemoryBlock }) =>
|
|
933
|
+
await streamAgentResponse(
|
|
934
|
+
{ ...streamCtx, memoryBlock: currentMemoryBlock },
|
|
935
|
+
{
|
|
936
|
+
agentId,
|
|
937
|
+
mode: 'fixedWorkstreamMode',
|
|
938
|
+
messages: buildRunInputMessages([specialistTaskMessage]),
|
|
939
|
+
tools: { ...tools, ...toolProviders },
|
|
940
|
+
observer: createObserver(agentId),
|
|
941
|
+
additionalInstructionSections: coreInstructionSections,
|
|
942
|
+
includeExecutionPlanTools: false,
|
|
943
|
+
abortSignal: specialistAbortSignal,
|
|
944
|
+
},
|
|
945
|
+
),
|
|
946
|
+
})
|
|
947
|
+
const committedFinalMessage = withMessageCreatedAt(finalMessage, Date.now())
|
|
948
|
+
memoryBlock = nextMemoryBlock
|
|
949
|
+
yield committedFinalMessage
|
|
950
|
+
return committedFinalMessage
|
|
1462
951
|
},
|
|
1463
952
|
toModelOutput: ({ output }) => {
|
|
1464
953
|
const result = getChatMessageFromToolOutput(output)
|
|
@@ -1545,11 +1034,17 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1545
1034
|
})
|
|
1546
1035
|
},
|
|
1547
1036
|
unregisterRun: (runId) => chatRunRegistry.unregister(runId),
|
|
1548
|
-
clearActiveRunId: (runId) =>
|
|
1037
|
+
clearActiveRunId: async (runId) => {
|
|
1038
|
+
const activeStreamId = await workstreamService.getActiveStreamId(workstreamRef)
|
|
1039
|
+
await workstreamService.clearActiveTurn(workstreamRef, { runId, streamId: activeStreamId })
|
|
1040
|
+
},
|
|
1549
1041
|
disposeAbort: () => runAbort.dispose(),
|
|
1550
1042
|
activeStreamId: params.streamId,
|
|
1551
|
-
clearActiveStreamId: (streamId) =>
|
|
1552
|
-
workstreamService.
|
|
1043
|
+
clearActiveStreamId: async (streamId) => {
|
|
1044
|
+
const activeRunId = await workstreamService.getActiveRunId(workstreamRef)
|
|
1045
|
+
if (!activeRunId) return
|
|
1046
|
+
await workstreamService.clearActiveTurn(workstreamRef, { runId: activeRunId, streamId })
|
|
1047
|
+
},
|
|
1553
1048
|
})
|
|
1554
1049
|
|
|
1555
1050
|
if (allAssistantMessages.length > 0 && shouldProcessPostRunSideEffects) {
|
|
@@ -1566,15 +1061,13 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1566
1061
|
allAssistantMessages,
|
|
1567
1062
|
referenceUserMessage,
|
|
1568
1063
|
referenceUserMessageId,
|
|
1569
|
-
|
|
1064
|
+
loadRecentHistory,
|
|
1570
1065
|
listReadableUploads: () => listReadableUploads(),
|
|
1571
1066
|
memoryBlock,
|
|
1572
1067
|
visibleWorkstreamAgentId,
|
|
1573
1068
|
defaultLeadAgentId,
|
|
1574
1069
|
latestWorkstreamRecord,
|
|
1575
1070
|
latestPersistedState,
|
|
1576
|
-
turnHooks,
|
|
1577
|
-
buildContextResult,
|
|
1578
1071
|
isUserTurn: params.kind === 'userTurn',
|
|
1579
1072
|
})
|
|
1580
1073
|
}
|