@lota-sdk/core 0.2.3 → 0.3.1
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 +2 -2
- package/infrastructure/schema/00_thread.surql +73 -0
- package/infrastructure/schema/02_execution_plan.surql +10 -11
- package/infrastructure/schema/04_runtime_bootstrap.surql +1 -0
- package/infrastructure/schema/10_autonomous_job.surql +3 -3
- package/package.json +2 -2
- package/src/ai/definitions.ts +1 -1
- package/src/config/agent-defaults.ts +5 -5
- package/src/config/index.ts +1 -1
- package/src/config/thread-defaults.ts +72 -0
- package/src/create-runtime.ts +90 -94
- package/src/db/record-id.ts +21 -21
- package/src/db/service.ts +44 -40
- package/src/db/tables.ts +3 -3
- package/src/db/{workstream-message-row.ts → thread-message-row.ts} +3 -3
- package/src/queues/context-compaction.queue.ts +6 -6
- package/src/queues/plan-agent-heartbeat.queue.ts +3 -3
- package/src/queues/post-chat-memory.queue.ts +1 -1
- package/src/queues/title-generation.queue.ts +10 -13
- package/src/redis/index.ts +1 -1
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/agent-identity-overrides.ts +1 -1
- package/src/runtime/agent-runtime-policy.ts +19 -21
- package/src/runtime/chat-request-routing.ts +1 -1
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src/runtime/memory-digest-policy.ts +1 -1
- package/src/runtime/plugin-types.ts +1 -1
- package/src/runtime/post-turn-side-effects.ts +35 -35
- package/src/runtime/runtime-config.ts +24 -21
- package/src/runtime/runtime-extensions.ts +11 -11
- package/src/runtime/social-chat-agent-runner.ts +3 -3
- package/src/runtime/social-chat-history.ts +1 -1
- package/src/runtime/social-chat.ts +6 -6
- package/src/runtime/team-consultation-orchestrator.ts +1 -1
- package/src/runtime/{workstream-chat-helpers.ts → thread-chat-helpers.ts} +7 -7
- package/src/runtime/{workstream-plan-turn.ts → thread-plan-turn.ts} +11 -17
- package/src/runtime/{workstream-turn-context.ts → thread-turn-context.ts} +10 -10
- package/src/services/agent-activity.service.ts +39 -44
- package/src/services/agent-executor.service.ts +17 -19
- package/src/services/attachment.service.ts +4 -8
- package/src/services/autonomous-job.service.ts +29 -28
- package/src/services/context-compaction.service.ts +19 -29
- package/src/services/execution-plan.service.ts +58 -70
- package/src/services/global-orchestrator.service.ts +5 -5
- package/src/services/index.ts +6 -6
- package/src/services/memory.service.ts +1 -1
- package/src/services/monitoring-window.service.ts +2 -2
- package/src/services/mutating-approval.service.ts +7 -10
- package/src/services/node-workspace.service.ts +8 -7
- package/src/services/notification.service.ts +1 -1
- package/src/services/organization.service.ts +9 -9
- package/src/services/ownership-dispatcher.service.ts +13 -19
- package/src/services/plan-agent-heartbeat.service.ts +13 -13
- package/src/services/plan-agent-query.service.ts +7 -7
- package/src/services/plan-artifact.service.ts +1 -2
- package/src/services/plan-coordination.service.ts +4 -4
- package/src/services/plan-cycle.service.ts +7 -7
- package/src/services/plan-deadline.service.ts +4 -4
- package/src/services/plan-event-delivery.service.ts +8 -12
- package/src/services/plan-executor.service.ts +25 -39
- package/src/services/plan-run-data.ts +27 -8
- package/src/services/plan-run.service.ts +7 -9
- package/src/services/plan-scheduler.service.ts +4 -4
- package/src/services/plan-template.service.ts +2 -2
- package/src/services/plan-validator.service.ts +0 -11
- package/src/services/plugin-executor.service.ts +1 -1
- package/src/services/queue-job.service.ts +1 -1
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +4 -4
- package/src/services/system-executor.service.ts +2 -2
- package/src/services/{workstream-message.service.ts → thread-message.service.ts} +72 -76
- package/src/services/thread-plan-registry.service.ts +22 -0
- package/src/services/thread-title.service.ts +39 -0
- package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts} +148 -171
- package/src/services/{workstream-turn.ts → thread-turn.ts} +27 -31
- package/src/services/thread.service.ts +853 -0
- package/src/services/thread.types.ts +17 -0
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/system-agents/index.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/researcher.agent.ts +3 -3
- package/src/system-agents/{workstream-router.agent.ts → thread-router.agent.ts} +68 -135
- package/src/system-agents/title-generator.agent.ts +8 -8
- package/src/tools/execution-plan.tool.ts +39 -40
- package/src/tools/memory-block.tool.ts +4 -4
- package/src/tools/research-topic.tool.ts +1 -0
- package/src/tools/search-web.tool.ts +1 -1
- package/src/tools/search.tool.ts +4 -4
- package/src/tools/team-think.tool.ts +9 -9
- package/src/utils/async.ts +6 -7
- package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +43 -43
- package/src/workers/skill-extraction.runner.ts +9 -13
- package/src/workers/utils/{workstream-message-query.ts → thread-message-query.ts} +21 -21
- package/infrastructure/schema/00_workstream.surql +0 -64
- package/src/config/workstream-defaults.ts +0 -72
- package/src/services/workstream-plan-registry.service.ts +0 -22
- package/src/services/workstream-title.service.ts +0 -42
- package/src/services/workstream.service.ts +0 -803
- package/src/services/workstream.types.ts +0 -17
- /package/src/services/{workstream-constants.ts → thread-constants.ts} +0 -0
package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
THREAD,
|
|
3
3
|
dataPartsSchemas,
|
|
4
4
|
messageMetadataSchema,
|
|
5
5
|
PlanNodeResultSubmissionSchema,
|
|
@@ -11,13 +11,13 @@ import type { ChatMessage, MessageMetadata, PlanNodeSpecRecord } from '@lota-sdk
|
|
|
11
11
|
import { convertToModelMessages, stepCountIs, tool as createTool, validateUIMessages } from 'ai'
|
|
12
12
|
import type { PrepareStepFunction, StopCondition, ToolLoopAgent, ToolSet, UIMessageStreamWriter } from 'ai'
|
|
13
13
|
|
|
14
|
-
import type {
|
|
14
|
+
import type { CoreThreadProfile } from '../config/agent-defaults'
|
|
15
15
|
import {
|
|
16
16
|
agentRoster,
|
|
17
17
|
buildAgentTools,
|
|
18
18
|
createAgent,
|
|
19
19
|
getLeadAgentId,
|
|
20
|
-
|
|
20
|
+
getCoreThreadProfile,
|
|
21
21
|
getAgentRuntimeConfig,
|
|
22
22
|
} from '../config/agent-defaults'
|
|
23
23
|
import { lotaDebugLogger } from '../config/debug-logger'
|
|
@@ -26,7 +26,7 @@ import type { RecordIdRef } from '../db/record-id'
|
|
|
26
26
|
import { recordIdToString } from '../db/record-id'
|
|
27
27
|
import { TABLES } from '../db/tables'
|
|
28
28
|
import { enqueueContextCompaction } from '../queues/context-compaction.queue'
|
|
29
|
-
import {
|
|
29
|
+
import { enqueueThreadTitleGeneration } from '../queues/title-generation.queue'
|
|
30
30
|
import { readRuntimeAgentIdentityOverrides, resolveRuntimeAgentDisplayName } from '../runtime/agent-identity-overrides'
|
|
31
31
|
import { OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES } from '../runtime/agent-runtime-policy'
|
|
32
32
|
import { createAgentMessageMetadata, createServerRunAbortController } from '../runtime/agent-stream-helpers'
|
|
@@ -39,7 +39,6 @@ import { createExecutionPlanInstructionSectionCache } from '../runtime/execution
|
|
|
39
39
|
import { mergeInstructionSections } from '../runtime/instruction-sections'
|
|
40
40
|
import { runPostTurnSideEffects } from '../runtime/post-turn-side-effects'
|
|
41
41
|
import { getRuntimeAdapters, getToolProviders, getTurnHooks } from '../runtime/runtime-extensions'
|
|
42
|
-
import { finalizeTurnRun } from '../runtime/turn-lifecycle'
|
|
43
42
|
import {
|
|
44
43
|
asRecord,
|
|
45
44
|
collectToolOutputErrors,
|
|
@@ -47,17 +46,18 @@ import {
|
|
|
47
46
|
readInstructionSections,
|
|
48
47
|
readOptionalString,
|
|
49
48
|
toOptionalTrimmedString,
|
|
50
|
-
} from '../runtime/
|
|
49
|
+
} from '../runtime/thread-chat-helpers'
|
|
51
50
|
import {
|
|
52
51
|
buildPlanTurnInstructionSections,
|
|
53
52
|
buildPlanTurnPromptMessage,
|
|
54
53
|
buildPlanTurnSubmitToolDescription,
|
|
55
|
-
} from '../runtime/
|
|
56
|
-
import type {
|
|
57
|
-
import {
|
|
54
|
+
} from '../runtime/thread-plan-turn'
|
|
55
|
+
import type { ThreadPlanTurnContext } from '../runtime/thread-plan-turn'
|
|
56
|
+
import { assembleThreadTurnContext } from '../runtime/thread-turn-context'
|
|
57
|
+
import { finalizeTurnRun } from '../runtime/turn-lifecycle'
|
|
58
58
|
import { chatRunRegistry } from '../services/chat-run-registry.service'
|
|
59
|
-
import type {
|
|
60
|
-
import {
|
|
59
|
+
import type { NormalizedThread, ThreadRecord } from '../services/thread.types'
|
|
60
|
+
import { triageThreadMessage, checkForNextAgent } from '../system-agents/thread-router.agent'
|
|
61
61
|
import { safeEnqueue } from '../utils/async'
|
|
62
62
|
import { AppError } from '../utils/errors'
|
|
63
63
|
import { attachmentService } from './attachment.service'
|
|
@@ -67,8 +67,8 @@ import { executionPlanService } from './execution-plan.service'
|
|
|
67
67
|
import { learnedSkillService } from './learned-skill.service'
|
|
68
68
|
import { memoryService } from './memory.service'
|
|
69
69
|
import { planRunService } from './plan-run.service'
|
|
70
|
-
import {
|
|
71
|
-
import {
|
|
70
|
+
import { threadMessageService } from './thread-message.service'
|
|
71
|
+
import { ActiveThreadRunConflictError, threadService } from './thread.service'
|
|
72
72
|
|
|
73
73
|
type ChatStreamChunk = Parameters<UIMessageStreamWriter<ChatMessage>['write']>[0]
|
|
74
74
|
|
|
@@ -86,22 +86,22 @@ function hasUIMessageStream(value: unknown): value is UIMessageStreamResult {
|
|
|
86
86
|
|
|
87
87
|
const PRESEEDED_MEMORY_LOOKUP_LIMIT = 3
|
|
88
88
|
|
|
89
|
-
async function
|
|
89
|
+
async function waitForThreadCompactionIfNeeded(threadId: RecordIdRef): Promise<ThreadRecord> {
|
|
90
90
|
return waitForCompactionIfNeeded({
|
|
91
|
-
entityId: recordIdToString(
|
|
92
|
-
entityLabel: '
|
|
93
|
-
loadEntity: () =>
|
|
94
|
-
isCompacting: (
|
|
91
|
+
entityId: recordIdToString(threadId, TABLES.THREAD),
|
|
92
|
+
entityLabel: 'Thread',
|
|
93
|
+
loadEntity: () => threadService.getById(threadId),
|
|
94
|
+
isCompacting: (thread) => thread.isCompacting === true,
|
|
95
95
|
})
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
class
|
|
98
|
+
class ThreadTurnError extends AppError {
|
|
99
99
|
constructor(
|
|
100
100
|
message: string,
|
|
101
101
|
readonly statusCode: 400 | 409,
|
|
102
102
|
) {
|
|
103
103
|
super(message, statusCode === 409 ? 'CONFLICT' : 'BAD_REQUEST', statusCode)
|
|
104
|
-
this.name = '
|
|
104
|
+
this.name = 'ThreadTurnError'
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -122,12 +122,13 @@ function writeMultiAgentEvent(
|
|
|
122
122
|
): void {
|
|
123
123
|
if (!writer) return
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
const chunk: ChatStreamChunk = {
|
|
126
126
|
type: 'data-multi-agent-event',
|
|
127
127
|
id: `multi-agent-${Bun.randomUUIDv7()}`,
|
|
128
128
|
data: event,
|
|
129
129
|
transient: true,
|
|
130
|
-
}
|
|
130
|
+
}
|
|
131
|
+
writer.write(chunk)
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
function applyPlanTurnToolPolicy(tools: ToolSet, nodeSpec: PlanNodeSpecRecord): ToolSet {
|
|
@@ -143,9 +144,9 @@ function applyPlanTurnToolPolicy(tools: ToolSet, nodeSpec: PlanNodeSpecRecord):
|
|
|
143
144
|
)
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
export interface
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
export interface ThreadTurnParams {
|
|
148
|
+
thread: NormalizedThread
|
|
149
|
+
threadRef: RecordIdRef
|
|
149
150
|
orgRef: RecordIdRef
|
|
150
151
|
userRef: RecordIdRef
|
|
151
152
|
userName?: string | null
|
|
@@ -156,9 +157,9 @@ export interface WorkstreamTurnParams {
|
|
|
156
157
|
streamId?: string
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
export interface
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
export interface ThreadApprovalContinuationParams {
|
|
161
|
+
thread: NormalizedThread
|
|
162
|
+
threadRef: RecordIdRef
|
|
162
163
|
orgRef: RecordIdRef
|
|
163
164
|
userRef: RecordIdRef
|
|
164
165
|
userName?: string | null
|
|
@@ -167,20 +168,20 @@ export interface WorkstreamApprovalContinuationParams {
|
|
|
167
168
|
streamId?: string
|
|
168
169
|
}
|
|
169
170
|
|
|
170
|
-
export interface
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
export interface ThreadPlanTurnParams {
|
|
172
|
+
thread: NormalizedThread
|
|
173
|
+
threadRef: RecordIdRef
|
|
173
174
|
orgRef: RecordIdRef
|
|
174
175
|
userRef: RecordIdRef
|
|
175
176
|
userName?: string | null
|
|
176
|
-
planTurn:
|
|
177
|
+
planTurn: ThreadPlanTurnContext
|
|
177
178
|
abortSignal?: AbortSignal
|
|
178
179
|
streamId?: string
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
type
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
type ThreadRunCoreParams = {
|
|
183
|
+
thread: NormalizedThread
|
|
184
|
+
threadRef: RecordIdRef
|
|
184
185
|
orgRef: RecordIdRef
|
|
185
186
|
userRef: RecordIdRef
|
|
186
187
|
userName?: string | null
|
|
@@ -191,15 +192,15 @@ type WorkstreamRunCoreParams = {
|
|
|
191
192
|
| { kind: 'userTurn'; inputMessage: ChatMessage; skipInputMessagePersistence?: boolean }
|
|
192
193
|
| { kind: 'approvalContinuation'; approvalMessages: ChatMessage[] }
|
|
193
194
|
| { kind: 'nativeToolApprovalTurn'; approvalMessages: ChatMessage[] }
|
|
194
|
-
| { kind: 'planTurn'; planTurn:
|
|
195
|
+
| { kind: 'planTurn'; planTurn: ThreadPlanTurnContext }
|
|
195
196
|
)
|
|
196
197
|
|
|
197
|
-
interface
|
|
198
|
+
interface PreparedThreadTurn {
|
|
198
199
|
originalMessages: ChatMessage[]
|
|
199
|
-
run: (writer?: UIMessageStreamWriter<ChatMessage>) => Promise<
|
|
200
|
+
run: (writer?: UIMessageStreamWriter<ChatMessage>) => Promise<PreparedThreadTurnResult>
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
export interface
|
|
203
|
+
export interface PreparedThreadTurnResult {
|
|
203
204
|
inputMessageId?: string
|
|
204
205
|
assistantMessages: ChatMessage[]
|
|
205
206
|
}
|
|
@@ -217,8 +218,8 @@ function upsertChatHistoryMessage(messages: ChatMessage[], nextMessage: ChatMess
|
|
|
217
218
|
|
|
218
219
|
interface StreamAgentResponseContext {
|
|
219
220
|
turnHooks: ReturnType<typeof getTurnHooks>
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
thread: NormalizedThread
|
|
222
|
+
threadRef: RecordIdRef
|
|
222
223
|
orgRef: RecordIdRef
|
|
223
224
|
userRef: RecordIdRef
|
|
224
225
|
userName?: string | null
|
|
@@ -238,7 +239,7 @@ interface StreamAgentResponseContext {
|
|
|
238
239
|
|
|
239
240
|
interface StreamAgentResponseParams {
|
|
240
241
|
agentId: string
|
|
241
|
-
mode: 'direct' | '
|
|
242
|
+
mode: 'direct' | 'fixedThreadMode' | 'threadMode'
|
|
242
243
|
messages: ChatMessage[]
|
|
243
244
|
tools: ToolSet
|
|
244
245
|
observer: {
|
|
@@ -269,8 +270,8 @@ async function streamAgentResponse(
|
|
|
269
270
|
await ctx.turnHooks.resolveAgent?.({
|
|
270
271
|
agentId: streamParams.agentId,
|
|
271
272
|
mode: streamParams.mode,
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
thread: ctx.thread,
|
|
274
|
+
threadRef: ctx.threadRef,
|
|
274
275
|
orgRef: ctx.orgRef,
|
|
275
276
|
userRef: ctx.userRef,
|
|
276
277
|
userName: ctx.userName,
|
|
@@ -306,7 +307,7 @@ async function streamAgentResponse(
|
|
|
306
307
|
resolvedAgentId === 'mentor'
|
|
307
308
|
const config = getAgentRuntimeConfig({
|
|
308
309
|
agentId: resolvedAgentId,
|
|
309
|
-
|
|
310
|
+
threadType: ctx.thread.type,
|
|
310
311
|
mode: streamParams.mode,
|
|
311
312
|
skills: streamParams.skills,
|
|
312
313
|
onboardingActive: ctx.onboardingActive,
|
|
@@ -314,7 +315,7 @@ async function streamAgentResponse(
|
|
|
314
315
|
systemWorkspaceDetails: ctx.promptContext.systemWorkspaceDetails,
|
|
315
316
|
preSeededMemoriesSection,
|
|
316
317
|
retrievedKnowledgeSection: ctx.retrievedKnowledgeSection,
|
|
317
|
-
|
|
318
|
+
threadMemoryBlock: ctx.memoryBlock,
|
|
318
319
|
learnedSkillsSection,
|
|
319
320
|
userMessageText: latestUserMessageText,
|
|
320
321
|
ruleOptions: { includeMemr3Rule: hasRetrievalTools, includeDomainReasoningFallbackRule: hasDomainRoutingSkills },
|
|
@@ -342,32 +343,20 @@ async function streamAgentResponse(
|
|
|
342
343
|
const agentAbortSignal = streamParams.abortSignal ?? ctx.runAbortSignal
|
|
343
344
|
agentTimer.step('agent-construction')
|
|
344
345
|
|
|
345
|
-
const MAX_STREAM_RETRIES = 3
|
|
346
346
|
let result: unknown
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
streamParams.observer.recordAbort(error)
|
|
357
|
-
throw error
|
|
358
|
-
}
|
|
359
|
-
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
360
|
-
const isTransient =
|
|
361
|
-
errorMessage.includes('client disconnected') ||
|
|
362
|
-
errorMessage.includes('ECONNRESET') ||
|
|
363
|
-
errorMessage.includes('socket hang up') ||
|
|
364
|
-
errorMessage.includes('fetch failed')
|
|
365
|
-
if (!isTransient || attempt >= MAX_STREAM_RETRIES - 1) {
|
|
366
|
-
streamParams.observer.recordError(error)
|
|
367
|
-
throw error
|
|
368
|
-
}
|
|
369
|
-
aiLogger.warn`Transient stream error (attempt ${attempt + 1}/${MAX_STREAM_RETRIES}): ${errorMessage} — retrying`
|
|
347
|
+
try {
|
|
348
|
+
result = await streamParams.observer.run(() =>
|
|
349
|
+
agent.stream({ messages: modelMessages, abortSignal: agentAbortSignal }),
|
|
350
|
+
)
|
|
351
|
+
agentTimer.step('agent.stream()-resolved')
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (agentAbortSignal.aborted) {
|
|
354
|
+
streamParams.observer.recordAbort(error)
|
|
355
|
+
throw error
|
|
370
356
|
}
|
|
357
|
+
|
|
358
|
+
streamParams.observer.recordError(error)
|
|
359
|
+
throw error
|
|
371
360
|
}
|
|
372
361
|
if (!hasUIMessageStream(result)) {
|
|
373
362
|
throw new Error(`Agent run for ${resolvedAgentId} did not expose a UI message stream.`)
|
|
@@ -426,15 +415,15 @@ async function streamAgentResponse(
|
|
|
426
415
|
return responseMessage
|
|
427
416
|
}
|
|
428
417
|
|
|
429
|
-
export async function
|
|
430
|
-
const {
|
|
418
|
+
export async function prepareThreadRunCore(params: ThreadRunCoreParams): Promise<PreparedThreadTurn> {
|
|
419
|
+
const { thread, threadRef, orgRef, userRef, userName } = params
|
|
431
420
|
const runtimeAdapters = getRuntimeAdapters()
|
|
432
421
|
const turnHooks = getTurnHooks()
|
|
433
422
|
const toolProviders = getToolProviders()
|
|
434
423
|
const workspaceProvider = runtimeAdapters.workspaceProvider
|
|
435
424
|
const orgIdString = recordIdToString(orgRef, TABLES.ORGANIZATION)
|
|
436
425
|
const userIdString = recordIdToString(userRef, TABLES.USER)
|
|
437
|
-
const
|
|
426
|
+
const threadIdString = recordIdToString(threadRef, TABLES.THREAD)
|
|
438
427
|
|
|
439
428
|
const hydrateMessageFileUrls = (message: ChatMessage): ChatMessage => ({
|
|
440
429
|
...message,
|
|
@@ -453,13 +442,13 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
453
442
|
if (params.kind === 'userTurn') {
|
|
454
443
|
inputMessage = hydrateMessageFileUrls(withMessageCreatedAt(params.inputMessage, Date.now()))
|
|
455
444
|
if (inputMessage.role !== 'user') {
|
|
456
|
-
throw new
|
|
445
|
+
throw new ThreadTurnError('Only user messages can be submitted to the thread runtime.', 400)
|
|
457
446
|
}
|
|
458
447
|
if (!hasMessageContent(inputMessage.parts)) {
|
|
459
|
-
throw new
|
|
448
|
+
throw new ThreadTurnError('Thread messages must include text or attachments.', 400)
|
|
460
449
|
}
|
|
461
|
-
if (
|
|
462
|
-
throw new
|
|
450
|
+
if (thread.type === 'default' && !thread.agentId) {
|
|
451
|
+
throw new ThreadTurnError('Default threads require an assigned agent.', 400)
|
|
463
452
|
}
|
|
464
453
|
}
|
|
465
454
|
|
|
@@ -471,17 +460,14 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
471
460
|
? workspaceProvider.getWorkspace(orgRef)
|
|
472
461
|
: Promise.resolve({})
|
|
473
462
|
|
|
474
|
-
const
|
|
463
|
+
const threadRecord = await waitForThreadCompactionIfNeeded(threadRef)
|
|
475
464
|
timer.step('compaction-gate')
|
|
476
465
|
// Plan turns run without the chat lease — they must not block or be blocked by user messages.
|
|
477
466
|
if (params.kind !== 'planTurn') {
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
toOptionalTrimmedString(workstreamRecord.activeRunId)
|
|
481
|
-
) {
|
|
482
|
-
const clearedStaleRun = await workstreamService.clearStaleActiveRunIfMissingFromRegistry(workstreamRef)
|
|
467
|
+
if ((await threadService.hasActiveRunLease(threadRef)) || toOptionalTrimmedString(threadRecord.activeRunId)) {
|
|
468
|
+
const clearedStaleRun = await threadService.clearStaleActiveRunIfMissingFromRegistry(threadRef)
|
|
483
469
|
if (!clearedStaleRun) {
|
|
484
|
-
throw new
|
|
470
|
+
throw new ThreadTurnError('A chat run is already active.', 409)
|
|
485
471
|
}
|
|
486
472
|
}
|
|
487
473
|
}
|
|
@@ -491,22 +477,19 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
491
477
|
.reverse()
|
|
492
478
|
.find((m) => m.role === 'assistant' && hasApprovalRespondedParts(m))
|
|
493
479
|
if (!approvedAssistantMessage) {
|
|
494
|
-
throw new
|
|
480
|
+
throw new ThreadTurnError('No approval-responded message found.', 400)
|
|
495
481
|
}
|
|
496
|
-
await
|
|
482
|
+
await threadMessageService.upsertMessages({ threadId: threadRef, messages: [approvedAssistantMessage] })
|
|
497
483
|
timer.step('persist-approval-message')
|
|
498
484
|
}
|
|
499
485
|
|
|
500
|
-
const persistedCompactionCursor = toOptionalTrimmedString(
|
|
501
|
-
const persistedLiveHistoryPromise =
|
|
502
|
-
workstreamRef,
|
|
503
|
-
persistedCompactionCursor,
|
|
504
|
-
)
|
|
486
|
+
const persistedCompactionCursor = toOptionalTrimmedString(threadRecord.lastCompactedMessageId) ?? undefined
|
|
487
|
+
const persistedLiveHistoryPromise = threadMessageService.listMessagesAfterCursor(threadRef, persistedCompactionCursor)
|
|
505
488
|
let recentHistoryPromise: Promise<ChatMessage[]> | null = null
|
|
506
489
|
const loadRecentHistory = async (): Promise<ChatMessage[]> => {
|
|
507
490
|
if (!recentHistoryPromise) {
|
|
508
|
-
recentHistoryPromise =
|
|
509
|
-
.listRecentMessages(
|
|
491
|
+
recentHistoryPromise = threadMessageService
|
|
492
|
+
.listRecentMessages(threadRef, 64)
|
|
510
493
|
.then(async (persistedRecentHistory) => {
|
|
511
494
|
if (persistedRecentHistory.length === 0) {
|
|
512
495
|
return [] as ChatMessage[]
|
|
@@ -547,7 +530,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
547
530
|
timer.step('validate+hydrate-history')
|
|
548
531
|
|
|
549
532
|
if (userMessage && shouldPersistInputMessage) {
|
|
550
|
-
await
|
|
533
|
+
await threadMessageService.upsertMessages({ threadId: threadRef, messages: [userMessage] })
|
|
551
534
|
timer.step('persist-user-message')
|
|
552
535
|
}
|
|
553
536
|
|
|
@@ -569,7 +552,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
569
552
|
const respondedBy = recordIdToString(userRef, TABLES.USER)
|
|
570
553
|
if (params.kind === 'approvalContinuation') {
|
|
571
554
|
await executionPlanService.applyApprovalResponseFromMessages({
|
|
572
|
-
|
|
555
|
+
threadId: threadRef,
|
|
573
556
|
approvalMessages: params.approvalMessages,
|
|
574
557
|
respondedBy,
|
|
575
558
|
})
|
|
@@ -580,31 +563,29 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
580
563
|
|
|
581
564
|
if (
|
|
582
565
|
params.kind === 'userTurn' &&
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
workstreamRecord.title === WORKSTREAM.DEFAULT_TITLE &&
|
|
566
|
+
thread.type === 'group' &&
|
|
567
|
+
threadRecord.nameGenerated !== true &&
|
|
568
|
+
threadRecord.title === THREAD.DEFAULT_TITLE &&
|
|
587
569
|
messageText.length > 0
|
|
588
570
|
) {
|
|
589
|
-
void safeEnqueue(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
)
|
|
571
|
+
void safeEnqueue(() => enqueueThreadTitleGeneration({ threadId: threadIdString, sourceText: messageText }), {
|
|
572
|
+
operationName: 'thread-title-generation',
|
|
573
|
+
})
|
|
593
574
|
}
|
|
594
575
|
|
|
595
|
-
if (
|
|
596
|
-
throw new
|
|
576
|
+
if (thread.type === 'thread' && !thread.threadType) {
|
|
577
|
+
throw new ThreadTurnError('Core threads require a thread type.', 400)
|
|
597
578
|
}
|
|
598
|
-
const
|
|
599
|
-
|
|
579
|
+
const coreThreadProfile: CoreThreadProfile | null =
|
|
580
|
+
thread.type === 'thread' && thread.threadType ? getCoreThreadProfile(thread.threadType) : null
|
|
600
581
|
const defaultLeadAgentId = getLeadAgentId()
|
|
601
|
-
const
|
|
582
|
+
const visibleThreadAgentId =
|
|
602
583
|
params.agentIdOverride ??
|
|
603
|
-
(
|
|
604
|
-
const coreInstructionSections =
|
|
605
|
-
const assembledContext = await
|
|
606
|
-
|
|
607
|
-
|
|
584
|
+
(thread.type === 'default' ? thread.agentId : (coreThreadProfile?.config.agentId ?? defaultLeadAgentId))
|
|
585
|
+
const coreInstructionSections = coreThreadProfile ? [coreThreadProfile.instructions] : undefined
|
|
586
|
+
const assembledContext = await assembleThreadTurnContext({
|
|
587
|
+
thread,
|
|
588
|
+
threadRef,
|
|
608
589
|
orgRef,
|
|
609
590
|
userRef,
|
|
610
591
|
userName,
|
|
@@ -628,11 +609,11 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
628
609
|
hookInstructionSections,
|
|
629
610
|
} = assembledContext
|
|
630
611
|
|
|
631
|
-
let memoryBlock =
|
|
612
|
+
let memoryBlock = threadService.formatMemoryBlockForPrompt(threadRecord)
|
|
632
613
|
const executionPlanInstructionSectionCache = createExecutionPlanInstructionSectionCache({
|
|
633
614
|
disabled: false,
|
|
634
615
|
loadPlans: async () => {
|
|
635
|
-
const runs = await planRunService.getActiveRunRecords(
|
|
616
|
+
const runs = await planRunService.getActiveRunRecords(threadRef)
|
|
636
617
|
return Promise.all(runs.map((run) => planRunService.toSerializablePlan(run, { slim: true })))
|
|
637
618
|
},
|
|
638
619
|
})
|
|
@@ -643,7 +624,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
643
624
|
}
|
|
644
625
|
if (userMessage) {
|
|
645
626
|
const appliedHumanInput = await executionPlanService.applyHumanInputFromUserMessage({
|
|
646
|
-
|
|
627
|
+
threadId: threadRef,
|
|
647
628
|
message: userMessage,
|
|
648
629
|
respondedBy,
|
|
649
630
|
})
|
|
@@ -684,8 +665,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
684
665
|
}
|
|
685
666
|
|
|
686
667
|
const persistedCompactionSummary =
|
|
687
|
-
persistedCompactionCursor && typeof
|
|
688
|
-
?
|
|
668
|
+
persistedCompactionCursor && typeof threadRecord.compactionSummary === 'string'
|
|
669
|
+
? threadRecord.compactionSummary
|
|
689
670
|
: ''
|
|
690
671
|
const messagesForContext = userMessage ? upsertChatHistoryMessage(liveHistory, userMessage) : liveHistory
|
|
691
672
|
let currentMessages = contextCompactionRuntime.prependSummaryMessage(persistedCompactionSummary, messagesForContext)
|
|
@@ -704,7 +685,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
704
685
|
})
|
|
705
686
|
const buildTurnToolParams = (toolParams: {
|
|
706
687
|
agentId: string
|
|
707
|
-
mode: 'direct' | '
|
|
688
|
+
mode: 'direct' | 'fixedThreadMode' | 'threadMode'
|
|
708
689
|
memoryBlock: string
|
|
709
690
|
onAppendMemoryBlock: (value: string) => void
|
|
710
691
|
extraMessages?: ChatMessage[]
|
|
@@ -715,9 +696,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
715
696
|
orgId: orgRef,
|
|
716
697
|
userId: userRef,
|
|
717
698
|
userName: userName ?? 'there',
|
|
718
|
-
|
|
699
|
+
threadId: threadRef,
|
|
719
700
|
orgIdString,
|
|
720
|
-
|
|
701
|
+
threadType: thread.type,
|
|
721
702
|
mode: toolParams.mode,
|
|
722
703
|
linearInstalled,
|
|
723
704
|
onboardingActive,
|
|
@@ -738,7 +719,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
738
719
|
return {
|
|
739
720
|
originalMessages,
|
|
740
721
|
run: async (writer?: UIMessageStreamWriter<ChatMessage>) => {
|
|
741
|
-
const executeRun = async (leaseAbortSignal?: AbortSignal): Promise<
|
|
722
|
+
const executeRun = async (leaseAbortSignal?: AbortSignal): Promise<PreparedThreadTurnResult | void> => {
|
|
742
723
|
const agentIdentityOverrides = readRuntimeAgentIdentityOverrides(buildContextResult)
|
|
743
724
|
const runTimer = lotaDebugLogger.timer('run')
|
|
744
725
|
const serverRunId = Bun.randomUUIDv7()
|
|
@@ -751,7 +732,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
751
732
|
}
|
|
752
733
|
// Plan turns run without the chat lease — don't claim the active run slot.
|
|
753
734
|
if (params.kind !== 'planTurn') {
|
|
754
|
-
await
|
|
735
|
+
await threadService.setActiveTurn(threadRef, serverRunId, params.streamId ?? null)
|
|
755
736
|
chatRunRegistry.register(serverRunId, runAbort.controller)
|
|
756
737
|
}
|
|
757
738
|
runTimer.step('set-active-run+stream')
|
|
@@ -794,7 +775,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
794
775
|
Date.now(),
|
|
795
776
|
)
|
|
796
777
|
|
|
797
|
-
await
|
|
778
|
+
await threadMessageService.upsertMessages({ threadId: threadRef, messages: [committed] })
|
|
798
779
|
currentMessages = upsertChatHistoryMessage(currentMessages, committed)
|
|
799
780
|
allAssistantMessages = upsertChatHistoryMessage(allAssistantMessages, committed)
|
|
800
781
|
throwIfRunAborted()
|
|
@@ -804,8 +785,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
804
785
|
|
|
805
786
|
const streamCtx: StreamAgentResponseContext = {
|
|
806
787
|
turnHooks,
|
|
807
|
-
|
|
808
|
-
|
|
788
|
+
thread,
|
|
789
|
+
threadRef,
|
|
809
790
|
orgRef,
|
|
810
791
|
userRef,
|
|
811
792
|
userName,
|
|
@@ -825,7 +806,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
825
806
|
|
|
826
807
|
const runVisibleAgent = async (runParams: {
|
|
827
808
|
agentId: string
|
|
828
|
-
mode: 'direct' | '
|
|
809
|
+
mode: 'direct' | 'fixedThreadMode' | 'threadMode'
|
|
829
810
|
skills?: string[]
|
|
830
811
|
additionalInstructionSections?: string[]
|
|
831
812
|
extraMessages?: ChatMessage[]
|
|
@@ -837,7 +818,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
837
818
|
const visibleTimer = lotaDebugLogger.timer(`visible:${runParams.agentId}`)
|
|
838
819
|
let runMemoryBlock = memoryBlock
|
|
839
820
|
const includeExecutionPlanTools =
|
|
840
|
-
runParams.includeExecutionPlanTools ?? runParams.mode !== '
|
|
821
|
+
runParams.includeExecutionPlanTools ?? runParams.mode !== 'fixedThreadMode'
|
|
841
822
|
const rawTools: ToolSet = {
|
|
842
823
|
...((await buildAgentTools(
|
|
843
824
|
buildTurnToolParams({
|
|
@@ -889,7 +870,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
889
870
|
inputSchema: PlanNodeResultSubmissionSchema,
|
|
890
871
|
execute: async (result) =>
|
|
891
872
|
await executionPlanService.submitPlanTurnResult({
|
|
892
|
-
|
|
873
|
+
threadId: threadRef,
|
|
893
874
|
runId: planTurn.runId,
|
|
894
875
|
nodeId: planTurn.nodeId,
|
|
895
876
|
emittedBy: planTurn.nodeSpec.owner.ref,
|
|
@@ -899,7 +880,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
899
880
|
|
|
900
881
|
await runVisibleAgent({
|
|
901
882
|
agentId: planTurn.nodeSpec.owner.ref,
|
|
902
|
-
mode:
|
|
883
|
+
mode: thread.type === 'default' ? 'direct' : 'threadMode',
|
|
903
884
|
additionalInstructionSections: buildPlanTurnInstructionSections(planTurn),
|
|
904
885
|
extraMessages: [buildPlanTurnPromptMessage(planTurn)],
|
|
905
886
|
includeExecutionPlanTools: false,
|
|
@@ -908,16 +889,16 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
908
889
|
metadataPatch: { trigger: 'plan-turn', planRunId: planTurn.runId, planNodeId: planTurn.nodeId },
|
|
909
890
|
})
|
|
910
891
|
} else {
|
|
911
|
-
if (
|
|
912
|
-
if (!
|
|
913
|
-
throw new
|
|
892
|
+
if (thread.type === 'default') {
|
|
893
|
+
if (!thread.agentId) {
|
|
894
|
+
throw new ThreadTurnError('Direct threads require an assigned agent.', 400)
|
|
914
895
|
}
|
|
915
|
-
await runVisibleAgent({ agentId:
|
|
896
|
+
await runVisibleAgent({ agentId: thread.agentId, mode: 'direct' })
|
|
916
897
|
} else {
|
|
917
|
-
// Multi-agent orchestration for group
|
|
918
|
-
const wsMembers = (
|
|
898
|
+
// Multi-agent orchestration for group threads
|
|
899
|
+
const wsMembers = (thread as { members?: string[] }).members ?? []
|
|
919
900
|
const members = wsMembers.length > 0 ? wsMembers : [...agentRoster]
|
|
920
|
-
const fallbackAgentId =
|
|
901
|
+
const fallbackAgentId = coreThreadProfile?.config.agentId ?? defaultLeadAgentId
|
|
921
902
|
throwIfRunAborted()
|
|
922
903
|
writeMultiAgentEvent(writer, { phase: 'routing', note: 'Routing this turn to the right agent.' })
|
|
923
904
|
|
|
@@ -926,8 +907,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
926
907
|
.map((m) => `${m.role}: ${extractMessageText(m).slice(0, 200)}`)
|
|
927
908
|
.join('\n')
|
|
928
909
|
|
|
929
|
-
const triageResult = await
|
|
930
|
-
|
|
910
|
+
const triageResult = await triageThreadMessage({
|
|
911
|
+
threadTitle: thread.title,
|
|
931
912
|
members,
|
|
932
913
|
messageText,
|
|
933
914
|
recentContext,
|
|
@@ -944,13 +925,13 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
944
925
|
}
|
|
945
926
|
// Multi-agent member protocol: be direct, focus on domain
|
|
946
927
|
additionalSections.push(
|
|
947
|
-
'<multi-agent-protocol>\nYou are responding as part of a multi-agent
|
|
928
|
+
'<multi-agent-protocol>\nYou are responding as part of a multi-agent thread. Focus on your domain expertise. Be direct and concise — another agent may follow up on different aspects.\n</multi-agent-protocol>',
|
|
948
929
|
)
|
|
949
930
|
|
|
950
931
|
return await runVisibleAgent({
|
|
951
932
|
agentId,
|
|
952
|
-
mode: '
|
|
953
|
-
skills:
|
|
933
|
+
mode: 'threadMode',
|
|
934
|
+
skills: coreThreadProfile?.skills ? [...coreThreadProfile.skills] : undefined,
|
|
954
935
|
additionalInstructionSections: additionalSections,
|
|
955
936
|
})
|
|
956
937
|
}
|
|
@@ -973,7 +954,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
973
954
|
while (respondedAgents.length < 3) {
|
|
974
955
|
const lastResponseText = extractMessageText(lastResponse).slice(0, 500)
|
|
975
956
|
const checkResult = await checkForNextAgent({
|
|
976
|
-
|
|
957
|
+
threadTitle: thread.title,
|
|
977
958
|
members,
|
|
978
959
|
messageText,
|
|
979
960
|
respondedAgents,
|
|
@@ -990,7 +971,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
990
971
|
phase: 'waiting-for-agent',
|
|
991
972
|
agentId: checkResult.agentId,
|
|
992
973
|
agentName: resolveRuntimeAgentDisplayName(agentIdentityOverrides, checkResult.agentId),
|
|
993
|
-
note: checkResult.routingContext,
|
|
974
|
+
note: checkResult.routingContext ?? undefined,
|
|
994
975
|
})
|
|
995
976
|
|
|
996
977
|
// Insert hidden bridge message between agent turns
|
|
@@ -1006,15 +987,12 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1006
987
|
metadata: { hidden: true, createdAt: Date.now() } as MessageMetadata,
|
|
1007
988
|
}
|
|
1008
989
|
throwIfRunAborted()
|
|
1009
|
-
await
|
|
1010
|
-
workstreamId: workstreamRef,
|
|
1011
|
-
messages: [bridgeMessage],
|
|
1012
|
-
})
|
|
990
|
+
await threadMessageService.upsertMessages({ threadId: threadRef, messages: [bridgeMessage] })
|
|
1013
991
|
currentMessages = upsertChatHistoryMessage(currentMessages, bridgeMessage)
|
|
1014
992
|
throwIfRunAborted()
|
|
1015
993
|
|
|
1016
994
|
lastResponse = await runGroupAgent(checkResult.agentId, {
|
|
1017
|
-
routingContext: checkResult.routingContext,
|
|
995
|
+
routingContext: checkResult.routingContext ?? undefined,
|
|
1018
996
|
})
|
|
1019
997
|
respondedAgents.push(checkResult.agentId)
|
|
1020
998
|
throwIfRunAborted()
|
|
@@ -1033,13 +1011,12 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1033
1011
|
}
|
|
1034
1012
|
} finally {
|
|
1035
1013
|
try {
|
|
1036
|
-
const
|
|
1014
|
+
const latestThreadRecord = await threadService.getById(threadRef)
|
|
1037
1015
|
|
|
1038
1016
|
await finalizeTurnRun({
|
|
1039
1017
|
serverRunId,
|
|
1040
|
-
getEntity: async () =>
|
|
1041
|
-
getUncompactedMessages: (cursor) =>
|
|
1042
|
-
workstreamMessageService.listMessagesAfterCursor(workstreamRef, cursor),
|
|
1018
|
+
getEntity: async () => latestThreadRecord,
|
|
1019
|
+
getUncompactedMessages: (cursor) => threadMessageService.listMessagesAfterCursor(threadRef, cursor),
|
|
1043
1020
|
assessCompaction: (summaryText, messages) =>
|
|
1044
1021
|
contextCompactionRuntime.shouldCompactHistory({
|
|
1045
1022
|
summaryText,
|
|
@@ -1048,34 +1025,34 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1048
1025
|
}),
|
|
1049
1026
|
enqueueCompaction: async () => {
|
|
1050
1027
|
await enqueueContextCompaction({
|
|
1051
|
-
domain: '
|
|
1052
|
-
entityId:
|
|
1028
|
+
domain: 'thread',
|
|
1029
|
+
entityId: threadIdString,
|
|
1053
1030
|
contextSize: CONTEXT_WINDOW_TOKENS,
|
|
1054
1031
|
})
|
|
1055
1032
|
},
|
|
1056
1033
|
unregisterRun: (runId) => chatRunRegistry.unregister(runId),
|
|
1057
1034
|
clearActiveRunId: async (runId) => {
|
|
1058
|
-
const activeStreamId = await
|
|
1059
|
-
await
|
|
1035
|
+
const activeStreamId = await threadService.getActiveStreamId(threadRef)
|
|
1036
|
+
await threadService.clearActiveTurn(threadRef, { runId, streamId: activeStreamId })
|
|
1060
1037
|
},
|
|
1061
1038
|
disposeAbort: () => runAbort.dispose(),
|
|
1062
1039
|
activeStreamId: params.streamId,
|
|
1063
1040
|
clearActiveStreamId: async (streamId) => {
|
|
1064
|
-
const activeRunId = await
|
|
1041
|
+
const activeRunId = await threadService.getActiveRunId(threadRef)
|
|
1065
1042
|
if (!activeRunId) return
|
|
1066
|
-
await
|
|
1043
|
+
await threadService.clearActiveTurn(threadRef, { runId: activeRunId, streamId })
|
|
1067
1044
|
},
|
|
1068
1045
|
})
|
|
1069
1046
|
|
|
1070
1047
|
if (allAssistantMessages.length > 0 && shouldProcessPostRunSideEffects) {
|
|
1071
1048
|
await runPostTurnSideEffects({
|
|
1072
|
-
|
|
1073
|
-
|
|
1049
|
+
thread,
|
|
1050
|
+
threadRef,
|
|
1074
1051
|
orgRef,
|
|
1075
1052
|
userRef,
|
|
1076
1053
|
userName,
|
|
1077
1054
|
orgIdString,
|
|
1078
|
-
|
|
1055
|
+
threadIdString,
|
|
1079
1056
|
onboardingActive,
|
|
1080
1057
|
workspace,
|
|
1081
1058
|
allAssistantMessages,
|
|
@@ -1084,9 +1061,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1084
1061
|
loadRecentHistory,
|
|
1085
1062
|
listReadableUploads: () => listReadableUploads(),
|
|
1086
1063
|
memoryBlock,
|
|
1087
|
-
|
|
1064
|
+
visibleThreadAgentId,
|
|
1088
1065
|
defaultLeadAgentId,
|
|
1089
|
-
|
|
1066
|
+
latestThreadRecord,
|
|
1090
1067
|
isUserTurn: params.kind === 'userTurn',
|
|
1091
1068
|
agentDisplayNamesById: agentIdentityOverrides.displayNamesById,
|
|
1092
1069
|
})
|
|
@@ -1094,20 +1071,20 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1094
1071
|
|
|
1095
1072
|
if (allAssistantMessages.length > 0 && params.kind !== 'planTurn') {
|
|
1096
1073
|
await turnHooks.afterTurn?.({
|
|
1097
|
-
|
|
1098
|
-
|
|
1074
|
+
thread,
|
|
1075
|
+
threadRef,
|
|
1099
1076
|
orgRef,
|
|
1100
1077
|
userRef,
|
|
1101
1078
|
userName,
|
|
1102
1079
|
onboardingActive,
|
|
1103
1080
|
referenceUserMessage,
|
|
1104
1081
|
assistantMessages: allAssistantMessages,
|
|
1105
|
-
|
|
1082
|
+
latestThreadRecord,
|
|
1106
1083
|
context: buildContextResult,
|
|
1107
1084
|
})
|
|
1108
1085
|
}
|
|
1109
1086
|
} catch (postRunError) {
|
|
1110
|
-
aiLogger.error`
|
|
1087
|
+
aiLogger.error`Thread post-run cleanup failed: ${postRunError}`
|
|
1111
1088
|
}
|
|
1112
1089
|
}
|
|
1113
1090
|
}
|
|
@@ -1123,7 +1100,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1123
1100
|
}
|
|
1124
1101
|
|
|
1125
1102
|
try {
|
|
1126
|
-
return await
|
|
1103
|
+
return await threadService.withActiveRunLease(threadRef, async (leaseAbortSignal) => {
|
|
1127
1104
|
const runResult = await executeRun(leaseAbortSignal)
|
|
1128
1105
|
if (runResult) {
|
|
1129
1106
|
return runResult
|
|
@@ -1132,8 +1109,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1132
1109
|
return { inputMessageId: referenceUserMessage?.id, assistantMessages: [...allAssistantMessages] }
|
|
1133
1110
|
})
|
|
1134
1111
|
} catch (error) {
|
|
1135
|
-
if (error instanceof
|
|
1136
|
-
throw new
|
|
1112
|
+
if (error instanceof ActiveThreadRunConflictError) {
|
|
1113
|
+
throw new ThreadTurnError(error.message, 409)
|
|
1137
1114
|
}
|
|
1138
1115
|
throw error
|
|
1139
1116
|
}
|