@lota-sdk/core 0.1.21 → 0.1.23
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/02_execution_plan.surql +4 -0
- package/package.json +3 -3
- package/src/ai-gateway/ai-gateway.ts +4 -0
- package/src/create-runtime.ts +8 -0
- package/src/queues/index.ts +1 -0
- package/src/queues/plan-agent-heartbeat.queue.ts +100 -0
- package/src/redis/redis-lease-lock.ts +1 -1
- package/src/runtime/agent-runtime-policy.ts +41 -4
- package/src/runtime/execution-plan-visibility.ts +23 -0
- package/src/runtime/execution-plan.ts +1 -0
- package/src/runtime/runtime-extensions.ts +26 -0
- package/src/runtime/runtime-worker-registry.ts +9 -1
- package/src/services/agent-executor.service.ts +6 -0
- package/src/services/execution-plan.service.ts +51 -36
- package/src/services/index.ts +3 -0
- package/src/services/ownership-dispatcher.service.ts +50 -8
- package/src/services/plan-agent-heartbeat.service.ts +136 -0
- package/src/services/plan-agent-query.service.ts +238 -0
- package/src/services/plan-builder.service.ts +11 -1
- package/src/services/plan-compiler.service.ts +2 -0
- package/src/services/plan-deadline.service.ts +186 -44
- package/src/services/plan-event-delivery.service.ts +170 -0
- package/src/services/plan-executor.service.ts +107 -3
- package/src/services/plan-helpers.ts +13 -0
- package/src/services/plan-run.service.ts +4 -0
- package/src/services/plan-template.service.ts +0 -1
- package/src/services/workstream-turn-preparation.service.ts +452 -176
- package/src/services/workstream-turn.ts +101 -1
- package/src/services/workstream.service.ts +76 -16
- package/src/tools/execution-plan.tool.ts +0 -2
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import type { ChatMessage } from '@lota-sdk/shared'
|
|
1
|
+
import type { ChatMessage, PlanNodeHandoffContext } from '@lota-sdk/shared'
|
|
2
2
|
import { createUIMessageStream } from 'ai'
|
|
3
3
|
|
|
4
4
|
import { lotaDebugLogger } from '../config/debug-logger'
|
|
5
|
+
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
6
|
+
import { TABLES } from '../db/tables'
|
|
5
7
|
import { hasApprovalRespondedParts, isApprovalContinuationRequest } from '../runtime/approval-continuation'
|
|
8
|
+
import { shouldPlanNodeUseVisibleTurn } from '../runtime/execution-plan-visibility'
|
|
6
9
|
import { wrapResponseWithKeepalive } from '../utils/sse-keepalive'
|
|
10
|
+
import { planExecutorService } from './plan-executor.service'
|
|
11
|
+
import { planRunService } from './plan-run.service'
|
|
12
|
+
import { userService } from './user.service'
|
|
7
13
|
import { prepareWorkstreamRunCore } from './workstream-turn-preparation.service'
|
|
8
14
|
import type {
|
|
9
15
|
PreparedWorkstreamTurnResult,
|
|
10
16
|
WorkstreamApprovalContinuationParams,
|
|
17
|
+
WorkstreamPlanTurnParams,
|
|
11
18
|
WorkstreamTurnParams,
|
|
12
19
|
} from './workstream-turn-preparation.service'
|
|
20
|
+
import { workstreamService } from './workstream.service'
|
|
13
21
|
|
|
14
22
|
export { hasApprovalRespondedParts, isApprovalContinuationRequest }
|
|
15
23
|
export { wrapResponseWithKeepalive }
|
|
16
24
|
export type { PreparedWorkstreamTurnResult }
|
|
25
|
+
export type { WorkstreamPlanTurnParams }
|
|
17
26
|
|
|
18
27
|
export async function createWorkstreamApprovalContinuationStream(params: WorkstreamApprovalContinuationParams) {
|
|
19
28
|
const timer = lotaDebugLogger.timer('turn:approval-continuation')
|
|
@@ -70,3 +79,94 @@ export async function runWorkstreamTurnInBackground(
|
|
|
70
79
|
timer.step('run')
|
|
71
80
|
return result
|
|
72
81
|
}
|
|
82
|
+
|
|
83
|
+
export async function triggerPlanNodeTurn(params: {
|
|
84
|
+
runId: string
|
|
85
|
+
nodeId: string
|
|
86
|
+
abortSignal?: AbortSignal
|
|
87
|
+
streamId?: string
|
|
88
|
+
}): Promise<PreparedWorkstreamTurnResult> {
|
|
89
|
+
const timer = lotaDebugLogger.timer('turn:plan-turn')
|
|
90
|
+
let run = await planRunService.getRunById(params.runId)
|
|
91
|
+
const spec = await planRunService.getPlanSpecById(run.planSpecId)
|
|
92
|
+
const nodeSpec = await planRunService.getNodeSpecByNodeId(spec.id, params.nodeId)
|
|
93
|
+
|
|
94
|
+
if (!shouldPlanNodeUseVisibleTurn(spec, nodeSpec) || nodeSpec.owner.executorType !== 'agent') {
|
|
95
|
+
throw new Error(`Plan node "${params.nodeId}" is not eligible for a visible plan turn.`)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let nodeRun = await planRunService.getNodeRunByNodeId(run.id, params.nodeId)
|
|
99
|
+
if (nodeRun.status === 'ready') {
|
|
100
|
+
await planExecutorService.transitionNodeToRunning({ runId: params.runId, nodeId: params.nodeId })
|
|
101
|
+
run = await planRunService.getRunById(params.runId)
|
|
102
|
+
nodeRun = await planRunService.getNodeRunByNodeId(run.id, params.nodeId)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (nodeRun.status !== 'running') {
|
|
106
|
+
return { assistantMessages: [], inputMessageId: undefined }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const workstream = await workstreamService.getWorkstream(run.workstreamId)
|
|
110
|
+
const userRef = ensureRecordId(workstream.userId, TABLES.USER)
|
|
111
|
+
|
|
112
|
+
const [artifacts, nodeRuns, userName] = await Promise.all([
|
|
113
|
+
planRunService.listArtifacts(run.id),
|
|
114
|
+
planRunService.listNodeRuns(run.id),
|
|
115
|
+
userService
|
|
116
|
+
.getUser(recordIdToString(userRef, TABLES.USER))
|
|
117
|
+
.then((user) => user.name)
|
|
118
|
+
.catch(() => undefined),
|
|
119
|
+
])
|
|
120
|
+
const inputArtifacts = artifacts
|
|
121
|
+
.filter((artifact) => nodeSpec.upstreamNodeIds.includes(artifact.nodeId))
|
|
122
|
+
.map((artifact) => ({
|
|
123
|
+
name: artifact.name,
|
|
124
|
+
kind: artifact.kind,
|
|
125
|
+
pointer: artifact.pointer,
|
|
126
|
+
...(artifact.schemaRef ? { schemaRef: artifact.schemaRef } : {}),
|
|
127
|
+
...(artifact.description ? { description: artifact.description } : {}),
|
|
128
|
+
...(artifact.payload !== undefined ? { payload: artifact.payload } : {}),
|
|
129
|
+
}))
|
|
130
|
+
const upstreamNodeSpecs = new Map(
|
|
131
|
+
(await planRunService.listNodeSpecs(spec.id)).map((upstreamNodeSpec) => [
|
|
132
|
+
upstreamNodeSpec.nodeId,
|
|
133
|
+
upstreamNodeSpec,
|
|
134
|
+
]),
|
|
135
|
+
)
|
|
136
|
+
const upstreamHandoffs = nodeRuns
|
|
137
|
+
.filter((candidate) => nodeSpec.upstreamNodeIds.includes(candidate.nodeId) && candidate.handoffContext)
|
|
138
|
+
.map((candidate) => ({
|
|
139
|
+
nodeId: candidate.nodeId,
|
|
140
|
+
label: upstreamNodeSpecs.get(candidate.nodeId)?.label ?? candidate.nodeId,
|
|
141
|
+
ownerRef: upstreamNodeSpecs.get(candidate.nodeId)?.owner.ref ?? 'system',
|
|
142
|
+
ownerType: upstreamNodeSpecs.get(candidate.nodeId)?.owner.executorType ?? 'system',
|
|
143
|
+
handoffContext: candidate.handoffContext as PlanNodeHandoffContext,
|
|
144
|
+
}))
|
|
145
|
+
timer.step('load-plan-turn-context')
|
|
146
|
+
|
|
147
|
+
const prepared = await prepareWorkstreamRunCore({
|
|
148
|
+
kind: 'planTurn',
|
|
149
|
+
workstream,
|
|
150
|
+
workstreamRef: ensureRecordId(run.workstreamId, TABLES.WORKSTREAM),
|
|
151
|
+
orgRef: ensureRecordId(run.organizationId, TABLES.ORGANIZATION),
|
|
152
|
+
userRef,
|
|
153
|
+
userName,
|
|
154
|
+
abortSignal: params.abortSignal,
|
|
155
|
+
streamId: params.streamId,
|
|
156
|
+
planTurn: {
|
|
157
|
+
runId: params.runId,
|
|
158
|
+
nodeId: params.nodeId,
|
|
159
|
+
planTitle: spec.title,
|
|
160
|
+
nodeSpec,
|
|
161
|
+
nodeRun,
|
|
162
|
+
resolvedInput: nodeRun.resolvedInput ?? {},
|
|
163
|
+
inputArtifacts,
|
|
164
|
+
upstreamHandoffs,
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
timer.step('prepare')
|
|
168
|
+
|
|
169
|
+
const result = await prepared.run()
|
|
170
|
+
timer.step('run')
|
|
171
|
+
return result
|
|
172
|
+
}
|
|
@@ -10,6 +10,7 @@ import type { RecordIdInput, RecordIdRef } from '../db/record-id'
|
|
|
10
10
|
import { databaseService } from '../db/service'
|
|
11
11
|
import type { DatabaseTable } from '../db/tables'
|
|
12
12
|
import { TABLES } from '../db/tables'
|
|
13
|
+
import { getRedisConnection, withRedisLeaseLock } from '../redis'
|
|
13
14
|
import {
|
|
14
15
|
MEMORY_BLOCK_COMPACTION_CHUNK_ENTRIES,
|
|
15
16
|
MEMORY_BLOCK_COMPACTION_TRIGGER_ENTRIES,
|
|
@@ -62,6 +63,10 @@ WHERE userId = $userId
|
|
|
62
63
|
AND status = "regular"
|
|
63
64
|
ORDER BY updatedAt DESC`
|
|
64
65
|
|
|
66
|
+
const WORKSTREAM_ACTIVE_RUN_LOCK_TTL_MS = 90_000
|
|
67
|
+
const WORKSTREAM_ACTIVE_RUN_LOCK_MAX_WAIT_MS = 750
|
|
68
|
+
const WORKSTREAM_ACTIVE_RUN_LOCK_RETRY_DELAY_MS = 75
|
|
69
|
+
|
|
65
70
|
function toSafeDirectIdSegment(value: string): string {
|
|
66
71
|
return value.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
67
72
|
}
|
|
@@ -107,6 +112,17 @@ function requireString(coreType: string | undefined): string {
|
|
|
107
112
|
return coreType
|
|
108
113
|
}
|
|
109
114
|
|
|
115
|
+
function buildActiveRunLockKey(workstreamId: RecordIdRef): string {
|
|
116
|
+
return `workstream-active-run:${recordIdToString(ensureRecordId(workstreamId, TABLES.WORKSTREAM), TABLES.WORKSTREAM)}`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class ActiveWorkstreamRunConflictError extends Error {
|
|
120
|
+
constructor() {
|
|
121
|
+
super('A chat run is already active.')
|
|
122
|
+
this.name = 'ActiveWorkstreamRunConflictError'
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
110
126
|
function buildDirectWorkstreamId({
|
|
111
127
|
userId,
|
|
112
128
|
orgId,
|
|
@@ -188,7 +204,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
188
204
|
|
|
189
205
|
const existing = await this.findById(directWorkstreamId)
|
|
190
206
|
if (existing) {
|
|
191
|
-
return this.
|
|
207
|
+
return await this.toNormalizedWorkstream(existing)
|
|
192
208
|
}
|
|
193
209
|
|
|
194
210
|
let createError: unknown = null
|
|
@@ -216,7 +232,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
216
232
|
throw new Error('Failed to create or load direct workstream')
|
|
217
233
|
}
|
|
218
234
|
|
|
219
|
-
return this.
|
|
235
|
+
return await this.toNormalizedWorkstream(workstream)
|
|
220
236
|
}
|
|
221
237
|
|
|
222
238
|
if (core) {
|
|
@@ -225,7 +241,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
225
241
|
const coreWorkstreamId = buildCoreWorkstreamId({ userId, orgId, coreType: resolvedCoreType })
|
|
226
242
|
const existing = await this.findById(coreWorkstreamId)
|
|
227
243
|
if (existing) {
|
|
228
|
-
return this.
|
|
244
|
+
return await this.toNormalizedWorkstream(existing)
|
|
229
245
|
}
|
|
230
246
|
|
|
231
247
|
let createError: unknown = null
|
|
@@ -263,7 +279,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
263
279
|
throw new Error('Failed to create or load core workstream')
|
|
264
280
|
}
|
|
265
281
|
|
|
266
|
-
return this.
|
|
282
|
+
return await this.toNormalizedWorkstream(workstream)
|
|
267
283
|
}
|
|
268
284
|
|
|
269
285
|
const groupWorkstream = await this.create({
|
|
@@ -276,7 +292,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
276
292
|
nameGenerated: options?.title !== undefined && options.title !== WORKSTREAM.DEFAULT_TITLE,
|
|
277
293
|
})
|
|
278
294
|
|
|
279
|
-
return this.
|
|
295
|
+
return await this.toNormalizedWorkstream(groupWorkstream)
|
|
280
296
|
}
|
|
281
297
|
|
|
282
298
|
async ensureBootstrapWorkstreams(
|
|
@@ -387,7 +403,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
387
403
|
WorkstreamSchema,
|
|
388
404
|
)
|
|
389
405
|
|
|
390
|
-
return { workstreams:
|
|
406
|
+
return { workstreams: await this.toNormalizedWorkstreams(workstreams), hasMore: false }
|
|
391
407
|
}
|
|
392
408
|
|
|
393
409
|
const take = options.take ?? WORKSTREAM.DEFAULT_PAGE_LIMIT
|
|
@@ -408,7 +424,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
408
424
|
const hasMore = workstreams.length > take
|
|
409
425
|
const sliced = hasMore ? workstreams.slice(0, take) : workstreams
|
|
410
426
|
|
|
411
|
-
return { workstreams:
|
|
427
|
+
return { workstreams: await this.toNormalizedWorkstreams(sliced), hasMore }
|
|
412
428
|
}
|
|
413
429
|
|
|
414
430
|
async listOrganizationWorkstreams(params: {
|
|
@@ -450,19 +466,19 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
450
466
|
WorkstreamSchema,
|
|
451
467
|
)
|
|
452
468
|
|
|
453
|
-
return
|
|
469
|
+
return await this.toNormalizedWorkstreams(workstreams)
|
|
454
470
|
}
|
|
455
471
|
|
|
456
472
|
async getWorkstream(workstreamId: RecordIdRef): Promise<NormalizedWorkstream> {
|
|
457
473
|
const workstream = await this.getById(workstreamId)
|
|
458
|
-
return this.
|
|
474
|
+
return await this.toNormalizedWorkstream(workstream)
|
|
459
475
|
}
|
|
460
476
|
|
|
461
477
|
async updateTitle(workstreamId: RecordIdRef, title: string): Promise<NormalizedWorkstream> {
|
|
462
478
|
const existing = await this.getById(workstreamId)
|
|
463
479
|
this.assertMutableWorkstream(existing, 'rename')
|
|
464
480
|
const workstream = await this.update(workstreamId, { title, nameGenerated: true })
|
|
465
|
-
return this.
|
|
481
|
+
return await this.toNormalizedWorkstream(workstream)
|
|
466
482
|
}
|
|
467
483
|
|
|
468
484
|
async updateStatus(workstreamId: RecordIdRef, status: string): Promise<NormalizedWorkstream> {
|
|
@@ -470,7 +486,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
470
486
|
const existing = await this.getById(workstreamId)
|
|
471
487
|
this.assertMutableWorkstream(existing, validStatus === 'archived' ? 'archive' : 'unarchive')
|
|
472
488
|
const workstream = await this.update(workstreamId, { status: validStatus })
|
|
473
|
-
return this.
|
|
489
|
+
return await this.toNormalizedWorkstream(workstream)
|
|
474
490
|
}
|
|
475
491
|
|
|
476
492
|
async setActiveRunId(workstreamId: RecordIdRef, runId: string | null): Promise<void> {
|
|
@@ -497,6 +513,33 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
497
513
|
return normalized.length > 0 ? normalized : null
|
|
498
514
|
}
|
|
499
515
|
|
|
516
|
+
async hasActiveRunLease(workstreamId: RecordIdRef): Promise<boolean> {
|
|
517
|
+
const count = await getRedisConnection().exists(buildActiveRunLockKey(workstreamId))
|
|
518
|
+
return count > 0
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async withActiveRunLease<T>(workstreamId: RecordIdRef, fn: (signal: AbortSignal) => Promise<T>): Promise<T> {
|
|
522
|
+
try {
|
|
523
|
+
return await withRedisLeaseLock(
|
|
524
|
+
{
|
|
525
|
+
redis: getRedisConnection(),
|
|
526
|
+
lockKey: buildActiveRunLockKey(workstreamId),
|
|
527
|
+
lockTtlMs: WORKSTREAM_ACTIVE_RUN_LOCK_TTL_MS,
|
|
528
|
+
retryDelayMs: WORKSTREAM_ACTIVE_RUN_LOCK_RETRY_DELAY_MS,
|
|
529
|
+
maxWaitMs: WORKSTREAM_ACTIVE_RUN_LOCK_MAX_WAIT_MS,
|
|
530
|
+
label: 'workstream active run',
|
|
531
|
+
logger: serverLogger,
|
|
532
|
+
},
|
|
533
|
+
fn,
|
|
534
|
+
)
|
|
535
|
+
} catch (error) {
|
|
536
|
+
if (error instanceof Error && error.message.startsWith('Timed out waiting for workstream active run')) {
|
|
537
|
+
throw new ActiveWorkstreamRunConflictError()
|
|
538
|
+
}
|
|
539
|
+
throw error
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
500
543
|
async clearActiveRunIdIfMatches(workstreamId: RecordIdRef, runId: string): Promise<void> {
|
|
501
544
|
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
502
545
|
await databaseService.query(surql`UPDATE ONLY ${workstreamRef} SET activeRunId = NONE WHERE activeRunId = ${runId}`)
|
|
@@ -527,7 +570,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
527
570
|
|
|
528
571
|
async clearStaleActiveRunIfMissingFromRegistry(workstreamId: RecordIdRef): Promise<boolean> {
|
|
529
572
|
const activeRunId = await this.getActiveRunId(workstreamId)
|
|
530
|
-
if (!activeRunId ||
|
|
573
|
+
if (!activeRunId || (await this.hasActiveRunLease(workstreamId))) {
|
|
531
574
|
return false
|
|
532
575
|
}
|
|
533
576
|
|
|
@@ -537,7 +580,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
537
580
|
activeStreamId ? this.clearActiveStreamIdIfMatches(workstreamId, activeStreamId) : Promise.resolve(),
|
|
538
581
|
])
|
|
539
582
|
|
|
540
|
-
serverLogger.warn`Cleared stale workstream run after
|
|
583
|
+
serverLogger.warn`Cleared stale workstream run after lease expired: workstream=${recordIdToString(ensureRecordId(workstreamId, TABLES.WORKSTREAM), TABLES.WORKSTREAM)} run=${activeRunId}`
|
|
541
584
|
return true
|
|
542
585
|
}
|
|
543
586
|
|
|
@@ -656,7 +699,7 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
656
699
|
WorkstreamSchema,
|
|
657
700
|
)
|
|
658
701
|
|
|
659
|
-
return
|
|
702
|
+
return await this.toNormalizedWorkstreams(workstreams)
|
|
660
703
|
}
|
|
661
704
|
|
|
662
705
|
private normalizeWorkstreamId(id: unknown): string {
|
|
@@ -686,12 +729,25 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
686
729
|
return WORKSTREAM.DEFAULT_TITLE
|
|
687
730
|
}
|
|
688
731
|
|
|
689
|
-
|
|
732
|
+
private async computeIsRunning(workstream: Pick<WorkstreamRecord, 'id' | 'activeRunId'>): Promise<boolean> {
|
|
690
733
|
const activeRunId =
|
|
691
734
|
typeof workstream.activeRunId === 'string' && workstream.activeRunId.trim().length > 0
|
|
692
735
|
? workstream.activeRunId
|
|
693
736
|
: null
|
|
694
|
-
|
|
737
|
+
|
|
738
|
+
if (activeRunId === null) {
|
|
739
|
+
return false
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (chatRunRegistry.has(activeRunId)) {
|
|
743
|
+
return true
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return await this.hasActiveRunLease(ensureRecordId(workstream.id, TABLES.WORKSTREAM))
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private async toNormalizedWorkstream(workstream: WorkstreamRecord): Promise<NormalizedWorkstream> {
|
|
750
|
+
const isRunning = await this.computeIsRunning(workstream)
|
|
695
751
|
const isCompacting = workstream.isCompacting === true
|
|
696
752
|
const mode = workstream.mode
|
|
697
753
|
const core = workstream.core
|
|
@@ -716,6 +772,10 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
716
772
|
}
|
|
717
773
|
}
|
|
718
774
|
|
|
775
|
+
private async toNormalizedWorkstreams(workstreams: WorkstreamRecord[]): Promise<NormalizedWorkstream[]> {
|
|
776
|
+
return await Promise.all(workstreams.map(async (workstream) => await this.toNormalizedWorkstream(workstream)))
|
|
777
|
+
}
|
|
778
|
+
|
|
719
779
|
toPublicWorkstream(workstream: NormalizedWorkstream) {
|
|
720
780
|
return {
|
|
721
781
|
id: workstream.id,
|
|
@@ -27,7 +27,6 @@ export function createCreateExecutionPlanTool(params: {
|
|
|
27
27
|
organizationId: params.orgId,
|
|
28
28
|
workstreamId: params.workstreamId,
|
|
29
29
|
leadAgentId: params.agentId,
|
|
30
|
-
dispatchMode: 'deferred',
|
|
31
30
|
input,
|
|
32
31
|
})
|
|
33
32
|
params.onPlanChanged?.()
|
|
@@ -51,7 +50,6 @@ export function createReplaceExecutionPlanTool(params: {
|
|
|
51
50
|
organizationId: params.orgId,
|
|
52
51
|
workstreamId: params.workstreamId,
|
|
53
52
|
leadAgentId: params.agentId,
|
|
54
|
-
dispatchMode: 'deferred',
|
|
55
53
|
input,
|
|
56
54
|
})
|
|
57
55
|
params.onPlanChanged?.()
|