@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
|
@@ -13,10 +13,12 @@ import type {
|
|
|
13
13
|
SerializableExecutionPlan,
|
|
14
14
|
} from '@lota-sdk/shared'
|
|
15
15
|
import {
|
|
16
|
+
HUMAN_NODE_TYPES as HUMAN_NODE_TYPE_VALUES,
|
|
16
17
|
PlanEventSchema,
|
|
17
18
|
PlanNodeAttemptSchema,
|
|
18
19
|
PlanNodeRunSchema,
|
|
19
20
|
PlanRunSchema,
|
|
21
|
+
STRUCTURAL_NODE_TYPES as STRUCTURAL_NODE_TYPE_VALUES,
|
|
20
22
|
PlanValidationIssueSchema,
|
|
21
23
|
} from '@lota-sdk/shared'
|
|
22
24
|
import { RecordId } from 'surrealdb'
|
|
@@ -29,23 +31,24 @@ import type { DatabaseTransaction } from '../db/service'
|
|
|
29
31
|
import { TABLES } from '../db/tables'
|
|
30
32
|
import { toDatabaseDateTime } from '../utils/date-time'
|
|
31
33
|
import { isRecord } from '../utils/string'
|
|
32
|
-
import { feedbackLoopService } from './feedback-loop.service'
|
|
33
|
-
import { institutionalMemoryService } from './institutional-memory.service'
|
|
34
34
|
import { planApprovalService } from './plan-approval.service'
|
|
35
35
|
import { planArtifactService } from './plan-artifact.service'
|
|
36
36
|
import { planCheckpointService } from './plan-checkpoint.service'
|
|
37
|
+
import { runPlanCompletionSideEffectsSafely, runPlanNodeCompletionSideEffects } from './plan-completion-side-effects'
|
|
37
38
|
import { planCoordinationService } from './plan-coordination.service'
|
|
38
39
|
import { planEventDeliveryService } from './plan-event-delivery.service'
|
|
39
40
|
import { isExecutableConditionExpression, readPathValue } from './plan-helpers'
|
|
41
|
+
import { toPlanNodeValidationSpec } from './plan-node-spec'
|
|
42
|
+
import { buildExecutionPlanToolResult, toRunData } from './plan-run-data'
|
|
43
|
+
import type { PlanRunUpdate } from './plan-run-data'
|
|
40
44
|
import { planRunService } from './plan-run.service'
|
|
41
45
|
import { planSchedulerService } from './plan-scheduler.service'
|
|
42
46
|
import type { PlanValidationIssueInput } from './plan-validator.service'
|
|
43
47
|
import { planValidatorService } from './plan-validator.service'
|
|
44
|
-
import { qualityMetricsService } from './quality-metrics.service'
|
|
45
48
|
|
|
46
49
|
const SUCCESSFUL_TERMINAL_NODE_STATUSES = new Set(['completed', 'partial', 'skipped', 'scheduled', 'monitoring'])
|
|
47
|
-
const
|
|
48
|
-
const
|
|
50
|
+
const HUMAN_NODE_TYPE_SET = new Set<string>(HUMAN_NODE_TYPE_VALUES)
|
|
51
|
+
const STRUCTURAL_NODE_TYPE_SET = new Set<string>(STRUCTURAL_NODE_TYPE_VALUES)
|
|
49
52
|
|
|
50
53
|
function setPathValue(target: Record<string, unknown>, path: string, value: unknown) {
|
|
51
54
|
const segments = path
|
|
@@ -142,23 +145,11 @@ function isSuccessfulTerminalStatus(status: string): boolean {
|
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
function isHumanNodeType(type: string): boolean {
|
|
145
|
-
return
|
|
148
|
+
return HUMAN_NODE_TYPE_SET.has(type)
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
function isStructuralNodeType(type: string): boolean {
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
type PlanRunUpdate = Omit<
|
|
153
|
-
Partial<PlanRunRecord>,
|
|
154
|
-
'currentNodeId' | 'waitingNodeId' | 'replacedRunId' | 'lastCheckpointId' | 'startedAt' | 'completedAt'
|
|
155
|
-
> & {
|
|
156
|
-
currentNodeId?: string | null
|
|
157
|
-
waitingNodeId?: string | null
|
|
158
|
-
replacedRunId?: RecordIdInput | null
|
|
159
|
-
lastCheckpointId?: RecordIdInput | null
|
|
160
|
-
startedAt?: string | Date | null
|
|
161
|
-
completedAt?: string | Date | null
|
|
152
|
+
return STRUCTURAL_NODE_TYPE_SET.has(type)
|
|
162
153
|
}
|
|
163
154
|
|
|
164
155
|
type PlanNodeRunUpdate = Omit<
|
|
@@ -188,60 +179,6 @@ type PlanNodeRunUpdate = Omit<
|
|
|
188
179
|
completedAt?: string | Date | null
|
|
189
180
|
}
|
|
190
181
|
|
|
191
|
-
function toRunData(run: PlanRunRecord, patch: PlanRunUpdate) {
|
|
192
|
-
return {
|
|
193
|
-
planSpecId: ensureRecordId(run.planSpecId, TABLES.PLAN_SPEC),
|
|
194
|
-
organizationId: ensureRecordId(run.organizationId, TABLES.ORGANIZATION),
|
|
195
|
-
workstreamId: ensureRecordId(run.workstreamId, TABLES.WORKSTREAM),
|
|
196
|
-
leadAgentId: patch.leadAgentId ?? run.leadAgentId,
|
|
197
|
-
status: patch.status ?? run.status,
|
|
198
|
-
...(patch.currentNodeId === null
|
|
199
|
-
? {}
|
|
200
|
-
: patch.currentNodeId !== undefined
|
|
201
|
-
? { currentNodeId: patch.currentNodeId }
|
|
202
|
-
: run.currentNodeId
|
|
203
|
-
? { currentNodeId: run.currentNodeId }
|
|
204
|
-
: {}),
|
|
205
|
-
...(patch.waitingNodeId === null
|
|
206
|
-
? {}
|
|
207
|
-
: patch.waitingNodeId !== undefined
|
|
208
|
-
? { waitingNodeId: patch.waitingNodeId }
|
|
209
|
-
: run.waitingNodeId
|
|
210
|
-
? { waitingNodeId: run.waitingNodeId }
|
|
211
|
-
: {}),
|
|
212
|
-
readyNodeIds: patch.readyNodeIds ? [...patch.readyNodeIds] : [...run.readyNodeIds],
|
|
213
|
-
failureCount: patch.failureCount ?? run.failureCount,
|
|
214
|
-
...(patch.replacedRunId === null
|
|
215
|
-
? {}
|
|
216
|
-
: patch.replacedRunId
|
|
217
|
-
? { replacedRunId: ensureRecordId(patch.replacedRunId, TABLES.PLAN_RUN) }
|
|
218
|
-
: run.replacedRunId
|
|
219
|
-
? { replacedRunId: ensureRecordId(run.replacedRunId, TABLES.PLAN_RUN) }
|
|
220
|
-
: {}),
|
|
221
|
-
...(patch.lastCheckpointId === null
|
|
222
|
-
? {}
|
|
223
|
-
: patch.lastCheckpointId
|
|
224
|
-
? { lastCheckpointId: ensureRecordId(patch.lastCheckpointId, TABLES.PLAN_CHECKPOINT) }
|
|
225
|
-
: run.lastCheckpointId
|
|
226
|
-
? { lastCheckpointId: ensureRecordId(run.lastCheckpointId, TABLES.PLAN_CHECKPOINT) }
|
|
227
|
-
: {}),
|
|
228
|
-
...(patch.startedAt === null
|
|
229
|
-
? {}
|
|
230
|
-
: patch.startedAt !== undefined
|
|
231
|
-
? { startedAt: toDatabaseDateTime(patch.startedAt) }
|
|
232
|
-
: run.startedAt
|
|
233
|
-
? { startedAt: toDatabaseDateTime(run.startedAt) }
|
|
234
|
-
: {}),
|
|
235
|
-
...(patch.completedAt === null
|
|
236
|
-
? {}
|
|
237
|
-
: patch.completedAt !== undefined
|
|
238
|
-
? { completedAt: toDatabaseDateTime(patch.completedAt) }
|
|
239
|
-
: run.completedAt
|
|
240
|
-
? { completedAt: toDatabaseDateTime(run.completedAt) }
|
|
241
|
-
: {}),
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
182
|
function toNodeRunData(nodeRun: PlanNodeRunRecord, patch: PlanNodeRunUpdate) {
|
|
246
183
|
return {
|
|
247
184
|
runId: ensureRecordId(nodeRun.runId, TABLES.PLAN_RUN),
|
|
@@ -330,22 +267,6 @@ function toNodeRunData(nodeRun: PlanNodeRunRecord, patch: PlanNodeRunUpdate) {
|
|
|
330
267
|
}
|
|
331
268
|
}
|
|
332
269
|
|
|
333
|
-
function buildToolResult(params: {
|
|
334
|
-
action: ExecutionPlanToolResultData['action']
|
|
335
|
-
plan: SerializableExecutionPlan | null
|
|
336
|
-
message: string
|
|
337
|
-
changedNodeId?: string
|
|
338
|
-
}): ExecutionPlanToolResultData {
|
|
339
|
-
return {
|
|
340
|
-
action: params.action,
|
|
341
|
-
message: params.message,
|
|
342
|
-
...(params.changedNodeId ? { changedNodeId: params.changedNodeId } : {}),
|
|
343
|
-
...(params.plan ? { plan: params.plan } : {}),
|
|
344
|
-
hasPlan: params.plan !== null,
|
|
345
|
-
status: params.plan?.status,
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
270
|
function deriveApprovalStatus(response: Record<string, unknown>): 'approved' | 'rejected' | 'changes-requested' {
|
|
350
271
|
const approved = response.approved === true
|
|
351
272
|
const requiredEdits = Array.isArray(response.requiredEdits)
|
|
@@ -396,30 +317,7 @@ class PlanExecutorService {
|
|
|
396
317
|
const latestCheckpoint = await planRunService.getLatestCheckpoint(run.id)
|
|
397
318
|
const validation = planValidatorService.validateNodeResult({
|
|
398
319
|
draft: { schemas: spec.schemaRegistry },
|
|
399
|
-
node:
|
|
400
|
-
id: nodeSpec.nodeId,
|
|
401
|
-
type: nodeSpec.type,
|
|
402
|
-
label: nodeSpec.label,
|
|
403
|
-
owner: nodeSpec.owner,
|
|
404
|
-
objective: nodeSpec.objective,
|
|
405
|
-
instructions: nodeSpec.instructions,
|
|
406
|
-
inputSchemaRef: nodeSpec.inputSchemaRef,
|
|
407
|
-
outputSchemaRef: nodeSpec.outputSchemaRef,
|
|
408
|
-
deliverables: [...nodeSpec.deliverables],
|
|
409
|
-
successCriteria: [...nodeSpec.successCriteria],
|
|
410
|
-
completionChecks: [...nodeSpec.completionChecks],
|
|
411
|
-
retryPolicy: { ...nodeSpec.retryPolicy, retryOn: [...nodeSpec.retryPolicy.retryOn] },
|
|
412
|
-
failurePolicy: [...nodeSpec.failurePolicy],
|
|
413
|
-
timeoutMs: nodeSpec.timeoutMs,
|
|
414
|
-
toolPolicy: { allow: [...nodeSpec.toolPolicy.allow], deny: [...nodeSpec.toolPolicy.deny] },
|
|
415
|
-
contextPolicy: {
|
|
416
|
-
retrievalScopes: [...nodeSpec.contextPolicy.retrievalScopes],
|
|
417
|
-
attachmentPolicy: nodeSpec.contextPolicy.attachmentPolicy,
|
|
418
|
-
webPolicy: nodeSpec.contextPolicy.webPolicy,
|
|
419
|
-
},
|
|
420
|
-
executionVisibility: nodeSpec.executionVisibility,
|
|
421
|
-
...(nodeSpec.escalation ? { escalation: nodeSpec.escalation } : {}),
|
|
422
|
-
},
|
|
320
|
+
node: toPlanNodeValidationSpec(nodeSpec),
|
|
423
321
|
result: params.result,
|
|
424
322
|
})
|
|
425
323
|
const emittedEvents: PlanEventRecord[] = []
|
|
@@ -557,6 +455,7 @@ class PlanExecutorService {
|
|
|
557
455
|
const checkpoint = await this.saveCheckpoint({
|
|
558
456
|
tx,
|
|
559
457
|
run: synced.run,
|
|
458
|
+
spec,
|
|
560
459
|
nodeRuns: synced.nodeRuns,
|
|
561
460
|
artifacts: synced.artifacts,
|
|
562
461
|
sequence: (latestCheckpoint?.sequence ?? 0) + 1,
|
|
@@ -627,6 +526,7 @@ class PlanExecutorService {
|
|
|
627
526
|
const checkpoint = await this.saveCheckpoint({
|
|
628
527
|
tx,
|
|
629
528
|
run: failedRun,
|
|
529
|
+
spec,
|
|
630
530
|
nodeRuns: withUpdatedNodeRuns.map((candidate) =>
|
|
631
531
|
candidate.nodeId === nextNodeRun.nodeId ? nextNodeRun : candidate,
|
|
632
532
|
),
|
|
@@ -681,6 +581,7 @@ class PlanExecutorService {
|
|
|
681
581
|
const checkpoint = await this.saveCheckpoint({
|
|
682
582
|
tx,
|
|
683
583
|
run: blockedRun,
|
|
584
|
+
spec,
|
|
684
585
|
nodeRuns: withUpdatedNodeRuns.map((candidate) =>
|
|
685
586
|
candidate.nodeId === nextNodeRun.nodeId ? nextNodeRun : candidate,
|
|
686
587
|
),
|
|
@@ -736,6 +637,7 @@ class PlanExecutorService {
|
|
|
736
637
|
const checkpoint = await this.saveCheckpoint({
|
|
737
638
|
tx,
|
|
738
639
|
run: failedRun,
|
|
640
|
+
spec,
|
|
739
641
|
nodeRuns: withUpdatedNodeRuns.map((candidate) =>
|
|
740
642
|
candidate.nodeId === nextNodeRun.nodeId ? nextNodeRun : candidate,
|
|
741
643
|
),
|
|
@@ -801,6 +703,7 @@ class PlanExecutorService {
|
|
|
801
703
|
const checkpoint = await this.saveCheckpoint({
|
|
802
704
|
tx,
|
|
803
705
|
run: synced.run,
|
|
706
|
+
spec,
|
|
804
707
|
nodeRuns: synced.nodeRuns,
|
|
805
708
|
artifacts: synced.artifacts,
|
|
806
709
|
sequence: (latestCheckpoint?.sequence ?? 0) + 1,
|
|
@@ -811,61 +714,27 @@ class PlanExecutorService {
|
|
|
811
714
|
await this.attachCheckpoint(tx, synced.run, checkpoint)
|
|
812
715
|
})
|
|
813
716
|
|
|
814
|
-
// Record node-level quality metrics (fire-and-forget)
|
|
815
717
|
const orgId = recordIdToString(run.organizationId, TABLES.ORGANIZATION)
|
|
816
718
|
const runIdStr = recordIdToString(run.id, TABLES.PLAN_RUN)
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
.
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
},
|
|
833
|
-
})
|
|
834
|
-
.catch((error) => {
|
|
835
|
-
aiLogger.warn`Failed to record node quality metrics for run ${runIdStr} node ${params.nodeId}: ${error instanceof Error ? error.message : String(error)}`
|
|
836
|
-
})
|
|
719
|
+
void runPlanNodeCompletionSideEffects({
|
|
720
|
+
runId: runIdStr,
|
|
721
|
+
organizationId: orgId,
|
|
722
|
+
nodeId: params.nodeId,
|
|
723
|
+
nodeLabel: nodeSpec.label,
|
|
724
|
+
nodeOwnerRef: nodeSpec.owner.ref,
|
|
725
|
+
nodeOwnerType: nodeSpec.owner.executorType,
|
|
726
|
+
nodeType: nodeSpec.type,
|
|
727
|
+
nodeStartedAt: nodeRun.startedAt,
|
|
728
|
+
nodeAttemptCount: nodeRun.attemptCount + 1,
|
|
729
|
+
artifactCount: params.result.artifacts.length,
|
|
730
|
+
validationIssues: [...validation.blocking, ...validation.warnings],
|
|
731
|
+
}).catch((error) => {
|
|
732
|
+
aiLogger.warn`Failed to record node completion metrics for run ${runIdStr} node ${params.nodeId}: ${error instanceof Error ? error.message : String(error)}`
|
|
733
|
+
})
|
|
837
734
|
|
|
838
|
-
// If the run just completed, record cycle metrics, persist feedback recommendations, and extract patterns
|
|
839
735
|
const updatedRun = await planRunService.getRunById(run.id)
|
|
840
736
|
if (updatedRun.status === 'completed') {
|
|
841
|
-
|
|
842
|
-
aiLogger.warn`Failed to record cycle quality metrics for run ${runIdStr}: ${error instanceof Error ? error.message : String(error)}`
|
|
843
|
-
})
|
|
844
|
-
feedbackLoopService
|
|
845
|
-
.analyzeOutcomes({ runId: runIdStr, organizationId: orgId })
|
|
846
|
-
.then(async (recommendations) => {
|
|
847
|
-
if (recommendations.length === 0) return
|
|
848
|
-
const specRecord = await planRunService.getPlanSpecById(updatedRun.planSpecId)
|
|
849
|
-
const event = await databaseService.create(
|
|
850
|
-
TABLES.PLAN_EVENT,
|
|
851
|
-
{
|
|
852
|
-
planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
|
|
853
|
-
runId: ensureRecordId(updatedRun.id, TABLES.PLAN_RUN),
|
|
854
|
-
eventType: 'feedback-analyzed',
|
|
855
|
-
message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
|
|
856
|
-
detail: { recommendations },
|
|
857
|
-
emittedBy: 'system',
|
|
858
|
-
},
|
|
859
|
-
PlanEventSchema,
|
|
860
|
-
)
|
|
861
|
-
await planEventDeliveryService.dispatchEvent(event)
|
|
862
|
-
})
|
|
863
|
-
.catch((error) => {
|
|
864
|
-
aiLogger.warn`Failed to analyze feedback outcomes for run ${runIdStr}: ${error instanceof Error ? error.message : String(error)}`
|
|
865
|
-
})
|
|
866
|
-
institutionalMemoryService.extractPatterns({ organizationId: orgId, runId: runIdStr }).catch((error) => {
|
|
867
|
-
aiLogger.warn`Failed to extract institutional memory patterns for run ${runIdStr}: ${error instanceof Error ? error.message : String(error)}`
|
|
868
|
-
})
|
|
737
|
+
void runPlanCompletionSideEffectsSafely({ runId: runIdStr, organizationId: orgId })
|
|
869
738
|
}
|
|
870
739
|
|
|
871
740
|
await planEventDeliveryService.dispatchEvents(emittedEvents)
|
|
@@ -878,7 +747,7 @@ class PlanExecutorService {
|
|
|
878
747
|
includeValidationIssues: true,
|
|
879
748
|
})
|
|
880
749
|
|
|
881
|
-
return
|
|
750
|
+
return buildExecutionPlanToolResult({
|
|
882
751
|
action: 'node-result-submitted',
|
|
883
752
|
plan: snapshot,
|
|
884
753
|
message: `Submitted result for node "${nodeSpec.label}".`,
|
|
@@ -917,30 +786,7 @@ class PlanExecutorService {
|
|
|
917
786
|
const latestCheckpoint = await planRunService.getLatestCheckpoint(run.id)
|
|
918
787
|
const validation = planValidatorService.validateNodeResult({
|
|
919
788
|
draft: { schemas: spec.schemaRegistry },
|
|
920
|
-
node:
|
|
921
|
-
id: nodeSpec.nodeId,
|
|
922
|
-
type: nodeSpec.type,
|
|
923
|
-
label: nodeSpec.label,
|
|
924
|
-
owner: nodeSpec.owner,
|
|
925
|
-
objective: nodeSpec.objective,
|
|
926
|
-
instructions: nodeSpec.instructions,
|
|
927
|
-
inputSchemaRef: nodeSpec.inputSchemaRef,
|
|
928
|
-
outputSchemaRef: nodeSpec.outputSchemaRef,
|
|
929
|
-
deliverables: [...nodeSpec.deliverables],
|
|
930
|
-
successCriteria: [...nodeSpec.successCriteria],
|
|
931
|
-
completionChecks: [...nodeSpec.completionChecks],
|
|
932
|
-
retryPolicy: { ...nodeSpec.retryPolicy, retryOn: [...nodeSpec.retryPolicy.retryOn] },
|
|
933
|
-
failurePolicy: [...nodeSpec.failurePolicy],
|
|
934
|
-
timeoutMs: nodeSpec.timeoutMs,
|
|
935
|
-
toolPolicy: { allow: [...nodeSpec.toolPolicy.allow], deny: [...nodeSpec.toolPolicy.deny] },
|
|
936
|
-
contextPolicy: {
|
|
937
|
-
retrievalScopes: [...nodeSpec.contextPolicy.retrievalScopes],
|
|
938
|
-
attachmentPolicy: nodeSpec.contextPolicy.attachmentPolicy,
|
|
939
|
-
webPolicy: nodeSpec.contextPolicy.webPolicy,
|
|
940
|
-
},
|
|
941
|
-
executionVisibility: nodeSpec.executionVisibility,
|
|
942
|
-
...(nodeSpec.escalation ? { escalation: nodeSpec.escalation } : {}),
|
|
943
|
-
},
|
|
789
|
+
node: toPlanNodeValidationSpec(nodeSpec),
|
|
944
790
|
result: {
|
|
945
791
|
structuredOutput: params.response,
|
|
946
792
|
artifacts: [],
|
|
@@ -1072,6 +918,7 @@ class PlanExecutorService {
|
|
|
1072
918
|
const checkpoint = await this.saveCheckpoint({
|
|
1073
919
|
tx,
|
|
1074
920
|
run: blockedRun,
|
|
921
|
+
spec,
|
|
1075
922
|
nodeRuns,
|
|
1076
923
|
artifacts: existingArtifacts,
|
|
1077
924
|
sequence: (latestCheckpoint?.sequence ?? 0) + 1,
|
|
@@ -1112,6 +959,7 @@ class PlanExecutorService {
|
|
|
1112
959
|
const checkpoint = await this.saveCheckpoint({
|
|
1113
960
|
tx,
|
|
1114
961
|
run: synced.run,
|
|
962
|
+
spec,
|
|
1115
963
|
nodeRuns: synced.nodeRuns,
|
|
1116
964
|
artifacts: synced.artifacts,
|
|
1117
965
|
sequence: (latestCheckpoint?.sequence ?? 0) + 1,
|
|
@@ -1210,6 +1058,7 @@ class PlanExecutorService {
|
|
|
1210
1058
|
const checkpoint = await this.saveCheckpoint({
|
|
1211
1059
|
tx,
|
|
1212
1060
|
run: synced.run,
|
|
1061
|
+
spec,
|
|
1213
1062
|
nodeRuns: synced.nodeRuns,
|
|
1214
1063
|
artifacts: synced.artifacts,
|
|
1215
1064
|
sequence: (latestCheckpoint?.sequence ?? 0) + 1,
|
|
@@ -1229,7 +1078,7 @@ class PlanExecutorService {
|
|
|
1229
1078
|
includeValidationIssues: true,
|
|
1230
1079
|
})
|
|
1231
1080
|
|
|
1232
|
-
return
|
|
1081
|
+
return buildExecutionPlanToolResult({
|
|
1233
1082
|
action: 'run-resumed',
|
|
1234
1083
|
plan: snapshot,
|
|
1235
1084
|
message: `Resumed execution run "${snapshot.title}".`,
|
|
@@ -1325,6 +1174,7 @@ class PlanExecutorService {
|
|
|
1325
1174
|
const checkpoint = await this.saveCheckpoint({
|
|
1326
1175
|
tx,
|
|
1327
1176
|
run: blockedRun,
|
|
1177
|
+
spec,
|
|
1328
1178
|
nodeRuns: (await planRunService.listNodeRuns(run.id)).map((candidate) =>
|
|
1329
1179
|
candidate.nodeId === blockedNodeRun.nodeId ? blockedNodeRun : candidate,
|
|
1330
1180
|
),
|
|
@@ -1405,6 +1255,7 @@ class PlanExecutorService {
|
|
|
1405
1255
|
const checkpoint = await this.saveCheckpoint({
|
|
1406
1256
|
tx,
|
|
1407
1257
|
run: synced.run,
|
|
1258
|
+
spec,
|
|
1408
1259
|
nodeRuns: synced.nodeRuns,
|
|
1409
1260
|
artifacts: synced.artifacts,
|
|
1410
1261
|
sequence: (latestCheckpoint?.sequence ?? 0) + 1,
|
|
@@ -1996,6 +1847,7 @@ class PlanExecutorService {
|
|
|
1996
1847
|
private async saveCheckpoint(params: {
|
|
1997
1848
|
tx: DatabaseTransaction
|
|
1998
1849
|
run: PlanRunRecord
|
|
1850
|
+
spec: PlanSpecRecord
|
|
1999
1851
|
nodeRuns: PlanNodeRunRecord[]
|
|
2000
1852
|
artifacts: Array<{ id: RecordIdInput; nodeId: string }>
|
|
2001
1853
|
sequence: number
|
|
@@ -2028,7 +1880,7 @@ class PlanExecutorService {
|
|
|
2028
1880
|
await this.emitEvent({
|
|
2029
1881
|
tx: params.tx,
|
|
2030
1882
|
run: params.run,
|
|
2031
|
-
spec:
|
|
1883
|
+
spec: params.spec,
|
|
2032
1884
|
eventType: 'checkpoint-saved',
|
|
2033
1885
|
message: `Saved checkpoint ${checkpoint.sequence}.`,
|
|
2034
1886
|
detail: { checkpointId: recordIdToString(checkpoint.id, TABLES.PLAN_CHECKPOINT), reason: params.reason },
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { PlanNodeSpec, PlanNodeSpecRecord } from '@lota-sdk/shared'
|
|
2
|
+
|
|
3
|
+
export type PlanNodeValidationSpec = Pick<
|
|
4
|
+
PlanNodeSpec,
|
|
5
|
+
| 'id'
|
|
6
|
+
| 'type'
|
|
7
|
+
| 'label'
|
|
8
|
+
| 'owner'
|
|
9
|
+
| 'objective'
|
|
10
|
+
| 'instructions'
|
|
11
|
+
| 'inputSchemaRef'
|
|
12
|
+
| 'outputSchemaRef'
|
|
13
|
+
| 'deliverables'
|
|
14
|
+
| 'successCriteria'
|
|
15
|
+
| 'completionChecks'
|
|
16
|
+
| 'retryPolicy'
|
|
17
|
+
| 'failurePolicy'
|
|
18
|
+
| 'timeoutMs'
|
|
19
|
+
| 'toolPolicy'
|
|
20
|
+
| 'contextPolicy'
|
|
21
|
+
| 'executionVisibility'
|
|
22
|
+
| 'schedule'
|
|
23
|
+
| 'deadline'
|
|
24
|
+
| 'escalation'
|
|
25
|
+
| 'monitoringConfig'
|
|
26
|
+
| 'delayAfterPredecessorMs'
|
|
27
|
+
| 'deliberationConfig'
|
|
28
|
+
>
|
|
29
|
+
|
|
30
|
+
export function toPlanNodeValidationSpec(nodeSpec: PlanNodeSpecRecord): PlanNodeValidationSpec {
|
|
31
|
+
return {
|
|
32
|
+
id: nodeSpec.nodeId,
|
|
33
|
+
type: nodeSpec.type,
|
|
34
|
+
label: nodeSpec.label,
|
|
35
|
+
owner: nodeSpec.owner,
|
|
36
|
+
objective: nodeSpec.objective,
|
|
37
|
+
instructions: nodeSpec.instructions,
|
|
38
|
+
inputSchemaRef: nodeSpec.inputSchemaRef,
|
|
39
|
+
outputSchemaRef: nodeSpec.outputSchemaRef,
|
|
40
|
+
deliverables: [...nodeSpec.deliverables],
|
|
41
|
+
successCriteria: [...nodeSpec.successCriteria],
|
|
42
|
+
completionChecks: [...nodeSpec.completionChecks],
|
|
43
|
+
retryPolicy: { ...nodeSpec.retryPolicy, retryOn: [...nodeSpec.retryPolicy.retryOn] },
|
|
44
|
+
failurePolicy: [...nodeSpec.failurePolicy],
|
|
45
|
+
timeoutMs: nodeSpec.timeoutMs,
|
|
46
|
+
toolPolicy: { allow: [...nodeSpec.toolPolicy.allow], deny: [...nodeSpec.toolPolicy.deny] },
|
|
47
|
+
contextPolicy: {
|
|
48
|
+
retrievalScopes: [...nodeSpec.contextPolicy.retrievalScopes],
|
|
49
|
+
attachmentPolicy: nodeSpec.contextPolicy.attachmentPolicy,
|
|
50
|
+
webPolicy: nodeSpec.contextPolicy.webPolicy,
|
|
51
|
+
},
|
|
52
|
+
executionVisibility: nodeSpec.executionVisibility,
|
|
53
|
+
...(nodeSpec.schedule ? { schedule: nodeSpec.schedule } : {}),
|
|
54
|
+
...(nodeSpec.deadline ? { deadline: nodeSpec.deadline } : {}),
|
|
55
|
+
...(nodeSpec.escalation ? { escalation: nodeSpec.escalation } : {}),
|
|
56
|
+
...(nodeSpec.monitoringConfig ? { monitoringConfig: nodeSpec.monitoringConfig } : {}),
|
|
57
|
+
...(nodeSpec.delayAfterPredecessorMs ? { delayAfterPredecessorMs: nodeSpec.delayAfterPredecessorMs } : {}),
|
|
58
|
+
...(nodeSpec.deliberationConfig ? { deliberationConfig: nodeSpec.deliberationConfig } : {}),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ExecutionPlanToolResultData, PlanRunRecord } from '@lota-sdk/shared'
|
|
2
|
+
|
|
3
|
+
import type { RecordIdInput } from '../db/record-id'
|
|
4
|
+
import { ensureRecordId } from '../db/record-id'
|
|
5
|
+
import { TABLES } from '../db/tables'
|
|
6
|
+
import { toDatabaseDateTime } from '../utils/date-time'
|
|
7
|
+
|
|
8
|
+
export type PlanRunUpdate = Omit<
|
|
9
|
+
Partial<PlanRunRecord>,
|
|
10
|
+
'currentNodeId' | 'waitingNodeId' | 'replacedRunId' | 'lastCheckpointId' | 'startedAt' | 'completedAt'
|
|
11
|
+
> & {
|
|
12
|
+
currentNodeId?: string | null
|
|
13
|
+
waitingNodeId?: string | null
|
|
14
|
+
replacedRunId?: RecordIdInput | null
|
|
15
|
+
lastCheckpointId?: RecordIdInput | null
|
|
16
|
+
startedAt?: string | Date | null
|
|
17
|
+
completedAt?: string | Date | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function toRunData(run: PlanRunRecord, patch: PlanRunUpdate) {
|
|
21
|
+
return {
|
|
22
|
+
planSpecId: ensureRecordId(run.planSpecId, TABLES.PLAN_SPEC),
|
|
23
|
+
organizationId: ensureRecordId(run.organizationId, TABLES.ORGANIZATION),
|
|
24
|
+
workstreamId: ensureRecordId(run.workstreamId, TABLES.WORKSTREAM),
|
|
25
|
+
leadAgentId: patch.leadAgentId ?? run.leadAgentId,
|
|
26
|
+
status: patch.status ?? run.status,
|
|
27
|
+
...(patch.currentNodeId === null
|
|
28
|
+
? {}
|
|
29
|
+
: patch.currentNodeId !== undefined
|
|
30
|
+
? { currentNodeId: patch.currentNodeId }
|
|
31
|
+
: run.currentNodeId
|
|
32
|
+
? { currentNodeId: run.currentNodeId }
|
|
33
|
+
: {}),
|
|
34
|
+
...(patch.waitingNodeId === null
|
|
35
|
+
? {}
|
|
36
|
+
: patch.waitingNodeId !== undefined
|
|
37
|
+
? { waitingNodeId: patch.waitingNodeId }
|
|
38
|
+
: run.waitingNodeId
|
|
39
|
+
? { waitingNodeId: run.waitingNodeId }
|
|
40
|
+
: {}),
|
|
41
|
+
readyNodeIds: patch.readyNodeIds ? [...patch.readyNodeIds] : [...run.readyNodeIds],
|
|
42
|
+
failureCount: patch.failureCount ?? run.failureCount,
|
|
43
|
+
...(patch.replacedRunId === null
|
|
44
|
+
? {}
|
|
45
|
+
: patch.replacedRunId
|
|
46
|
+
? { replacedRunId: ensureRecordId(patch.replacedRunId, TABLES.PLAN_RUN) }
|
|
47
|
+
: run.replacedRunId
|
|
48
|
+
? { replacedRunId: ensureRecordId(run.replacedRunId, TABLES.PLAN_RUN) }
|
|
49
|
+
: {}),
|
|
50
|
+
...(patch.lastCheckpointId === null
|
|
51
|
+
? {}
|
|
52
|
+
: patch.lastCheckpointId
|
|
53
|
+
? { lastCheckpointId: ensureRecordId(patch.lastCheckpointId, TABLES.PLAN_CHECKPOINT) }
|
|
54
|
+
: run.lastCheckpointId
|
|
55
|
+
? { lastCheckpointId: ensureRecordId(run.lastCheckpointId, TABLES.PLAN_CHECKPOINT) }
|
|
56
|
+
: {}),
|
|
57
|
+
...(patch.startedAt === null
|
|
58
|
+
? {}
|
|
59
|
+
: patch.startedAt !== undefined
|
|
60
|
+
? { startedAt: toDatabaseDateTime(patch.startedAt) }
|
|
61
|
+
: run.startedAt
|
|
62
|
+
? { startedAt: toDatabaseDateTime(run.startedAt) }
|
|
63
|
+
: {}),
|
|
64
|
+
...(patch.completedAt === null
|
|
65
|
+
? {}
|
|
66
|
+
: patch.completedAt !== undefined
|
|
67
|
+
? { completedAt: toDatabaseDateTime(patch.completedAt) }
|
|
68
|
+
: run.completedAt
|
|
69
|
+
? { completedAt: toDatabaseDateTime(run.completedAt) }
|
|
70
|
+
: {}),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function buildExecutionPlanToolResult(params: {
|
|
75
|
+
action: ExecutionPlanToolResultData['action']
|
|
76
|
+
plan: ExecutionPlanToolResultData['plan'] | null
|
|
77
|
+
message: string
|
|
78
|
+
changedNodeId?: string
|
|
79
|
+
}): ExecutionPlanToolResultData {
|
|
80
|
+
return {
|
|
81
|
+
action: params.action,
|
|
82
|
+
message: params.message,
|
|
83
|
+
...(params.changedNodeId ? { changedNodeId: params.changedNodeId } : {}),
|
|
84
|
+
...(params.plan ? { plan: params.plan } : {}),
|
|
85
|
+
hasPlan: params.plan !== null,
|
|
86
|
+
status: params.plan?.status,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -3,17 +3,19 @@ import type {
|
|
|
3
3
|
PlanDataSchema,
|
|
4
4
|
PlanDraft,
|
|
5
5
|
PlanFailureClass,
|
|
6
|
+
PlanNodeType,
|
|
6
7
|
PlanNodeResultSubmission,
|
|
7
|
-
PlanNodeSpec,
|
|
8
8
|
PlanValidationIssueSeverity,
|
|
9
9
|
} from '@lota-sdk/shared'
|
|
10
|
+
import { HUMAN_NODE_TYPES, STRUCTURAL_NODE_TYPES } from '@lota-sdk/shared'
|
|
10
11
|
|
|
11
12
|
import { isRecord } from '../utils/string'
|
|
12
13
|
import { planCoordinationService } from './plan-coordination.service'
|
|
13
14
|
import { readPathValue } from './plan-helpers'
|
|
15
|
+
import type { PlanNodeValidationSpec } from './plan-node-spec'
|
|
14
16
|
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
+
const STRUCTURAL_NODE_TYPE_SET = new Set<PlanNodeType>(STRUCTURAL_NODE_TYPES)
|
|
18
|
+
const HUMAN_NODE_TYPE_SET = new Set<PlanNodeType>(HUMAN_NODE_TYPES)
|
|
17
19
|
|
|
18
20
|
export interface PlanValidationIssueInput {
|
|
19
21
|
severity: PlanValidationIssueSeverity
|
|
@@ -186,7 +188,7 @@ class PlanValidatorService {
|
|
|
186
188
|
}
|
|
187
189
|
nodeIds.add(node.id)
|
|
188
190
|
|
|
189
|
-
const isStructuralNode =
|
|
191
|
+
const isStructuralNode = STRUCTURAL_NODE_TYPE_SET.has(node.type)
|
|
190
192
|
if (!isStructuralNode && node.deliverables.length === 0) {
|
|
191
193
|
blocking.push(
|
|
192
194
|
createIssue({
|
|
@@ -214,7 +216,7 @@ class PlanValidatorService {
|
|
|
214
216
|
}),
|
|
215
217
|
)
|
|
216
218
|
}
|
|
217
|
-
if (
|
|
219
|
+
if (HUMAN_NODE_TYPE_SET.has(node.type) && node.owner.executorType !== 'user') {
|
|
218
220
|
blocking.push(
|
|
219
221
|
createIssue({
|
|
220
222
|
code: 'human_node_owner_mismatch',
|
|
@@ -224,7 +226,7 @@ class PlanValidatorService {
|
|
|
224
226
|
)
|
|
225
227
|
}
|
|
226
228
|
if (
|
|
227
|
-
!
|
|
229
|
+
!HUMAN_NODE_TYPE_SET.has(node.type) &&
|
|
228
230
|
node.type !== 'join' &&
|
|
229
231
|
node.type !== 'switch' &&
|
|
230
232
|
node.owner.ref.trim().length === 0
|
|
@@ -518,7 +520,7 @@ class PlanValidatorService {
|
|
|
518
520
|
|
|
519
521
|
validateNodeResult(params: {
|
|
520
522
|
draft: Pick<PlanDraft, 'schemas'>
|
|
521
|
-
node:
|
|
523
|
+
node: PlanNodeValidationSpec
|
|
522
524
|
result: PlanNodeResultSubmission
|
|
523
525
|
}): NodeResultValidationResult {
|
|
524
526
|
const blocking: PlanValidationIssueInput[] = []
|
|
@@ -639,7 +641,7 @@ class PlanValidatorService {
|
|
|
639
641
|
|
|
640
642
|
private evaluateCompletionCheck(params: {
|
|
641
643
|
draft: Pick<PlanDraft, 'schemas'>
|
|
642
|
-
node:
|
|
644
|
+
node: PlanNodeValidationSpec
|
|
643
645
|
result: PlanNodeResultSubmission
|
|
644
646
|
check: PlanCompletionCheck
|
|
645
647
|
}): PlanValidationIssueInput | null {
|
|
@@ -35,7 +35,7 @@ class WorkstreamTitleService {
|
|
|
35
35
|
title = limitTitleWords(deriveTitle(sourceText || WORKSTREAM.DEFAULT_TITLE))
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
await workstreamService.
|
|
38
|
+
await workstreamService.update(workstreamId, { title, nameGenerated: true })
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|