@lota-sdk/core 0.1.23 → 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 +5 -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/execution-plan.ts +2 -1
- 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/agent-activity.service.ts +350 -0
- package/src/services/autonomous-job.service.ts +1 -8
- package/src/services/execution-plan.service.ts +205 -334
- package/src/services/index.ts +2 -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 +11 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/project-with-plan.tool.ts +87 -0
- 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
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ChatMessage,
|
|
3
|
-
CreateExecutionPlanArgs,
|
|
4
3
|
ExecutionPlanToolResultData,
|
|
5
4
|
GetActiveExecutionPlanArgs,
|
|
6
5
|
ListExecutionPlansSummary,
|
|
7
6
|
ListExecutionPlansToolResultData,
|
|
8
7
|
PlanEventRecord,
|
|
8
|
+
PlanDraft,
|
|
9
9
|
PlanNodeRunRecord,
|
|
10
10
|
PlanNodeSpecRecord,
|
|
11
|
-
PlanRunRecord,
|
|
12
11
|
PlanSpecRecord,
|
|
13
|
-
ReplaceExecutionPlanArgs,
|
|
14
12
|
ResumeExecutionPlanRunArgs,
|
|
15
13
|
SerializableExecutionPlan,
|
|
16
14
|
SubmitPlanTurnResultArgs,
|
|
@@ -26,7 +24,6 @@ import {
|
|
|
26
24
|
} from '@lota-sdk/shared'
|
|
27
25
|
import { RecordId } from 'surrealdb'
|
|
28
26
|
|
|
29
|
-
import { serverLogger } from '../config/logger'
|
|
30
27
|
import type { RecordIdInput } from '../db/record-id'
|
|
31
28
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
32
29
|
import { databaseService } from '../db/service'
|
|
@@ -35,33 +32,17 @@ import { TABLES } from '../db/tables'
|
|
|
35
32
|
import { readApprovalContinuationResponse } from '../runtime/approval-continuation'
|
|
36
33
|
import { extractMessageText } from '../runtime/workstream-chat-helpers'
|
|
37
34
|
import { toDatabaseDateTime } from '../utils/date-time'
|
|
38
|
-
import { contextEnrichmentService } from './context-enrichment.service'
|
|
39
35
|
import { ownershipDispatcherService } from './ownership-dispatcher.service'
|
|
40
36
|
import { planBuilderService } from './plan-builder.service'
|
|
41
37
|
import type { CompiledPlanNode } from './plan-compiler.service'
|
|
42
38
|
import { planCompilerService } from './plan-compiler.service'
|
|
43
39
|
import { planEventDeliveryService } from './plan-event-delivery.service'
|
|
44
40
|
import { planExecutorService } from './plan-executor.service'
|
|
41
|
+
import { buildExecutionPlanToolResult, toRunData } from './plan-run-data'
|
|
45
42
|
import { planRunService } from './plan-run.service'
|
|
46
43
|
import { planSchedulerService } from './plan-scheduler.service'
|
|
47
44
|
import { planValidatorService } from './plan-validator.service'
|
|
48
45
|
|
|
49
|
-
function buildToolResult(params: {
|
|
50
|
-
action: ExecutionPlanToolResultData['action']
|
|
51
|
-
plan: SerializableExecutionPlan | null
|
|
52
|
-
message: string
|
|
53
|
-
changedNodeId?: string
|
|
54
|
-
}): ExecutionPlanToolResultData {
|
|
55
|
-
return {
|
|
56
|
-
action: params.action,
|
|
57
|
-
message: params.message,
|
|
58
|
-
...(params.changedNodeId ? { changedNodeId: params.changedNodeId } : {}),
|
|
59
|
-
...(params.plan ? { plan: params.plan } : {}),
|
|
60
|
-
hasPlan: params.plan !== null,
|
|
61
|
-
status: params.plan?.status,
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
46
|
function aggregateBlockingIssues(issues: Array<{ code: string; message: string }>): string {
|
|
66
47
|
return issues.map((issue) => `${issue.code}: ${issue.message}`).join(' | ')
|
|
67
48
|
}
|
|
@@ -100,72 +81,6 @@ function toSpecData(spec: PlanSpecRecord, patch: Partial<PlanSpecRecord> & { rep
|
|
|
100
81
|
}
|
|
101
82
|
}
|
|
102
83
|
|
|
103
|
-
type PlanRunUpdate = Omit<
|
|
104
|
-
Partial<PlanRunRecord>,
|
|
105
|
-
'currentNodeId' | 'waitingNodeId' | 'replacedRunId' | 'lastCheckpointId' | 'startedAt' | 'completedAt'
|
|
106
|
-
> & {
|
|
107
|
-
currentNodeId?: string | null
|
|
108
|
-
waitingNodeId?: string | null
|
|
109
|
-
replacedRunId?: RecordIdInput | null
|
|
110
|
-
lastCheckpointId?: RecordIdInput | null
|
|
111
|
-
startedAt?: string | Date | null
|
|
112
|
-
completedAt?: string | Date | null
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function toRunData(run: PlanRunRecord, patch: PlanRunUpdate) {
|
|
116
|
-
return {
|
|
117
|
-
planSpecId: ensureRecordId(run.planSpecId, TABLES.PLAN_SPEC),
|
|
118
|
-
organizationId: ensureRecordId(run.organizationId, TABLES.ORGANIZATION),
|
|
119
|
-
workstreamId: ensureRecordId(run.workstreamId, TABLES.WORKSTREAM),
|
|
120
|
-
leadAgentId: patch.leadAgentId ?? run.leadAgentId,
|
|
121
|
-
status: patch.status ?? run.status,
|
|
122
|
-
...(patch.currentNodeId === null
|
|
123
|
-
? {}
|
|
124
|
-
: patch.currentNodeId !== undefined
|
|
125
|
-
? { currentNodeId: patch.currentNodeId }
|
|
126
|
-
: run.currentNodeId
|
|
127
|
-
? { currentNodeId: run.currentNodeId }
|
|
128
|
-
: {}),
|
|
129
|
-
...(patch.waitingNodeId === null
|
|
130
|
-
? {}
|
|
131
|
-
: patch.waitingNodeId !== undefined
|
|
132
|
-
? { waitingNodeId: patch.waitingNodeId }
|
|
133
|
-
: run.waitingNodeId
|
|
134
|
-
? { waitingNodeId: run.waitingNodeId }
|
|
135
|
-
: {}),
|
|
136
|
-
readyNodeIds: patch.readyNodeIds ? [...patch.readyNodeIds] : [...run.readyNodeIds],
|
|
137
|
-
failureCount: patch.failureCount ?? run.failureCount,
|
|
138
|
-
...(patch.replacedRunId === null
|
|
139
|
-
? {}
|
|
140
|
-
: patch.replacedRunId
|
|
141
|
-
? { replacedRunId: ensureRecordId(patch.replacedRunId, TABLES.PLAN_RUN) }
|
|
142
|
-
: run.replacedRunId
|
|
143
|
-
? { replacedRunId: ensureRecordId(run.replacedRunId, TABLES.PLAN_RUN) }
|
|
144
|
-
: {}),
|
|
145
|
-
...(patch.lastCheckpointId === null
|
|
146
|
-
? {}
|
|
147
|
-
: patch.lastCheckpointId
|
|
148
|
-
? { lastCheckpointId: ensureRecordId(patch.lastCheckpointId, TABLES.PLAN_CHECKPOINT) }
|
|
149
|
-
: run.lastCheckpointId
|
|
150
|
-
? { lastCheckpointId: ensureRecordId(run.lastCheckpointId, TABLES.PLAN_CHECKPOINT) }
|
|
151
|
-
: {}),
|
|
152
|
-
...(patch.startedAt === null
|
|
153
|
-
? {}
|
|
154
|
-
: patch.startedAt !== undefined
|
|
155
|
-
? { startedAt: toDatabaseDateTime(patch.startedAt) }
|
|
156
|
-
: run.startedAt
|
|
157
|
-
? { startedAt: toDatabaseDateTime(run.startedAt) }
|
|
158
|
-
: {}),
|
|
159
|
-
...(patch.completedAt === null
|
|
160
|
-
? {}
|
|
161
|
-
: patch.completedAt !== undefined
|
|
162
|
-
? { completedAt: toDatabaseDateTime(patch.completedAt) }
|
|
163
|
-
: run.completedAt
|
|
164
|
-
? { completedAt: toDatabaseDateTime(run.completedAt) }
|
|
165
|
-
: {}),
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
84
|
function buildApprovalResponseFromMessages(
|
|
170
85
|
messages: ChatMessage[],
|
|
171
86
|
): { approvalId: string; response: Record<string, unknown> } | null {
|
|
@@ -188,6 +103,38 @@ function buildApprovalResponseFromMessages(
|
|
|
188
103
|
return null
|
|
189
104
|
}
|
|
190
105
|
|
|
106
|
+
function buildCompiledSpecCreateData(params: {
|
|
107
|
+
organizationId: RecordIdInput
|
|
108
|
+
workstreamId: RecordIdInput
|
|
109
|
+
leadAgentId: string
|
|
110
|
+
compiled: ReturnType<typeof planCompilerService.compile>
|
|
111
|
+
version: number
|
|
112
|
+
replacedSpecId?: RecordIdInput
|
|
113
|
+
contextEnrichments?: PlanSpecRecord['contextEnrichments']
|
|
114
|
+
}) {
|
|
115
|
+
return {
|
|
116
|
+
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
117
|
+
workstreamId: ensureRecordId(params.workstreamId, TABLES.WORKSTREAM),
|
|
118
|
+
title: params.compiled.draft.title,
|
|
119
|
+
objective: params.compiled.draft.objective,
|
|
120
|
+
version: params.version,
|
|
121
|
+
status: 'compiled' as const,
|
|
122
|
+
leadAgentId: params.leadAgentId,
|
|
123
|
+
schemaRegistry: structuredClone(params.compiled.draft.schemas),
|
|
124
|
+
defaultExecutionVisibility: params.compiled.draft.defaultExecutionVisibility,
|
|
125
|
+
...(params.contextEnrichments?.length
|
|
126
|
+
? { contextEnrichments: params.contextEnrichments.map((entry) => ({ ...entry })) }
|
|
127
|
+
: {}),
|
|
128
|
+
edges: [...params.compiled.draft.edges],
|
|
129
|
+
entryNodeIds: [...(params.compiled.draft.entryNodeIds ?? [])],
|
|
130
|
+
executionMode: params.compiled.draft.executionMode ?? 'linear',
|
|
131
|
+
...(params.compiled.draft.schedule ? { schedule: params.compiled.draft.schedule } : {}),
|
|
132
|
+
...(params.compiled.draft.dependencies ? { dependencies: params.compiled.draft.dependencies } : {}),
|
|
133
|
+
...(params.replacedSpecId ? { replacedSpecId: ensureRecordId(params.replacedSpecId, TABLES.PLAN_SPEC) } : {}),
|
|
134
|
+
compiledAt: new Date(),
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
191
138
|
class ExecutionPlanService {
|
|
192
139
|
async hasActivePlan(workstreamId: RecordIdInput): Promise<boolean> {
|
|
193
140
|
return (await planRunService.getActiveRunRecord(workstreamId)) !== null
|
|
@@ -275,12 +222,12 @@ class ExecutionPlanService {
|
|
|
275
222
|
const plan = await planRunService
|
|
276
223
|
.getRunById(params.runId)
|
|
277
224
|
.then((run) => planRunService.toSerializablePlan(run, serializeOptions))
|
|
278
|
-
return
|
|
225
|
+
return buildExecutionPlanToolResult({ action: 'loaded', plan, message: `Loaded execution run "${plan.title}".` })
|
|
279
226
|
}
|
|
280
227
|
|
|
281
228
|
const runs = await planRunService.getActiveRunRecords(params.workstreamId)
|
|
282
229
|
if (runs.length === 0) {
|
|
283
|
-
return
|
|
230
|
+
return buildExecutionPlanToolResult({ action: 'none', plan: null, message: 'No active execution run.' })
|
|
284
231
|
}
|
|
285
232
|
|
|
286
233
|
const plan = await planRunService.toSerializablePlan(runs[0], serializeOptions)
|
|
@@ -292,7 +239,7 @@ class ExecutionPlanService {
|
|
|
292
239
|
)
|
|
293
240
|
|
|
294
241
|
return {
|
|
295
|
-
...
|
|
242
|
+
...buildExecutionPlanToolResult({
|
|
296
243
|
action: 'loaded',
|
|
297
244
|
plan,
|
|
298
245
|
message:
|
|
@@ -309,7 +256,7 @@ class ExecutionPlanService {
|
|
|
309
256
|
organizationId: RecordIdInput
|
|
310
257
|
workstreamId: RecordIdInput
|
|
311
258
|
leadAgentId: string
|
|
312
|
-
input:
|
|
259
|
+
input: PlanDraft
|
|
313
260
|
}): Promise<ExecutionPlanToolResultData> {
|
|
314
261
|
const preparedDraft = planBuilderService.prepareDraft(params.input)
|
|
315
262
|
const validation = planValidatorService.validateDraft(preparedDraft)
|
|
@@ -319,17 +266,6 @@ class ExecutionPlanService {
|
|
|
319
266
|
await this.assertDispatchExecutors(preparedDraft)
|
|
320
267
|
const compiled = planCompilerService.compile(preparedDraft)
|
|
321
268
|
|
|
322
|
-
// Context enrichment — best-effort, failures do not block plan creation
|
|
323
|
-
const enrichments = await contextEnrichmentService
|
|
324
|
-
.enrichForPlanCreation({
|
|
325
|
-
objective: compiled.draft.objective,
|
|
326
|
-
organizationId: recordIdToString(params.organizationId, TABLES.ORGANIZATION),
|
|
327
|
-
})
|
|
328
|
-
.catch((error: unknown) => {
|
|
329
|
-
serverLogger.error`Context enrichment failed: ${error instanceof Error ? error.message : String(error)}`
|
|
330
|
-
return []
|
|
331
|
-
})
|
|
332
|
-
|
|
333
269
|
const specId = new RecordId(TABLES.PLAN_SPEC, Bun.randomUUIDv7())
|
|
334
270
|
const runId = new RecordId(TABLES.PLAN_RUN, Bun.randomUUIDv7())
|
|
335
271
|
const emittedEvents: PlanEventRecord[] = []
|
|
@@ -338,113 +274,31 @@ class ExecutionPlanService {
|
|
|
338
274
|
const spec = PlanSpecSchema.parse(
|
|
339
275
|
await tx
|
|
340
276
|
.create(specId)
|
|
341
|
-
.content(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
defaultExecutionVisibility: compiled.draft.defaultExecutionVisibility,
|
|
351
|
-
...(enrichments.length > 0
|
|
352
|
-
? { contextEnrichments: enrichments.map((e) => ({ type: e.domain, content: JSON.stringify(e.data) })) }
|
|
353
|
-
: {}),
|
|
354
|
-
edges: [...compiled.draft.edges],
|
|
355
|
-
entryNodeIds: [...(compiled.draft.entryNodeIds ?? [])],
|
|
356
|
-
executionMode: compiled.draft.executionMode ?? 'linear',
|
|
357
|
-
...(compiled.draft.schedule ? { schedule: compiled.draft.schedule } : {}),
|
|
358
|
-
...(compiled.draft.dependencies ? { dependencies: compiled.draft.dependencies } : {}),
|
|
359
|
-
compiledAt: new Date(),
|
|
360
|
-
})
|
|
361
|
-
.output('after'),
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
const nodeSpecs = await this.createNodeSpecs(tx, spec.id, compiled.nodes)
|
|
365
|
-
const run = PlanRunSchema.parse(
|
|
366
|
-
await tx
|
|
367
|
-
.create(runId)
|
|
368
|
-
.content({
|
|
369
|
-
planSpecId: ensureRecordId(spec.id, TABLES.PLAN_SPEC),
|
|
370
|
-
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
371
|
-
workstreamId: ensureRecordId(params.workstreamId, TABLES.WORKSTREAM),
|
|
372
|
-
leadAgentId: params.leadAgentId,
|
|
373
|
-
status: 'running',
|
|
374
|
-
readyNodeIds: [],
|
|
375
|
-
failureCount: 0,
|
|
376
|
-
startedAt: new Date(),
|
|
377
|
-
})
|
|
277
|
+
.content(
|
|
278
|
+
buildCompiledSpecCreateData({
|
|
279
|
+
organizationId: params.organizationId,
|
|
280
|
+
workstreamId: params.workstreamId,
|
|
281
|
+
leadAgentId: params.leadAgentId,
|
|
282
|
+
compiled,
|
|
283
|
+
version: 1,
|
|
284
|
+
}),
|
|
285
|
+
)
|
|
378
286
|
.output('after'),
|
|
379
287
|
)
|
|
380
|
-
|
|
381
|
-
const nodeRuns = await this.createNodeRuns(tx, run.id, spec.id, nodeSpecs)
|
|
382
|
-
const synced = await planExecutorService.syncRunGraph({
|
|
288
|
+
await this.createInitializedRunGraph({
|
|
383
289
|
tx,
|
|
384
|
-
|
|
290
|
+
runId,
|
|
385
291
|
spec,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
292
|
+
organizationId: params.organizationId,
|
|
293
|
+
workstreamId: params.workstreamId,
|
|
294
|
+
leadAgentId: params.leadAgentId,
|
|
295
|
+
nodes: compiled.nodes,
|
|
296
|
+
emittedEvents,
|
|
297
|
+
createdEventType: 'plan-created',
|
|
298
|
+
createdEventMessage: `Created execution plan "${spec.title}".`,
|
|
299
|
+
createdEventDetail: { title: spec.title, objective: spec.objective },
|
|
300
|
+
checkpointReason: 'plan-created',
|
|
391
301
|
})
|
|
392
|
-
|
|
393
|
-
const event = await tx
|
|
394
|
-
.create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
|
|
395
|
-
.content({
|
|
396
|
-
planSpecId: ensureRecordId(spec.id, TABLES.PLAN_SPEC),
|
|
397
|
-
runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
|
|
398
|
-
eventType: 'plan-created',
|
|
399
|
-
message: `Created execution plan "${spec.title}".`,
|
|
400
|
-
emittedBy: params.leadAgentId,
|
|
401
|
-
detail: { title: spec.title, objective: spec.objective, nodeCount: nodeSpecs.length },
|
|
402
|
-
})
|
|
403
|
-
.output('after')
|
|
404
|
-
emittedEvents.push(PlanEventSchema.parse(event))
|
|
405
|
-
|
|
406
|
-
const checkpoint = PlanCheckpointSchema.parse(
|
|
407
|
-
await tx
|
|
408
|
-
.create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
|
|
409
|
-
.content({
|
|
410
|
-
runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
|
|
411
|
-
sequence: 1,
|
|
412
|
-
runStatus: synced.run.status,
|
|
413
|
-
readyNodeIds: [...synced.run.readyNodeIds],
|
|
414
|
-
activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
|
|
415
|
-
artifactIds: [],
|
|
416
|
-
lastCompletedNodeIds: synced.nodeRuns
|
|
417
|
-
.filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
|
|
418
|
-
.map((nodeRun) => nodeRun.nodeId),
|
|
419
|
-
snapshot: {
|
|
420
|
-
reason: 'plan-created',
|
|
421
|
-
currentNodeId: synced.run.currentNodeId,
|
|
422
|
-
waitingNodeId: synced.run.waitingNodeId,
|
|
423
|
-
readyNodeIds: synced.run.readyNodeIds,
|
|
424
|
-
},
|
|
425
|
-
})
|
|
426
|
-
.output('after'),
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
const updatedRun = PlanRunSchema.parse(
|
|
430
|
-
await tx
|
|
431
|
-
.update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
|
|
432
|
-
.content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
|
|
433
|
-
.output('after'),
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
const checkpointEvent = await tx
|
|
437
|
-
.create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
|
|
438
|
-
.content({
|
|
439
|
-
planSpecId: ensureRecordId(spec.id, TABLES.PLAN_SPEC),
|
|
440
|
-
runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
|
|
441
|
-
eventType: 'checkpoint-saved',
|
|
442
|
-
message: 'Saved checkpoint 1.',
|
|
443
|
-
emittedBy: 'system',
|
|
444
|
-
detail: { checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT), reason: 'plan-created' },
|
|
445
|
-
})
|
|
446
|
-
.output('after')
|
|
447
|
-
emittedEvents.push(PlanEventSchema.parse(checkpointEvent))
|
|
448
302
|
})
|
|
449
303
|
|
|
450
304
|
await planEventDeliveryService.dispatchEvents(emittedEvents)
|
|
@@ -472,14 +326,14 @@ class ExecutionPlanService {
|
|
|
472
326
|
|
|
473
327
|
const plan = await this.finalizePlanSnapshot({ runId, emittedBy: params.leadAgentId })
|
|
474
328
|
|
|
475
|
-
return
|
|
329
|
+
return buildExecutionPlanToolResult({ action: 'created', plan, message: `Created execution plan "${plan.title}".` })
|
|
476
330
|
}
|
|
477
331
|
|
|
478
332
|
async replacePlan(params: {
|
|
479
333
|
workstreamId: RecordIdInput
|
|
480
334
|
organizationId: RecordIdInput
|
|
481
335
|
leadAgentId: string
|
|
482
|
-
input:
|
|
336
|
+
input: PlanDraft & { runId: string; reason: string }
|
|
483
337
|
}): Promise<ExecutionPlanToolResultData> {
|
|
484
338
|
const activeRun = await planRunService.getRunById(params.input.runId)
|
|
485
339
|
if (
|
|
@@ -547,119 +401,47 @@ class ExecutionPlanService {
|
|
|
547
401
|
const spec = PlanSpecSchema.parse(
|
|
548
402
|
await tx
|
|
549
403
|
.create(specId)
|
|
550
|
-
.content(
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
edges: [...compiled.draft.edges],
|
|
561
|
-
entryNodeIds: [...(compiled.draft.entryNodeIds ?? [])],
|
|
562
|
-
executionMode: compiled.draft.executionMode ?? 'linear',
|
|
563
|
-
...(compiled.draft.schedule ? { schedule: compiled.draft.schedule } : {}),
|
|
564
|
-
...(compiled.draft.dependencies ? { dependencies: compiled.draft.dependencies } : {}),
|
|
565
|
-
replacedSpecId: ensureRecordId(supersededSpec.id, TABLES.PLAN_SPEC),
|
|
566
|
-
compiledAt: new Date(),
|
|
567
|
-
})
|
|
568
|
-
.output('after'),
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
const nodeSpecs = await this.createNodeSpecs(tx, spec.id, compiled.nodes)
|
|
572
|
-
const run = PlanRunSchema.parse(
|
|
573
|
-
await tx
|
|
574
|
-
.create(runId)
|
|
575
|
-
.content({
|
|
576
|
-
planSpecId: ensureRecordId(spec.id, TABLES.PLAN_SPEC),
|
|
577
|
-
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
578
|
-
workstreamId: ensureRecordId(params.workstreamId, TABLES.WORKSTREAM),
|
|
579
|
-
leadAgentId: params.leadAgentId,
|
|
580
|
-
status: 'running',
|
|
581
|
-
readyNodeIds: [],
|
|
582
|
-
failureCount: 0,
|
|
583
|
-
replacedRunId: ensureRecordId(abortedRun.id, TABLES.PLAN_RUN),
|
|
584
|
-
startedAt: new Date(),
|
|
585
|
-
})
|
|
404
|
+
.content(
|
|
405
|
+
buildCompiledSpecCreateData({
|
|
406
|
+
organizationId: params.organizationId,
|
|
407
|
+
workstreamId: params.workstreamId,
|
|
408
|
+
leadAgentId: params.leadAgentId,
|
|
409
|
+
compiled,
|
|
410
|
+
version: supersededSpec.version + 1,
|
|
411
|
+
replacedSpecId: supersededSpec.id,
|
|
412
|
+
}),
|
|
413
|
+
)
|
|
586
414
|
.output('after'),
|
|
587
415
|
)
|
|
588
|
-
|
|
589
|
-
const nodeRuns = await this.createNodeRuns(tx, run.id, spec.id, nodeSpecs)
|
|
590
|
-
const synced = await planExecutorService.syncRunGraph({
|
|
416
|
+
await this.createInitializedRunGraph({
|
|
591
417
|
tx,
|
|
592
|
-
|
|
418
|
+
runId,
|
|
593
419
|
spec,
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
420
|
+
organizationId: params.organizationId,
|
|
421
|
+
workstreamId: params.workstreamId,
|
|
422
|
+
leadAgentId: params.leadAgentId,
|
|
423
|
+
nodes: compiled.nodes,
|
|
424
|
+
emittedEvents,
|
|
425
|
+
runPatch: { replacedRunId: abortedRun.id },
|
|
426
|
+
createdEventType: 'plan-replaced',
|
|
427
|
+
createdEventMessage: `Replaced execution plan "${activeSpec.title}" with "${spec.title}".`,
|
|
428
|
+
createdEventDetail: {
|
|
429
|
+
reason: params.input.reason,
|
|
430
|
+
replacedRunId: recordIdToString(abortedRun.id, TABLES.PLAN_RUN),
|
|
431
|
+
},
|
|
432
|
+
checkpointReason: 'plan-replaced',
|
|
599
433
|
})
|
|
600
|
-
|
|
601
|
-
const replaceEvent = await tx
|
|
602
|
-
.create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
|
|
603
|
-
.content({
|
|
604
|
-
planSpecId: ensureRecordId(spec.id, TABLES.PLAN_SPEC),
|
|
605
|
-
runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
|
|
606
|
-
eventType: 'plan-replaced',
|
|
607
|
-
message: `Replaced execution plan "${activeSpec.title}" with "${spec.title}".`,
|
|
608
|
-
emittedBy: params.leadAgentId,
|
|
609
|
-
detail: { reason: params.input.reason, replacedRunId: recordIdToString(abortedRun.id, TABLES.PLAN_RUN) },
|
|
610
|
-
})
|
|
611
|
-
.output('after')
|
|
612
|
-
emittedEvents.push(PlanEventSchema.parse(replaceEvent))
|
|
613
|
-
|
|
614
|
-
const checkpoint = PlanCheckpointSchema.parse(
|
|
615
|
-
await tx
|
|
616
|
-
.create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
|
|
617
|
-
.content({
|
|
618
|
-
runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
|
|
619
|
-
sequence: 1,
|
|
620
|
-
runStatus: synced.run.status,
|
|
621
|
-
readyNodeIds: [...synced.run.readyNodeIds],
|
|
622
|
-
activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
|
|
623
|
-
artifactIds: [],
|
|
624
|
-
lastCompletedNodeIds: synced.nodeRuns
|
|
625
|
-
.filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
|
|
626
|
-
.map((nodeRun) => nodeRun.nodeId),
|
|
627
|
-
snapshot: {
|
|
628
|
-
reason: 'plan-replaced',
|
|
629
|
-
currentNodeId: synced.run.currentNodeId,
|
|
630
|
-
waitingNodeId: synced.run.waitingNodeId,
|
|
631
|
-
readyNodeIds: synced.run.readyNodeIds,
|
|
632
|
-
},
|
|
633
|
-
})
|
|
634
|
-
.output('after'),
|
|
635
|
-
)
|
|
636
|
-
|
|
637
|
-
const updatedRun = PlanRunSchema.parse(
|
|
638
|
-
await tx
|
|
639
|
-
.update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
|
|
640
|
-
.content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
|
|
641
|
-
.output('after'),
|
|
642
|
-
)
|
|
643
|
-
|
|
644
|
-
const checkpointEvent = await tx
|
|
645
|
-
.create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
|
|
646
|
-
.content({
|
|
647
|
-
planSpecId: ensureRecordId(spec.id, TABLES.PLAN_SPEC),
|
|
648
|
-
runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
|
|
649
|
-
eventType: 'checkpoint-saved',
|
|
650
|
-
message: 'Saved checkpoint 1.',
|
|
651
|
-
emittedBy: 'system',
|
|
652
|
-
detail: { checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT), reason: 'plan-replaced' },
|
|
653
|
-
})
|
|
654
|
-
.output('after')
|
|
655
|
-
emittedEvents.push(PlanEventSchema.parse(checkpointEvent))
|
|
656
434
|
})
|
|
657
435
|
|
|
658
436
|
await planEventDeliveryService.dispatchEvents(emittedEvents)
|
|
659
437
|
|
|
660
438
|
const plan = await this.finalizePlanSnapshot({ runId, emittedBy: params.leadAgentId })
|
|
661
439
|
|
|
662
|
-
return
|
|
440
|
+
return buildExecutionPlanToolResult({
|
|
441
|
+
action: 'replaced',
|
|
442
|
+
plan,
|
|
443
|
+
message: `Replaced execution plan with "${plan.title}".`,
|
|
444
|
+
})
|
|
663
445
|
}
|
|
664
446
|
|
|
665
447
|
async submitNodeResult(params: {
|
|
@@ -667,23 +449,12 @@ class ExecutionPlanService {
|
|
|
667
449
|
emittedBy: string
|
|
668
450
|
input: SubmitExecutionNodeResultArgs
|
|
669
451
|
}): Promise<ExecutionPlanToolResultData> {
|
|
670
|
-
|
|
452
|
+
return await this.submitPlanTurnResult({
|
|
671
453
|
workstreamId: params.workstreamId,
|
|
672
|
-
runId: params.input.runId,
|
|
673
|
-
nodeId: params.input.nodeId,
|
|
674
454
|
emittedBy: params.emittedBy,
|
|
675
|
-
result: params.input.result,
|
|
676
|
-
})
|
|
677
|
-
const plan = await ownershipDispatcherService.dispatchRunToStableBoundary({
|
|
678
455
|
runId: params.input.runId,
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
return buildToolResult({
|
|
683
|
-
action: result.action,
|
|
684
|
-
plan,
|
|
685
|
-
message: result.message ?? `Submitted result for node "${params.input.nodeId}".`,
|
|
686
|
-
changedNodeId: result.changedNodeId ?? undefined,
|
|
456
|
+
nodeId: params.input.nodeId,
|
|
457
|
+
input: params.input.result,
|
|
687
458
|
})
|
|
688
459
|
}
|
|
689
460
|
|
|
@@ -706,7 +477,7 @@ class ExecutionPlanService {
|
|
|
706
477
|
emittedBy: params.emittedBy,
|
|
707
478
|
})
|
|
708
479
|
|
|
709
|
-
return
|
|
480
|
+
return buildExecutionPlanToolResult({
|
|
710
481
|
action: result.action,
|
|
711
482
|
plan,
|
|
712
483
|
message: result.message ?? `Submitted result for node "${params.nodeId}".`,
|
|
@@ -729,7 +500,7 @@ class ExecutionPlanService {
|
|
|
729
500
|
emittedBy: params.emittedBy,
|
|
730
501
|
})
|
|
731
502
|
|
|
732
|
-
return
|
|
503
|
+
return buildExecutionPlanToolResult({
|
|
733
504
|
action: result.action,
|
|
734
505
|
plan,
|
|
735
506
|
message: result.message ?? `Resumed execution run "${params.input.runId}".`,
|
|
@@ -745,18 +516,11 @@ class ExecutionPlanService {
|
|
|
745
516
|
const approvalResponse = buildApprovalResponseFromMessages(params.approvalMessages)
|
|
746
517
|
if (!approvalResponse) return null
|
|
747
518
|
|
|
748
|
-
|
|
749
|
-
if (!run) return null
|
|
750
|
-
|
|
751
|
-
const plan = await planExecutorService.submitHumanNodeResponse({
|
|
519
|
+
return await this.respondToApproval({
|
|
752
520
|
workstreamId: params.workstreamId,
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
response: approvalResponse.response,
|
|
521
|
+
emittedBy: params.respondedBy,
|
|
522
|
+
input: approvalResponse,
|
|
756
523
|
})
|
|
757
|
-
if (!plan) return null
|
|
758
|
-
|
|
759
|
-
return ownershipDispatcherService.dispatchRunToStableBoundary({ runId: run.id, emittedBy: params.respondedBy })
|
|
760
524
|
}
|
|
761
525
|
|
|
762
526
|
async respondToApproval(params: {
|
|
@@ -912,6 +676,113 @@ class ExecutionPlanService {
|
|
|
912
676
|
|
|
913
677
|
return createdNodeRuns
|
|
914
678
|
}
|
|
679
|
+
|
|
680
|
+
private async createInitializedRunGraph(params: {
|
|
681
|
+
tx: DatabaseTransaction
|
|
682
|
+
runId: RecordIdInput
|
|
683
|
+
spec: PlanSpecRecord
|
|
684
|
+
organizationId: RecordIdInput
|
|
685
|
+
workstreamId: RecordIdInput
|
|
686
|
+
leadAgentId: string
|
|
687
|
+
nodes: CompiledPlanNode[]
|
|
688
|
+
emittedEvents: PlanEventRecord[]
|
|
689
|
+
createdEventType: 'plan-created' | 'plan-replaced'
|
|
690
|
+
createdEventMessage: string
|
|
691
|
+
createdEventDetail: Record<string, unknown>
|
|
692
|
+
checkpointReason: 'plan-created' | 'plan-replaced'
|
|
693
|
+
runPatch?: { replacedRunId?: RecordIdInput }
|
|
694
|
+
}): Promise<void> {
|
|
695
|
+
const nodeSpecs = await this.createNodeSpecs(params.tx, params.spec.id, params.nodes)
|
|
696
|
+
const run = PlanRunSchema.parse(
|
|
697
|
+
await params.tx
|
|
698
|
+
.create(ensureRecordId(params.runId, TABLES.PLAN_RUN))
|
|
699
|
+
.content({
|
|
700
|
+
planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
|
|
701
|
+
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
702
|
+
workstreamId: ensureRecordId(params.workstreamId, TABLES.WORKSTREAM),
|
|
703
|
+
leadAgentId: params.leadAgentId,
|
|
704
|
+
status: 'running',
|
|
705
|
+
readyNodeIds: [],
|
|
706
|
+
failureCount: 0,
|
|
707
|
+
...(params.runPatch?.replacedRunId
|
|
708
|
+
? { replacedRunId: ensureRecordId(params.runPatch.replacedRunId, TABLES.PLAN_RUN) }
|
|
709
|
+
: {}),
|
|
710
|
+
startedAt: new Date(),
|
|
711
|
+
})
|
|
712
|
+
.output('after'),
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
const nodeRuns = await this.createNodeRuns(params.tx, run.id, params.spec.id, nodeSpecs)
|
|
716
|
+
const synced = await planExecutorService.syncRunGraph({
|
|
717
|
+
tx: params.tx,
|
|
718
|
+
run,
|
|
719
|
+
spec: params.spec,
|
|
720
|
+
nodeSpecs,
|
|
721
|
+
nodeRuns,
|
|
722
|
+
artifacts: [],
|
|
723
|
+
emittedBy: params.leadAgentId,
|
|
724
|
+
capturedEvents: params.emittedEvents,
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
const event = await params.tx
|
|
728
|
+
.create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
|
|
729
|
+
.content({
|
|
730
|
+
planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
|
|
731
|
+
runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
|
|
732
|
+
eventType: params.createdEventType,
|
|
733
|
+
message: params.createdEventMessage,
|
|
734
|
+
emittedBy: params.leadAgentId,
|
|
735
|
+
detail: { ...params.createdEventDetail, nodeCount: nodeSpecs.length },
|
|
736
|
+
})
|
|
737
|
+
.output('after')
|
|
738
|
+
params.emittedEvents.push(PlanEventSchema.parse(event))
|
|
739
|
+
|
|
740
|
+
const checkpoint = PlanCheckpointSchema.parse(
|
|
741
|
+
await params.tx
|
|
742
|
+
.create(new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7()))
|
|
743
|
+
.content({
|
|
744
|
+
runId: ensureRecordId(synced.run.id, TABLES.PLAN_RUN),
|
|
745
|
+
sequence: 1,
|
|
746
|
+
runStatus: synced.run.status,
|
|
747
|
+
readyNodeIds: [...synced.run.readyNodeIds],
|
|
748
|
+
activeNodeIds: synced.run.currentNodeId ? [synced.run.currentNodeId] : [],
|
|
749
|
+
artifactIds: [],
|
|
750
|
+
lastCompletedNodeIds: synced.nodeRuns
|
|
751
|
+
.filter((nodeRun) => nodeRun.status === 'completed' || nodeRun.status === 'partial')
|
|
752
|
+
.map((nodeRun) => nodeRun.nodeId),
|
|
753
|
+
snapshot: {
|
|
754
|
+
reason: params.checkpointReason,
|
|
755
|
+
currentNodeId: synced.run.currentNodeId,
|
|
756
|
+
waitingNodeId: synced.run.waitingNodeId,
|
|
757
|
+
readyNodeIds: synced.run.readyNodeIds,
|
|
758
|
+
},
|
|
759
|
+
})
|
|
760
|
+
.output('after'),
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
const updatedRun = PlanRunSchema.parse(
|
|
764
|
+
await params.tx
|
|
765
|
+
.update(ensureRecordId(synced.run.id, TABLES.PLAN_RUN))
|
|
766
|
+
.content(toRunData(synced.run, { lastCheckpointId: checkpoint.id }))
|
|
767
|
+
.output('after'),
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
const checkpointEvent = await params.tx
|
|
771
|
+
.create(new RecordId(TABLES.PLAN_EVENT, Bun.randomUUIDv7()))
|
|
772
|
+
.content({
|
|
773
|
+
planSpecId: ensureRecordId(params.spec.id, TABLES.PLAN_SPEC),
|
|
774
|
+
runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
|
|
775
|
+
eventType: 'checkpoint-saved',
|
|
776
|
+
message: 'Saved checkpoint 1.',
|
|
777
|
+
emittedBy: 'system',
|
|
778
|
+
detail: {
|
|
779
|
+
checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT),
|
|
780
|
+
reason: params.checkpointReason,
|
|
781
|
+
},
|
|
782
|
+
})
|
|
783
|
+
.output('after')
|
|
784
|
+
params.emittedEvents.push(PlanEventSchema.parse(checkpointEvent))
|
|
785
|
+
}
|
|
915
786
|
}
|
|
916
787
|
|
|
917
788
|
export const executionPlanService = new ExecutionPlanService()
|