@chllming/wave-orchestration 0.8.3 → 0.8.5
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/CHANGELOG.md +36 -1
- package/README.md +60 -11
- package/docs/README.md +8 -2
- package/docs/agents/wave-design-role.md +47 -0
- package/docs/concepts/what-is-a-wave.md +11 -7
- package/docs/guides/author-and-run-waves.md +24 -0
- package/docs/guides/planner.md +44 -0
- package/docs/plans/architecture-hardening-migration.md +8 -1
- package/docs/plans/current-state.md +19 -7
- package/docs/plans/end-state-architecture.md +88 -70
- package/docs/plans/examples/wave-example-design-handoff.md +262 -0
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +370 -64
- package/docs/plans/wave-orchestrator.md +49 -13
- package/docs/reference/cli-reference.md +46 -14
- package/docs/reference/coordination-and-closure.md +19 -6
- package/docs/reference/npmjs-trusted-publishing.md +5 -4
- package/docs/reference/sample-waves.md +14 -7
- package/docs/reference/skills.md +10 -0
- package/package.json +1 -1
- package/releases/manifest.json +39 -0
- package/scripts/wave-orchestrator/agent-state.mjs +64 -491
- package/scripts/wave-orchestrator/autonomous.mjs +10 -6
- package/scripts/wave-orchestrator/{launcher-closure.mjs → closure-engine.mjs} +190 -74
- package/scripts/wave-orchestrator/config.mjs +5 -0
- package/scripts/wave-orchestrator/coordination.mjs +42 -1
- package/scripts/wave-orchestrator/{launcher-derived-state.mjs → derived-state-engine.mjs} +34 -146
- package/scripts/wave-orchestrator/{launcher-gates.mjs → gate-engine.mjs} +501 -141
- package/scripts/wave-orchestrator/human-input-resolution.mjs +14 -10
- package/scripts/wave-orchestrator/human-input-workflow.mjs +104 -0
- package/scripts/wave-orchestrator/implementation-engine.mjs +120 -0
- package/scripts/wave-orchestrator/install.mjs +3 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +11 -6
- package/scripts/wave-orchestrator/launcher.mjs +324 -723
- package/scripts/wave-orchestrator/ledger.mjs +56 -27
- package/scripts/wave-orchestrator/local-executor.mjs +37 -0
- package/scripts/wave-orchestrator/planner.mjs +24 -4
- package/scripts/wave-orchestrator/projection-writer.mjs +256 -0
- package/scripts/wave-orchestrator/reconcile-format.mjs +32 -0
- package/scripts/wave-orchestrator/reducer-snapshot.mjs +297 -0
- package/scripts/wave-orchestrator/replay.mjs +3 -1
- package/scripts/wave-orchestrator/result-envelope.mjs +620 -0
- package/scripts/wave-orchestrator/retry-control.mjs +22 -2
- package/scripts/wave-orchestrator/{launcher-retry.mjs → retry-engine.mjs} +352 -18
- package/scripts/wave-orchestrator/role-helpers.mjs +124 -1
- package/scripts/wave-orchestrator/{launcher-supervisor.mjs → session-supervisor.mjs} +178 -103
- package/scripts/wave-orchestrator/shared.mjs +2 -0
- package/scripts/wave-orchestrator/skills.mjs +1 -0
- package/scripts/wave-orchestrator/task-entity.mjs +65 -45
- package/scripts/wave-orchestrator/traces.mjs +10 -1
- package/scripts/wave-orchestrator/wave-files.mjs +96 -10
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +76 -12
- package/skills/README.md +7 -0
- package/skills/role-design/SKILL.md +50 -0
- package/skills/role-design/skill.json +36 -0
- package/skills/tui-design/SKILL.md +77 -0
- package/skills/tui-design/references/tui-design.md +259 -0
- package/skills/tui-design/skill.json +36 -0
- package/wave.config.json +15 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
readClarificationBarrier,
|
|
9
9
|
readWaveAssignmentBarrier,
|
|
10
10
|
readWaveDependencyBarrier,
|
|
11
|
-
} from "./
|
|
11
|
+
} from "./gate-engine.mjs";
|
|
12
12
|
import {
|
|
13
13
|
isOpenCoordinationStatus,
|
|
14
14
|
openClarificationLinkedRequests,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from "./shared.mjs";
|
|
21
21
|
import {
|
|
22
22
|
readAgentExecutionSummary,
|
|
23
|
+
validateDesignSummary,
|
|
23
24
|
validateImplementationSummary,
|
|
24
25
|
} from "./agent-state.mjs";
|
|
25
26
|
import {
|
|
@@ -32,7 +33,11 @@ import {
|
|
|
32
33
|
} from "./proof-registry.mjs";
|
|
33
34
|
import { hashAgentPromptFingerprint } from "./context7.mjs";
|
|
34
35
|
import {
|
|
36
|
+
isDocsOnlyDesignAgent,
|
|
37
|
+
isDesignAgent,
|
|
38
|
+
isImplementationOwningDesignAgent,
|
|
35
39
|
isSecurityReviewAgent,
|
|
40
|
+
resolveWaveRoleBindings,
|
|
36
41
|
} from "./role-helpers.mjs";
|
|
37
42
|
import {
|
|
38
43
|
commandForExecutor,
|
|
@@ -121,6 +126,7 @@ export function persistedRelaunchPlanMatchesCurrentState(
|
|
|
121
126
|
}
|
|
122
127
|
const componentGate = readWaveComponentGate(waveDefinition, agentRuns, {
|
|
123
128
|
laneProfile: lanePaths?.laneProfile,
|
|
129
|
+
mode: "live",
|
|
124
130
|
});
|
|
125
131
|
if (componentGate?.statusCode !== "shared-component-sibling-pending") {
|
|
126
132
|
return true;
|
|
@@ -271,7 +277,10 @@ export function reconcileFailuresAgainstSharedComponentState(wave, agentRuns, fa
|
|
|
271
277
|
return failures;
|
|
272
278
|
}
|
|
273
279
|
const summariesByAgentId = Object.fromEntries(
|
|
274
|
-
(agentRuns || []).map((runInfo) => [
|
|
280
|
+
(agentRuns || []).map((runInfo) => [
|
|
281
|
+
runInfo.agent.agentId,
|
|
282
|
+
readRunExecutionSummary(runInfo, wave, { mode: "live" }),
|
|
283
|
+
]),
|
|
275
284
|
);
|
|
276
285
|
const failureAgentIds = new Set(failures.map((failure) => failure.agentId).filter(Boolean));
|
|
277
286
|
const consumedSatisfiedAgentIds = new Set();
|
|
@@ -310,6 +319,28 @@ export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
|
310
319
|
if (!basicReuseOk) {
|
|
311
320
|
return false;
|
|
312
321
|
}
|
|
322
|
+
if (isDocsOnlyDesignAgent(agent) || isImplementationOwningDesignAgent(agent)) {
|
|
323
|
+
const summary = readAgentExecutionSummary(statusPath, {
|
|
324
|
+
agent,
|
|
325
|
+
statusPath,
|
|
326
|
+
statusRecord,
|
|
327
|
+
logPath: options.logPath || null,
|
|
328
|
+
reportPath: options.reportPath || null,
|
|
329
|
+
});
|
|
330
|
+
if (!summary) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
const effectiveSummary = options.proofRegistry
|
|
334
|
+
? augmentSummaryWithProofRegistry(agent, summary, options.proofRegistry)
|
|
335
|
+
: summary;
|
|
336
|
+
if (isDocsOnlyDesignAgent(agent)) {
|
|
337
|
+
return validateDesignSummary(agent, effectiveSummary).ok;
|
|
338
|
+
}
|
|
339
|
+
return (
|
|
340
|
+
validateDesignSummary(agent, effectiveSummary).ok &&
|
|
341
|
+
validateImplementationSummary(agent, effectiveSummary).ok
|
|
342
|
+
);
|
|
343
|
+
}
|
|
313
344
|
const proofCentric =
|
|
314
345
|
agentRequiresProofCentricValidation(agent) || waveRequiresProofCentricValidation(options.wave);
|
|
315
346
|
if (!proofCentric) {
|
|
@@ -337,13 +368,43 @@ export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
|
337
368
|
return true;
|
|
338
369
|
}
|
|
339
370
|
|
|
340
|
-
function
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
371
|
+
function hasReusableDesignPassStatus(agent, statusPath, options = {}) {
|
|
372
|
+
if (!isDesignAgent(agent)) {
|
|
373
|
+
return hasReusableSuccessStatus(agent, statusPath, options);
|
|
374
|
+
}
|
|
375
|
+
const statusRecord = readStatusRecordIfPresent(statusPath);
|
|
376
|
+
const basicReuseOk = Boolean(
|
|
377
|
+
statusRecord && statusRecord.code === 0 && statusRecord.promptHash === hashAgentPromptFingerprint(agent),
|
|
378
|
+
);
|
|
379
|
+
if (!basicReuseOk) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
const summary = readAgentExecutionSummary(statusPath, {
|
|
383
|
+
agent: {
|
|
384
|
+
...agent,
|
|
385
|
+
exitContract: null,
|
|
386
|
+
components: [],
|
|
387
|
+
componentTargets: {},
|
|
388
|
+
deliverables: [],
|
|
389
|
+
proofArtifacts: [],
|
|
390
|
+
},
|
|
391
|
+
statusPath,
|
|
392
|
+
statusRecord,
|
|
393
|
+
logPath: options.logPath || null,
|
|
394
|
+
reportPath: options.reportPath || null,
|
|
395
|
+
});
|
|
396
|
+
if (!summary) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
return validateDesignSummary(agent, summary).ok;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function isClosureAgentId(agent, lanePaths, waveDefinition = null) {
|
|
403
|
+
return (
|
|
404
|
+
resolveWaveRoleBindings(waveDefinition, lanePaths, waveDefinition?.agents).closureAgentIds.includes(
|
|
405
|
+
agent?.agentId,
|
|
406
|
+
) || isSecurityReviewAgent(agent)
|
|
407
|
+
);
|
|
347
408
|
}
|
|
348
409
|
|
|
349
410
|
export function selectReusablePreCompletedAgentIds(
|
|
@@ -357,7 +418,7 @@ export function selectReusablePreCompletedAgentIds(
|
|
|
357
418
|
.filter(
|
|
358
419
|
(run) =>
|
|
359
420
|
!retryOverrideClearedAgentIds.has(run.agent.agentId) &&
|
|
360
|
-
!isClosureAgentId(run.agent, lanePaths) &&
|
|
421
|
+
!isClosureAgentId(run.agent, lanePaths, wave) &&
|
|
361
422
|
hasReusableSuccessStatus(run.agent, run.statusPath, {
|
|
362
423
|
wave,
|
|
363
424
|
derivedState,
|
|
@@ -369,10 +430,27 @@ export function selectReusablePreCompletedAgentIds(
|
|
|
369
430
|
);
|
|
370
431
|
}
|
|
371
432
|
|
|
372
|
-
export function selectInitialWaveRuns(agentRuns, lanePaths) {
|
|
433
|
+
export function selectInitialWaveRuns(agentRuns, lanePaths, waveDefinition = null) {
|
|
373
434
|
const implementationRuns = (agentRuns || []).filter(
|
|
374
|
-
(run) => !isClosureAgentId(run?.agent, lanePaths),
|
|
435
|
+
(run) => !isClosureAgentId(run?.agent, lanePaths, waveDefinition),
|
|
436
|
+
);
|
|
437
|
+
const pendingDesignRuns = implementationRuns.filter(
|
|
438
|
+
(run) =>
|
|
439
|
+
isDesignAgent(run.agent) &&
|
|
440
|
+
!hasReusableDesignPassStatus(run.agent, run.statusPath, {
|
|
441
|
+
wave: waveDefinition,
|
|
442
|
+
logPath: run.logPath,
|
|
443
|
+
}),
|
|
375
444
|
);
|
|
445
|
+
if (pendingDesignRuns.length > 0) {
|
|
446
|
+
return pendingDesignRuns;
|
|
447
|
+
}
|
|
448
|
+
const implementationFanoutRuns = implementationRuns.filter(
|
|
449
|
+
(run) => !isDocsOnlyDesignAgent(run.agent),
|
|
450
|
+
);
|
|
451
|
+
if (implementationFanoutRuns.length > 0) {
|
|
452
|
+
return implementationFanoutRuns;
|
|
453
|
+
}
|
|
376
454
|
return implementationRuns.length > 0 ? implementationRuns : agentRuns;
|
|
377
455
|
}
|
|
378
456
|
|
|
@@ -560,7 +638,38 @@ function retryBarrierFromOutcomes(outcomes, failures) {
|
|
|
560
638
|
};
|
|
561
639
|
}
|
|
562
640
|
|
|
563
|
-
|
|
641
|
+
function runsFromAgentIds(agentRuns, agentIds) {
|
|
642
|
+
const runsByAgentId = new Map((agentRuns || []).map((run) => [run.agent.agentId, run]));
|
|
643
|
+
return Array.from(new Set((agentIds || []).filter(Boolean)))
|
|
644
|
+
.map((agentId) => runsByAgentId.get(agentId))
|
|
645
|
+
.filter(Boolean);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function resolveRunsForResumePhase(agentRuns, lanePaths, resumePhase, waveDefinition = null) {
|
|
649
|
+
const roleBindings = resolveWaveRoleBindings(waveDefinition, lanePaths, waveDefinition?.agents);
|
|
650
|
+
if (resumePhase === "design") {
|
|
651
|
+
return (agentRuns || []).filter((run) => isDesignAgent(run.agent));
|
|
652
|
+
}
|
|
653
|
+
if (resumePhase === "integrating") {
|
|
654
|
+
return runsFromAgentIds(agentRuns, [roleBindings.integrationAgentId]);
|
|
655
|
+
}
|
|
656
|
+
if (resumePhase === "security-review") {
|
|
657
|
+
return (agentRuns || []).filter((run) => isSecurityReviewAgent(run.agent));
|
|
658
|
+
}
|
|
659
|
+
if (resumePhase === "docs-closure") {
|
|
660
|
+
return runsFromAgentIds(agentRuns, [roleBindings.documentationAgentId]);
|
|
661
|
+
}
|
|
662
|
+
if (resumePhase === "cont-qa-closure") {
|
|
663
|
+
return runsFromAgentIds(agentRuns, [roleBindings.contQaAgentId]);
|
|
664
|
+
}
|
|
665
|
+
if (resumePhase === "cont-eval") {
|
|
666
|
+
return runsFromAgentIds(agentRuns, [roleBindings.contEvalAgentId]);
|
|
667
|
+
}
|
|
668
|
+
return [];
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function resolveRelaunchRunsLegacy(agentRuns, failures, derivedState, lanePaths, waveDefinition = null) {
|
|
672
|
+
const roleBindings = resolveWaveRoleBindings(waveDefinition, lanePaths, waveDefinition?.agents);
|
|
564
673
|
const runsByAgentId = new Map(agentRuns.map((run) => [run.agent.agentId, run]));
|
|
565
674
|
const pendingFeedback = (derivedState?.coordinationState?.humanFeedback || []).filter((record) =>
|
|
566
675
|
isOpenCoordinationStatus(record.status),
|
|
@@ -693,7 +802,13 @@ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths
|
|
|
693
802
|
}
|
|
694
803
|
if (derivedState?.ledger?.phase === "docs-closure") {
|
|
695
804
|
return {
|
|
696
|
-
runs: [runsByAgentId.get(
|
|
805
|
+
runs: [runsByAgentId.get(roleBindings.documentationAgentId)].filter(Boolean),
|
|
806
|
+
barrier: null,
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
if (derivedState?.ledger?.phase === "design") {
|
|
810
|
+
return {
|
|
811
|
+
runs: agentRuns.filter((run) => isDesignAgent(run.agent)),
|
|
697
812
|
barrier: null,
|
|
698
813
|
};
|
|
699
814
|
}
|
|
@@ -705,19 +820,19 @@ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths
|
|
|
705
820
|
}
|
|
706
821
|
if (derivedState?.ledger?.phase === "cont-eval") {
|
|
707
822
|
return {
|
|
708
|
-
runs: [runsByAgentId.get(
|
|
823
|
+
runs: [runsByAgentId.get(roleBindings.contEvalAgentId)].filter(Boolean),
|
|
709
824
|
barrier: null,
|
|
710
825
|
};
|
|
711
826
|
}
|
|
712
827
|
if (derivedState?.ledger?.phase === "cont-qa-closure") {
|
|
713
828
|
return {
|
|
714
|
-
runs: [runsByAgentId.get(
|
|
829
|
+
runs: [runsByAgentId.get(roleBindings.contQaAgentId)].filter(Boolean),
|
|
715
830
|
barrier: null,
|
|
716
831
|
};
|
|
717
832
|
}
|
|
718
833
|
if (derivedState?.ledger?.phase === "integrating") {
|
|
719
834
|
return {
|
|
720
|
-
runs: [runsByAgentId.get(
|
|
835
|
+
runs: [runsByAgentId.get(roleBindings.integrationAgentId)].filter(Boolean),
|
|
721
836
|
barrier: null,
|
|
722
837
|
};
|
|
723
838
|
}
|
|
@@ -742,6 +857,216 @@ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths
|
|
|
742
857
|
};
|
|
743
858
|
}
|
|
744
859
|
|
|
860
|
+
function resolveRelaunchRunsFromWaveState(
|
|
861
|
+
agentRuns,
|
|
862
|
+
failures,
|
|
863
|
+
derivedState,
|
|
864
|
+
lanePaths,
|
|
865
|
+
waveDefinition,
|
|
866
|
+
waveState,
|
|
867
|
+
) {
|
|
868
|
+
const roleBindings = resolveWaveRoleBindings(waveDefinition, lanePaths, waveDefinition?.agents);
|
|
869
|
+
const pendingFeedback = (waveState?.coordinationState?.humanFeedback || []).filter((record) =>
|
|
870
|
+
isOpenCoordinationStatus(record.status),
|
|
871
|
+
);
|
|
872
|
+
const pendingHumanEscalations = (waveState?.coordinationState?.humanEscalations || []).filter(
|
|
873
|
+
(record) => isOpenCoordinationStatus(record.status),
|
|
874
|
+
);
|
|
875
|
+
if (pendingFeedback.length > 0 || pendingHumanEscalations.length > 0) {
|
|
876
|
+
return { runs: [], barrier: null };
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const nextAttemptNumber = Number(derivedState?.ledger?.attempt || 0) + 1;
|
|
880
|
+
const fallbackResolution = applyRetryFallbacks(
|
|
881
|
+
agentRuns,
|
|
882
|
+
failures,
|
|
883
|
+
lanePaths,
|
|
884
|
+
nextAttemptNumber,
|
|
885
|
+
waveDefinition,
|
|
886
|
+
);
|
|
887
|
+
const retryBarrier = retryBarrierFromOutcomes(fallbackResolution.outcomes, failures);
|
|
888
|
+
if (retryBarrier) {
|
|
889
|
+
return { runs: [], barrier: retryBarrier };
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
const clarificationTargets = new Set();
|
|
893
|
+
for (const record of openClarificationLinkedRequests(waveState?.coordinationState)) {
|
|
894
|
+
for (const target of record.targets || []) {
|
|
895
|
+
if (String(target).startsWith("agent:")) {
|
|
896
|
+
clarificationTargets.add(String(target).slice("agent:".length));
|
|
897
|
+
} else {
|
|
898
|
+
clarificationTargets.add(target);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (clarificationTargets.size > 0) {
|
|
903
|
+
return {
|
|
904
|
+
runs: runsFromAgentIds(agentRuns, Array.from(clarificationTargets)),
|
|
905
|
+
barrier: null,
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const blockingAssignments = (waveState?.capabilityAssignments || []).filter(
|
|
910
|
+
(assignment) => assignment.blocking,
|
|
911
|
+
);
|
|
912
|
+
if (blockingAssignments.length > 0) {
|
|
913
|
+
const unresolvedAssignments = blockingAssignments.filter((assignment) => !assignment.assignedAgentId);
|
|
914
|
+
if (unresolvedAssignments.length > 0) {
|
|
915
|
+
return {
|
|
916
|
+
runs: [],
|
|
917
|
+
barrier: {
|
|
918
|
+
statusCode: "helper-assignment-unresolved",
|
|
919
|
+
detail: `No matching assignee exists for helper requests (${unresolvedAssignments.map((assignment) => assignment.requestId).join(", ")}).`,
|
|
920
|
+
failures: unresolvedAssignments.map((assignment) => ({
|
|
921
|
+
agentId: null,
|
|
922
|
+
statusCode: "helper-assignment-unresolved",
|
|
923
|
+
logPath: null,
|
|
924
|
+
detail: assignment.assignmentDetail || assignment.summary || assignment.requestId,
|
|
925
|
+
})),
|
|
926
|
+
},
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
return {
|
|
930
|
+
runs: runsFromAgentIds(
|
|
931
|
+
agentRuns,
|
|
932
|
+
blockingAssignments.map((assignment) => assignment.assignedAgentId),
|
|
933
|
+
),
|
|
934
|
+
barrier: null,
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const unresolvedInboundAssignments =
|
|
939
|
+
waveState?.dependencySnapshot?.unresolvedInboundAssignments || [];
|
|
940
|
+
if (unresolvedInboundAssignments.length > 0) {
|
|
941
|
+
return {
|
|
942
|
+
runs: [],
|
|
943
|
+
barrier: {
|
|
944
|
+
statusCode: "dependency-assignment-unresolved",
|
|
945
|
+
detail: `Required inbound dependencies are not assigned (${unresolvedInboundAssignments.map((record) => record.id).join(", ")}).`,
|
|
946
|
+
failures: unresolvedInboundAssignments.map((record) => ({
|
|
947
|
+
agentId: null,
|
|
948
|
+
statusCode: "dependency-assignment-unresolved",
|
|
949
|
+
logPath: null,
|
|
950
|
+
detail: record.assignmentDetail || record.summary || record.id,
|
|
951
|
+
})),
|
|
952
|
+
},
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const inboundDependencyAgentIds = new Set(
|
|
957
|
+
(waveState?.dependencySnapshot?.openInbound || [])
|
|
958
|
+
.map((record) => record.assignedAgentId)
|
|
959
|
+
.filter(Boolean),
|
|
960
|
+
);
|
|
961
|
+
if (inboundDependencyAgentIds.size > 0) {
|
|
962
|
+
return {
|
|
963
|
+
runs: runsFromAgentIds(agentRuns, Array.from(inboundDependencyAgentIds)),
|
|
964
|
+
barrier: null,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const blockerAgentIds = new Set();
|
|
969
|
+
for (const record of waveState?.coordinationState?.blockers || []) {
|
|
970
|
+
if (!isOpenCoordinationStatus(record.status)) {
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
blockerAgentIds.add(record.agentId);
|
|
974
|
+
for (const target of record.targets || []) {
|
|
975
|
+
if (String(target).startsWith("agent:")) {
|
|
976
|
+
blockerAgentIds.add(String(target).slice("agent:".length));
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (blockerAgentIds.size > 0) {
|
|
981
|
+
return {
|
|
982
|
+
runs: runsFromAgentIds(agentRuns, Array.from(blockerAgentIds)),
|
|
983
|
+
barrier: null,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const sharedComponentWaitingAgentIds = new Set(
|
|
988
|
+
(failures || [])
|
|
989
|
+
.filter((failure) => failure.statusCode === "shared-component-sibling-pending")
|
|
990
|
+
.flatMap((failure) => failure.waitingOnAgentIds || [])
|
|
991
|
+
.filter(Boolean),
|
|
992
|
+
);
|
|
993
|
+
if (sharedComponentWaitingAgentIds.size > 0) {
|
|
994
|
+
return {
|
|
995
|
+
runs: runsFromAgentIds(agentRuns, Array.from(sharedComponentWaitingAgentIds)),
|
|
996
|
+
barrier: null,
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
const resumePlan = buildResumePlan(waveState, {
|
|
1001
|
+
waveDefinition,
|
|
1002
|
+
lanePaths,
|
|
1003
|
+
});
|
|
1004
|
+
if (!resumePlan.canResume || resumePlan.reason === "human-request") {
|
|
1005
|
+
return { runs: [], barrier: null };
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const phaseRuns = resolveRunsForResumePhase(
|
|
1009
|
+
agentRuns,
|
|
1010
|
+
lanePaths,
|
|
1011
|
+
resumePlan.resumeFromPhase,
|
|
1012
|
+
waveDefinition,
|
|
1013
|
+
);
|
|
1014
|
+
if (phaseRuns.length > 0 && resumePlan.resumeFromPhase !== "implementation") {
|
|
1015
|
+
return {
|
|
1016
|
+
runs: phaseRuns,
|
|
1017
|
+
barrier: null,
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const retryTargetAgentIds = normalizeRetryTargets(waveState?.retryTargetSet).map(
|
|
1022
|
+
(target) => target.agentId,
|
|
1023
|
+
);
|
|
1024
|
+
const implementationAgentIds =
|
|
1025
|
+
resumePlan.invalidatedAgentIds.length > 0
|
|
1026
|
+
? resumePlan.invalidatedAgentIds
|
|
1027
|
+
: retryTargetAgentIds;
|
|
1028
|
+
if (implementationAgentIds.length > 0) {
|
|
1029
|
+
return {
|
|
1030
|
+
runs: runsFromAgentIds(agentRuns, implementationAgentIds),
|
|
1031
|
+
barrier: null,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const failedAgentIds = new Set(failures.map((failure) => failure.agentId));
|
|
1036
|
+
return {
|
|
1037
|
+
runs: agentRuns.filter((run) => failedAgentIds.has(run.agent.agentId)),
|
|
1038
|
+
barrier: null,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
export function resolveRelaunchRuns(
|
|
1043
|
+
agentRuns,
|
|
1044
|
+
failures,
|
|
1045
|
+
derivedState,
|
|
1046
|
+
lanePaths,
|
|
1047
|
+
waveDefinition = null,
|
|
1048
|
+
options = {},
|
|
1049
|
+
) {
|
|
1050
|
+
const waveState = options?.waveState || null;
|
|
1051
|
+
if (!waveState) {
|
|
1052
|
+
return resolveRelaunchRunsLegacy(
|
|
1053
|
+
agentRuns,
|
|
1054
|
+
failures,
|
|
1055
|
+
derivedState,
|
|
1056
|
+
lanePaths,
|
|
1057
|
+
waveDefinition,
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
return resolveRelaunchRunsFromWaveState(
|
|
1061
|
+
agentRuns,
|
|
1062
|
+
failures,
|
|
1063
|
+
derivedState,
|
|
1064
|
+
lanePaths,
|
|
1065
|
+
waveDefinition,
|
|
1066
|
+
waveState,
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
745
1070
|
export function preflightWavesForExecutorAvailability(waves, lanePaths) {
|
|
746
1071
|
for (const wave of waves) {
|
|
747
1072
|
const mixValidation = validateWaveRuntimeMixAssignments(wave, {
|
|
@@ -776,12 +1101,21 @@ export function preflightWavesForExecutorAvailability(waves, lanePaths) {
|
|
|
776
1101
|
|
|
777
1102
|
function phaseFromGate(gateName) {
|
|
778
1103
|
switch (gateName) {
|
|
1104
|
+
case "designGate":
|
|
1105
|
+
return "design";
|
|
779
1106
|
case "implementationGate":
|
|
780
1107
|
case "componentGate":
|
|
781
|
-
case "
|
|
1108
|
+
case "helperAssignmentBarrier":
|
|
1109
|
+
case "dependencyBarrier":
|
|
1110
|
+
case "clarificationBarrier":
|
|
782
1111
|
return "implementation";
|
|
1112
|
+
case "contEvalGate":
|
|
1113
|
+
return "cont-eval";
|
|
1114
|
+
case "securityGate":
|
|
1115
|
+
return "security-review";
|
|
783
1116
|
case "integrationBarrier":
|
|
784
1117
|
return "integrating";
|
|
1118
|
+
case "componentMatrixGate":
|
|
785
1119
|
case "documentationGate":
|
|
786
1120
|
return "docs-closure";
|
|
787
1121
|
case "contQaGate":
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_CONT_QA_AGENT_ID,
|
|
2
3
|
DEFAULT_CONT_EVAL_AGENT_ID,
|
|
4
|
+
DEFAULT_DESIGN_ROLE_PROMPT_PATH,
|
|
5
|
+
DEFAULT_DOCUMENTATION_AGENT_ID,
|
|
6
|
+
DEFAULT_INTEGRATION_AGENT_ID,
|
|
3
7
|
DEFAULT_SECURITY_ROLE_PROMPT_PATH,
|
|
4
8
|
} from "./config.mjs";
|
|
5
9
|
|
|
@@ -34,6 +38,29 @@ export function isSecurityReportPath(relPath) {
|
|
|
34
38
|
return /(?:^|\/).*security.*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
export function isDesignRolePromptPath(
|
|
42
|
+
relPath,
|
|
43
|
+
designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH,
|
|
44
|
+
) {
|
|
45
|
+
const normalized = cleanPath(relPath);
|
|
46
|
+
const configured = cleanPath(designRolePromptPath);
|
|
47
|
+
return (
|
|
48
|
+
normalized === configured ||
|
|
49
|
+
normalized === DEFAULT_DESIGN_ROLE_PROMPT_PATH ||
|
|
50
|
+
normalized.endsWith("/wave-design-role.md")
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function isDesignReportPath(relPath) {
|
|
55
|
+
return /(?:^|\/).*(?:design|handoff|decision-lineage).*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizedOwnedPaths(agent) {
|
|
59
|
+
return Array.isArray(agent?.ownedPaths)
|
|
60
|
+
? agent.ownedPaths.map(cleanPath).filter(Boolean)
|
|
61
|
+
: [];
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
export function isContEvalImplementationOwningAgent(
|
|
38
65
|
agent,
|
|
39
66
|
{ contEvalAgentId = DEFAULT_CONT_EVAL_AGENT_ID } = {},
|
|
@@ -78,7 +105,103 @@ export function isSecurityReviewAgent(
|
|
|
78
105
|
return capabilities.includes("security-review");
|
|
79
106
|
}
|
|
80
107
|
|
|
108
|
+
export function isDesignAgent(
|
|
109
|
+
agent,
|
|
110
|
+
{ designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH } = {},
|
|
111
|
+
) {
|
|
112
|
+
if (!agent || typeof agent !== "object") {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const rolePromptPaths = Array.isArray(agent.rolePromptPaths) ? agent.rolePromptPaths : [];
|
|
116
|
+
if (
|
|
117
|
+
rolePromptPaths.some((rolePromptPath) =>
|
|
118
|
+
isDesignRolePromptPath(rolePromptPath, designRolePromptPath),
|
|
119
|
+
)
|
|
120
|
+
) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const capabilities = Array.isArray(agent.capabilities)
|
|
124
|
+
? agent.capabilities.map((entry) => String(entry || "").trim().toLowerCase())
|
|
125
|
+
: [];
|
|
126
|
+
return capabilities.includes("design");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isImplementationOwningDesignAgent(
|
|
130
|
+
agent,
|
|
131
|
+
{ designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH } = {},
|
|
132
|
+
) {
|
|
133
|
+
if (!isDesignAgent(agent, { designRolePromptPath })) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const ownedPaths = normalizedOwnedPaths(agent);
|
|
137
|
+
if (ownedPaths.length === 0) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return ownedPaths.some((ownedPath) => !isDesignReportPath(ownedPath));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function isDocsOnlyDesignAgent(
|
|
144
|
+
agent,
|
|
145
|
+
{ designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH } = {},
|
|
146
|
+
) {
|
|
147
|
+
return isDesignAgent(agent, { designRolePromptPath }) &&
|
|
148
|
+
!isImplementationOwningDesignAgent(agent, { designRolePromptPath });
|
|
149
|
+
}
|
|
150
|
+
|
|
81
151
|
export function resolveSecurityReviewReportPath(agent) {
|
|
82
|
-
const ownedPaths =
|
|
152
|
+
const ownedPaths = normalizedOwnedPaths(agent);
|
|
83
153
|
return ownedPaths.find((ownedPath) => isSecurityReportPath(ownedPath)) || null;
|
|
84
154
|
}
|
|
155
|
+
|
|
156
|
+
export function resolveDesignReportPath(agent) {
|
|
157
|
+
const ownedPaths = normalizedOwnedPaths(agent);
|
|
158
|
+
return ownedPaths.find((ownedPath) => isDesignReportPath(ownedPath)) || null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function resolveWaveRoleBindings(wave = {}, lanePaths = {}, agents = wave?.agents || []) {
|
|
162
|
+
const contQaAgentId =
|
|
163
|
+
wave?.contQaAgentId || lanePaths?.contQaAgentId || DEFAULT_CONT_QA_AGENT_ID;
|
|
164
|
+
const contEvalAgentId =
|
|
165
|
+
wave?.contEvalAgentId || lanePaths?.contEvalAgentId || DEFAULT_CONT_EVAL_AGENT_ID;
|
|
166
|
+
const integrationAgentId =
|
|
167
|
+
wave?.integrationAgentId || lanePaths?.integrationAgentId || DEFAULT_INTEGRATION_AGENT_ID;
|
|
168
|
+
const documentationAgentId =
|
|
169
|
+
wave?.documentationAgentId ||
|
|
170
|
+
lanePaths?.documentationAgentId ||
|
|
171
|
+
DEFAULT_DOCUMENTATION_AGENT_ID;
|
|
172
|
+
const securityReviewerAgentIds = Array.from(
|
|
173
|
+
new Set(
|
|
174
|
+
(Array.isArray(agents) ? agents : [])
|
|
175
|
+
.filter((agent) =>
|
|
176
|
+
isSecurityReviewAgent(agent, {
|
|
177
|
+
securityRolePromptPath: lanePaths?.securityRolePromptPath,
|
|
178
|
+
}),
|
|
179
|
+
)
|
|
180
|
+
.map((agent) => agent.agentId)
|
|
181
|
+
.filter(Boolean),
|
|
182
|
+
),
|
|
183
|
+
).sort();
|
|
184
|
+
const closureAgentIds = Array.from(
|
|
185
|
+
new Set(
|
|
186
|
+
[
|
|
187
|
+
contEvalAgentId,
|
|
188
|
+
integrationAgentId,
|
|
189
|
+
documentationAgentId,
|
|
190
|
+
contQaAgentId,
|
|
191
|
+
...securityReviewerAgentIds,
|
|
192
|
+
].filter(Boolean),
|
|
193
|
+
),
|
|
194
|
+
).sort();
|
|
195
|
+
return {
|
|
196
|
+
contQaAgentId,
|
|
197
|
+
contEvalAgentId,
|
|
198
|
+
integrationAgentId,
|
|
199
|
+
documentationAgentId,
|
|
200
|
+
securityReviewerAgentIds,
|
|
201
|
+
closureAgentIds,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function isClosureRoleAgentId(agentId, roleBindings) {
|
|
206
|
+
return (roleBindings?.closureAgentIds || []).includes(agentId);
|
|
207
|
+
}
|