@autobe/agent 0.30.0-dev.20260315 → 0.30.0
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/LICENSE +661 -661
- package/lib/constants/AutoBeTemplateFileConstant.d.ts +1 -1
- package/lib/factory/consentFunctionCall.js +4 -4
- package/lib/factory/createAutoBeContext.js +24 -24
- package/lib/factory/createAutoBeMessageContent.js +6 -6
- package/lib/index.mjs +41 -41
- package/lib/orchestrate/analyze/histories/transformAnalyzeScenarioHistory.js +18 -18
- package/lib/orchestrate/analyze/histories/transformAnalyzeScenarioReviewHistory.js +13 -13
- package/lib/orchestrate/analyze/histories/transformAnalyzeSectionCrossFileReviewHistory.js +47 -47
- package/lib/orchestrate/analyze/histories/transformAnalyzeSectionReviewHistory.js +66 -66
- package/lib/orchestrate/analyze/histories/transformAnalyzeWriteSectionHistory.js +91 -91
- package/lib/orchestrate/analyze/histories/transformAnalyzeWriteSectionPatchHistory.js +46 -46
- package/lib/orchestrate/analyze/histories/transformAnalyzeWriteUnitHistory.js +72 -72
- package/lib/orchestrate/analyze/orchestrateAnalyzeScenario.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeSectionCrossFileReview.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeSectionReview.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeWriteSection.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeWriteSectionPatch.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeWriteUnit.js +1 -1
- package/lib/orchestrate/common/histories/transformCommonCorrectCastingHistory.js +5 -5
- package/lib/orchestrate/common/histories/transformPreliminaryHistory.js +44 -44
- package/lib/orchestrate/common/histories/transformPreviousAndLatestCorrectHistory.js +44 -44
- package/lib/orchestrate/common/internal/fixPrelminaryApplication.js +22 -22
- package/lib/orchestrate/common/internal/validatePreliminary.js +116 -116
- package/lib/orchestrate/facade/createAutoBeFacadeController.js +3 -3
- package/lib/orchestrate/facade/structures/transformFacadeStateMessage.js +20 -20
- package/lib/orchestrate/interface/histories/transformInterfaceActionEndpointReviewHistory.js +34 -34
- package/lib/orchestrate/interface/histories/transformInterfaceActionEndpointWriteHistory.js +37 -37
- package/lib/orchestrate/interface/histories/transformInterfaceAuthorizationHistory.js +56 -56
- package/lib/orchestrate/interface/histories/transformInterfaceBaseEndpointReviewHistory.js +26 -26
- package/lib/orchestrate/interface/histories/transformInterfaceBaseEndpointWriteHistory.js +28 -28
- package/lib/orchestrate/interface/histories/transformInterfaceEndpointAuthorizationSection.js +11 -11
- package/lib/orchestrate/interface/histories/transformInterfaceGroupHistory.js +38 -38
- package/lib/orchestrate/interface/histories/transformInterfaceOperationHistory.js +29 -29
- package/lib/orchestrate/interface/histories/transformInterfaceOperationParameterHistory.js +15 -15
- package/lib/orchestrate/interface/histories/transformInterfaceOperationReviewHistory.js +6 -6
- package/lib/orchestrate/interface/histories/transformInterfacePrerequisiteHistory.js +25 -25
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaCastingHistory.js +67 -67
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaComplementHistory.js +60 -60
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaRefineHistory.js +83 -83
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaRenameHistory.js +29 -29
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaReviewHistory.js +74 -74
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaWriteHistory.js +46 -46
- package/lib/orchestrate/interface/orchestrateInterfaceAuthorization.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceEndpointReview.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceEndpointWrite.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceGroup.js +9 -9
- package/lib/orchestrate/interface/orchestrateInterfaceOperation.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceOperationReview.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfacePrerequisite.js +4 -4
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaCasting.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaComplement.js +5 -5
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaRefine.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaReview.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaWrite.js +1 -1
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceAuthorizationProgrammer.js +90 -90
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceEndpointProgrammer.js +6 -6
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceEndpointReviewProgrammer.js +25 -25
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceOperationProgrammer.js +71 -71
- package/lib/orchestrate/interface/programmers/AutoBeInterfacePrerequisiteProgrammer.js +162 -162
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaProgrammer.js +11 -11
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaPropertyReviseProgrammer.js +73 -73
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaRefineProgrammer.js +35 -35
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaReviewProgrammer.js +11 -11
- package/lib/orchestrate/interface/utils/AutoBeJsonSchemaFactory.js +4 -4
- package/lib/orchestrate/interface/utils/AutoBeJsonSchemaValidator.js +283 -283
- package/lib/orchestrate/interface/utils/fulfillJsonSchemaErrorMessages.js +76 -76
- package/lib/orchestrate/prisma/histories/transformPrismaAuthorizationHistory.js +76 -76
- package/lib/orchestrate/prisma/histories/transformPrismaAuthorizationReviewHistory.js +51 -51
- package/lib/orchestrate/prisma/histories/transformPrismaComponentReviewHistory.js +54 -54
- package/lib/orchestrate/prisma/histories/transformPrismaComponentsHistory.js +83 -83
- package/lib/orchestrate/prisma/histories/transformPrismaCorrectHistory.js +6 -6
- package/lib/orchestrate/prisma/histories/transformPrismaGroupHistory.js +22 -22
- package/lib/orchestrate/prisma/histories/transformPrismaGroupReviewHistory.js +41 -41
- package/lib/orchestrate/prisma/histories/transformPrismaSchemaHistory.js +39 -39
- package/lib/orchestrate/prisma/histories/transformPrismaSchemaReviewHistory.js +41 -41
- package/lib/orchestrate/prisma/orchestratePrismaAuthorization.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaAuthorizationReview.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaComponent.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaComponentReview.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaCorrect.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaGroup.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaGroupReview.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaSchema.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaSchemaReview.js +1 -1
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationProgrammer.js +23 -23
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationReviewProgrammer.js +7 -7
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseComponentProgrammer.js +5 -5
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseComponentReviewProgrammer.js +20 -20
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseGroupProgrammer.js +13 -13
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseGroupReviewProgrammer.js +51 -51
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseSchemaProgrammer.js +25 -25
- package/lib/orchestrate/realize/histories/transformRealizeAuthorizationCorrectHistory.js +28 -28
- package/lib/orchestrate/realize/histories/transformRealizeAuthorizationWriteHistory.js +14 -14
- package/lib/orchestrate/realize/histories/transformRealizeCollectorCorrectHistory.js +42 -42
- package/lib/orchestrate/realize/histories/transformRealizeCollectorPlanHistory.js +27 -27
- package/lib/orchestrate/realize/histories/transformRealizeCollectorWriteHistory.js +57 -57
- package/lib/orchestrate/realize/histories/transformRealizeCorrectCastingHistory.js +35 -35
- package/lib/orchestrate/realize/histories/transformRealizeOperationCorrectHistory.js +14 -14
- package/lib/orchestrate/realize/histories/transformRealizeOperationWriteHistory.js +37 -37
- package/lib/orchestrate/realize/histories/transformRealizeOperationWriteHistory.js.map +1 -1
- package/lib/orchestrate/realize/histories/transformRealizeTransformerCorrectHistory.js +47 -47
- package/lib/orchestrate/realize/histories/transformRealizeTransformerPlanHistory.js +27 -27
- package/lib/orchestrate/realize/histories/transformRealizeTransformerWriteHistory.js +51 -51
- package/lib/orchestrate/realize/orchestrateRealizeAuthorizationCorrect.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeAuthorizationWrite.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeCollectorCorrectOverall.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeCollectorPlan.js +11 -11
- package/lib/orchestrate/realize/orchestrateRealizeCollectorWrite.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeOperationCorrectOverall.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeOperationWrite.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeTransformerCorrectOverall.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeTransformerPlan.js +11 -11
- package/lib/orchestrate/realize/orchestrateRealizeTransformerWrite.js +1 -1
- package/lib/orchestrate/realize/programmers/AutoBeRealizeCollectorProgrammer.js +41 -41
- package/lib/orchestrate/realize/programmers/AutoBeRealizeOperationProgrammer.js +21 -21
- package/lib/orchestrate/realize/programmers/AutoBeRealizeTransformerProgrammer.js +57 -57
- package/lib/orchestrate/realize/utils/getRealizeWriteInputType.js +3 -3
- package/lib/orchestrate/realize/utils/printErrorHints.js +5 -5
- package/lib/orchestrate/test/compile/getTestImportStatements.js +9 -9
- package/lib/orchestrate/test/histories/transformTestAuthorizeWriteHistory.js +38 -38
- package/lib/orchestrate/test/histories/transformTestGenerationWriteHistory.js +79 -79
- package/lib/orchestrate/test/histories/transformTestOperationWriteHistory.js +162 -162
- package/lib/orchestrate/test/histories/transformTestPrepareWriteHistory.js +54 -54
- package/lib/orchestrate/test/histories/transformTestScenarioHistory.js +31 -31
- package/lib/orchestrate/test/histories/transformTestScenarioReviewHistory.js +27 -27
- package/lib/orchestrate/test/orchestrateTestScenario.js +1 -1
- package/lib/orchestrate/test/orchestrateTestScenarioReview.js +1 -1
- package/lib/orchestrate/test/programmers/AutoBeTestAuthorizeProgrammer.js +31 -31
- package/lib/orchestrate/test/programmers/AutoBeTestGenerateProgrammer.js +23 -23
- package/lib/orchestrate/test/programmers/AutoBeTestOperationProgrammer.js +6 -6
- package/lib/orchestrate/test/programmers/AutoBeTestPrepareProgrammer.js +34 -34
- package/lib/orchestrate/test/programmers/AutoBeTestScenarioProgrammer.js +39 -39
- package/lib/utils/predicateStateMessage.js +19 -19
- package/lib/utils/validateEmptyCode.js +16 -16
- package/package.json +5 -5
- package/src/AutoBeAgent.ts +374 -374
- package/src/AutoBeAgentBase.ts +126 -126
- package/src/AutoBeMockAgent.ts +254 -254
- package/src/constants/AutoBeConfigConstant.ts +173 -173
- package/src/constants/AutoBeTemplateFileConstant.ts +1 -1
- package/src/context/AutoBeContext.ts +107 -107
- package/src/context/AutoBeState.ts +66 -66
- package/src/context/AutoBeTokenUsage.ts +307 -307
- package/src/context/AutoBeTokenUsageComponent.ts +227 -227
- package/src/describe/describe.ts +47 -47
- package/src/describe/image/histories/transformImageDescribeDraftHistories.ts +16 -16
- package/src/describe/image/orchestrateImageDescribeDraft.ts +200 -200
- package/src/describe/image/structures/IAutoBeImageDescribeDraftApplication.ts +92 -92
- package/src/describe/imageDescribe.ts +64 -64
- package/src/factory/consentFunctionCall.ts +141 -141
- package/src/factory/createAgenticaHistory.ts +71 -71
- package/src/factory/createAutoBeContext.ts +584 -584
- package/src/factory/createAutoBeMessageContent.ts +36 -36
- package/src/factory/createAutoBeState.ts +20 -20
- package/src/factory/getAutoBeGenerated.ts +317 -317
- package/src/factory/getAutoBeRealizeGenerated.ts +31 -31
- package/src/factory/getCriticalCompiler.ts +52 -52
- package/src/factory/index.ts +1 -1
- package/src/factory/mergeSystemMessages.ts +60 -60
- package/src/factory/supportFunctionCallFallback.ts +214 -214
- package/src/factory/supportMistral.ts +138 -138
- package/src/index.ts +16 -16
- package/src/orchestrate/analyze/fillTocDeterministic.ts +280 -280
- package/src/orchestrate/analyze/histories/transformAnalyzeScenarioHistory.ts +55 -55
- package/src/orchestrate/analyze/histories/transformAnalyzeScenarioReviewHistory.ts +74 -74
- package/src/orchestrate/analyze/histories/transformAnalyzeSectionCrossFileReviewHistory.ts +179 -179
- package/src/orchestrate/analyze/histories/transformAnalyzeSectionReviewHistory.ts +163 -163
- package/src/orchestrate/analyze/histories/transformAnalyzeWriteSectionHistory.ts +191 -191
- package/src/orchestrate/analyze/histories/transformAnalyzeWriteSectionPatchHistory.ts +122 -122
- package/src/orchestrate/analyze/histories/transformAnalyzeWriteUnitHistory.ts +161 -161
- package/src/orchestrate/analyze/index.ts +7 -7
- package/src/orchestrate/analyze/orchestrateAnalyze.ts +1693 -1693
- package/src/orchestrate/analyze/orchestrateAnalyzeScenario.ts +267 -267
- package/src/orchestrate/analyze/orchestrateAnalyzeScenarioReview.ts +122 -122
- package/src/orchestrate/analyze/orchestrateAnalyzeSectionCrossFileReview.ts +146 -146
- package/src/orchestrate/analyze/orchestrateAnalyzeSectionReview.ts +154 -154
- package/src/orchestrate/analyze/orchestrateAnalyzeWriteSection.ts +388 -388
- package/src/orchestrate/analyze/orchestrateAnalyzeWriteSectionPatch.ts +407 -407
- package/src/orchestrate/analyze/orchestrateAnalyzeWriteUnit.ts +348 -348
- package/src/orchestrate/analyze/programmers/AutoBeAnalyzeProgrammer.ts +149 -149
- package/src/orchestrate/analyze/structures/FixedAnalyzeTemplate.ts +961 -961
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeScenarioApplication.ts +141 -141
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeScenarioReviewApplication.ts +96 -96
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeSectionCrossFileReviewApplication.ts +160 -160
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeSectionReviewApplication.ts +192 -192
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeWriteSectionApplication.ts +142 -142
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeWriteUnitApplication.ts +123 -123
- package/src/orchestrate/analyze/utils/buildConstraintConsistencyReport.ts +813 -813
- package/src/orchestrate/analyze/utils/buildErrorCodeRegistry.ts +324 -324
- package/src/orchestrate/analyze/utils/buildHardValidators.ts +88 -88
- package/src/orchestrate/analyze/utils/detectInventedEntities.ts +87 -87
- package/src/orchestrate/analyze/utils/detectProseConstraintConflicts.ts +319 -319
- package/src/orchestrate/analyze/utils/repairSectionReviewUtils.ts +181 -181
- package/src/orchestrate/analyze/utils/repairUtils.ts +60 -60
- package/src/orchestrate/analyze/utils/validateScenarioBasics.ts +104 -104
- package/src/orchestrate/common/AutoBePreliminaryController.ts +400 -400
- package/src/orchestrate/common/histories/transformCommonCorrectCastingHistory.ts +32 -32
- package/src/orchestrate/common/histories/transformPreliminaryHistory.ts +742 -742
- package/src/orchestrate/common/histories/transformPreviousAndLatestCorrectHistory.ts +77 -77
- package/src/orchestrate/common/internal/complementPreliminaryCollection.ts +263 -263
- package/src/orchestrate/common/internal/convertToSectionEntries.ts +57 -57
- package/src/orchestrate/common/internal/createPreliminaryCollection.ts +76 -76
- package/src/orchestrate/common/internal/fixPrelminaryApplication.ts +346 -346
- package/src/orchestrate/common/internal/validatePreliminary.ts +671 -671
- package/src/orchestrate/common/orchestrateCommonCorrectCasting.ts +223 -223
- package/src/orchestrate/common/orchestratePreliminary.ts +610 -610
- package/src/orchestrate/common/structures/AutoBePreliminaryRequest.ts +42 -42
- package/src/orchestrate/common/structures/IAnalysisSectionEntry.ts +35 -35
- package/src/orchestrate/common/structures/IAutoBeCommonCorrectCastingApplication.ts +72 -72
- package/src/orchestrate/common/structures/IAutoBeOrchestrateResult.ts +28 -28
- package/src/orchestrate/common/structures/IAutoBePreliminaryCollection.ts +52 -52
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetAnalysisSections.ts +34 -34
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetDatabaseSchemas.ts +31 -31
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetInterfaceOperations.ts +32 -32
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetInterfaceSchemas.ts +31 -31
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousAnalysisSections.ts +28 -28
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousDatabaseSchemas.ts +88 -88
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousInterfaceOperations.ts +102 -102
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousInterfaceSchemas.ts +105 -105
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetRealizeCollectors.ts +32 -32
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetRealizeTransformers.ts +32 -32
- package/src/orchestrate/facade/createAutoBeFacadeController.ts +118 -118
- package/src/orchestrate/facade/histories/IAutoBeFacadeApplication.ts +139 -139
- package/src/orchestrate/facade/histories/IAutoBeFacadeApplicationProps.ts +8 -8
- package/src/orchestrate/facade/histories/IAutoBeFacadeApplicationResult.ts +9 -9
- package/src/orchestrate/facade/structures/transformFacadeStateMessage.ts +59 -59
- package/src/orchestrate/index.ts +5 -5
- package/src/orchestrate/interface/histories/transformInterfaceActionEndpointReviewHistory.ts +83 -83
- package/src/orchestrate/interface/histories/transformInterfaceActionEndpointWriteHistory.ts +78 -78
- package/src/orchestrate/interface/histories/transformInterfaceAuthorizationHistory.ts +129 -129
- package/src/orchestrate/interface/histories/transformInterfaceBaseEndpointReviewHistory.ts +74 -74
- package/src/orchestrate/interface/histories/transformInterfaceBaseEndpointWriteHistory.ts +68 -68
- package/src/orchestrate/interface/histories/transformInterfaceCommonHistory.ts +64 -64
- package/src/orchestrate/interface/histories/transformInterfaceEndpointAuthorizationSection.ts +42 -42
- package/src/orchestrate/interface/histories/transformInterfaceGroupHistory.ts +103 -103
- package/src/orchestrate/interface/histories/transformInterfaceOperationHistory.ts +81 -81
- package/src/orchestrate/interface/histories/transformInterfaceOperationParameterHistory.ts +53 -53
- package/src/orchestrate/interface/histories/transformInterfaceOperationReviewHistory.ts +49 -49
- package/src/orchestrate/interface/histories/transformInterfacePrerequisiteHistory.ts +94 -94
- package/src/orchestrate/interface/histories/transformInterfaceSchemaCastingHistory.ts +122 -122
- package/src/orchestrate/interface/histories/transformInterfaceSchemaComplementHistory.ts +192 -192
- package/src/orchestrate/interface/histories/transformInterfaceSchemaRefineHistory.ts +189 -189
- package/src/orchestrate/interface/histories/transformInterfaceSchemaRenameHistory.ts +63 -63
- package/src/orchestrate/interface/histories/transformInterfaceSchemaReviewHistory.ts +173 -173
- package/src/orchestrate/interface/histories/transformInterfaceSchemaWriteHistory.ts +88 -88
- package/src/orchestrate/interface/index.ts +6 -6
- package/src/orchestrate/interface/orchestrateInterface.ts +118 -118
- package/src/orchestrate/interface/orchestrateInterfaceActionEndpoint.ts +58 -58
- package/src/orchestrate/interface/orchestrateInterfaceAuthorization.ts +208 -208
- package/src/orchestrate/interface/orchestrateInterfaceBaseEndpoint.ts +55 -55
- package/src/orchestrate/interface/orchestrateInterfaceEndpoint.ts +67 -67
- package/src/orchestrate/interface/orchestrateInterfaceEndpointOverall.ts +80 -80
- package/src/orchestrate/interface/orchestrateInterfaceEndpointReview.ts +212 -212
- package/src/orchestrate/interface/orchestrateInterfaceEndpointWrite.ts +236 -236
- package/src/orchestrate/interface/orchestrateInterfaceGroup.ts +166 -166
- package/src/orchestrate/interface/orchestrateInterfaceOperation.ts +322 -322
- package/src/orchestrate/interface/orchestrateInterfaceOperationReview.ts +245 -245
- package/src/orchestrate/interface/orchestrateInterfacePrerequisite.ts +240 -240
- package/src/orchestrate/interface/orchestrateInterfaceSchema.ts +191 -191
- package/src/orchestrate/interface/orchestrateInterfaceSchemaCasting.ts +274 -274
- package/src/orchestrate/interface/orchestrateInterfaceSchemaComplement.ts +329 -329
- package/src/orchestrate/interface/orchestrateInterfaceSchemaRefine.ts +292 -292
- package/src/orchestrate/interface/orchestrateInterfaceSchemaRename.ts +266 -266
- package/src/orchestrate/interface/orchestrateInterfaceSchemaReview.ts +310 -310
- package/src/orchestrate/interface/orchestrateInterfaceSchemaWrite.ts +283 -283
- package/src/orchestrate/interface/programmers/AutoBeInterfaceAuthorizationProgrammer.ts +260 -260
- package/src/orchestrate/interface/programmers/AutoBeInterfaceEndpointProgrammer.ts +155 -155
- package/src/orchestrate/interface/programmers/AutoBeInterfaceEndpointReviewProgrammer.ts +139 -139
- package/src/orchestrate/interface/programmers/AutoBeInterfaceOperationProgrammer.ts +277 -277
- package/src/orchestrate/interface/programmers/AutoBeInterfacePrerequisiteProgrammer.ts +259 -259
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaProgrammer.ts +152 -152
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaPropertyReviseProgrammer.ts +347 -347
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaRefineProgrammer.ts +183 -183
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaReviewProgrammer.ts +263 -263
- package/src/orchestrate/interface/structures/IAutoBeInterfaceAuthorizationApplication.ts +135 -135
- package/src/orchestrate/interface/structures/IAutoBeInterfaceEndpointReviewApplication.ts +128 -128
- package/src/orchestrate/interface/structures/IAutoBeInterfaceEndpointWriteApplication.ts +133 -133
- package/src/orchestrate/interface/structures/IAutoBeInterfaceGroupApplication.ts +122 -122
- package/src/orchestrate/interface/structures/IAutoBeInterfaceOperationApplication.ts +178 -178
- package/src/orchestrate/interface/structures/IAutoBeInterfaceOperationReviewApplication.ts +175 -175
- package/src/orchestrate/interface/structures/IAutoBeInterfacePrerequisiteApplication.ts +130 -130
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaApplication.ts +121 -121
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaCastingApplication.ts +153 -153
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaComplementApplication.ts +123 -123
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaRefineApplication.ts +181 -181
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaRenameApplication.ts +45 -45
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaReviewApplication.ts +108 -108
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaCollection.ts +42 -42
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaFactory.ts +714 -714
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaNamingConvention.ts +86 -86
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaValidator.ts +725 -725
- package/src/orchestrate/interface/utils/fulfillJsonSchemaErrorMessages.ts +219 -219
- package/src/orchestrate/prisma/histories/transformPrismaAuthorizationHistory.ts +144 -144
- package/src/orchestrate/prisma/histories/transformPrismaAuthorizationReviewHistory.ts +104 -104
- package/src/orchestrate/prisma/histories/transformPrismaComponentReviewHistory.ts +97 -97
- package/src/orchestrate/prisma/histories/transformPrismaComponentsHistory.ts +136 -136
- package/src/orchestrate/prisma/histories/transformPrismaCorrectHistory.ts +41 -41
- package/src/orchestrate/prisma/histories/transformPrismaGroupHistory.ts +63 -63
- package/src/orchestrate/prisma/histories/transformPrismaGroupReviewHistory.ts +79 -79
- package/src/orchestrate/prisma/histories/transformPrismaSchemaHistory.ts +97 -97
- package/src/orchestrate/prisma/histories/transformPrismaSchemaReviewHistory.ts +126 -126
- package/src/orchestrate/prisma/index.ts +7 -7
- package/src/orchestrate/prisma/orchestratePrisma.ts +296 -296
- package/src/orchestrate/prisma/orchestratePrismaAuthorization.ts +173 -173
- package/src/orchestrate/prisma/orchestratePrismaAuthorizationReview.ts +183 -183
- package/src/orchestrate/prisma/orchestratePrismaComponent.ts +205 -205
- package/src/orchestrate/prisma/orchestratePrismaComponentReview.ts +205 -205
- package/src/orchestrate/prisma/orchestratePrismaCorrect.ts +265 -265
- package/src/orchestrate/prisma/orchestratePrismaGroup.ts +121 -121
- package/src/orchestrate/prisma/orchestratePrismaGroupReview.ts +141 -141
- package/src/orchestrate/prisma/orchestratePrismaSchema.ts +218 -218
- package/src/orchestrate/prisma/orchestratePrismaSchemaReview.ts +220 -220
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationProgrammer.ts +86 -86
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationReviewProgrammer.ts +103 -103
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseComponentProgrammer.ts +80 -80
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseComponentReviewProgrammer.ts +168 -168
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseGroupProgrammer.ts +63 -63
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseGroupReviewProgrammer.ts +210 -210
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseModelProgrammer.ts +57 -57
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseSchemaProgrammer.ts +92 -92
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseAuthorizationApplication.ts +147 -147
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseAuthorizationReviewApplication.ts +153 -153
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseComponentApplication.ts +142 -142
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseComponentReviewApplication.ts +151 -151
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseCorrectApplication.ts +157 -157
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseGroupApplication.ts +117 -117
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseGroupReviewApplication.ts +154 -154
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseSchemaApplication.ts +144 -144
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseSchemaReviewApplication.ts +138 -138
- package/src/orchestrate/realize/histories/transformRealizeAuthorizationCorrectHistory.ts +89 -89
- package/src/orchestrate/realize/histories/transformRealizeAuthorizationWriteHistory.ts +45 -45
- package/src/orchestrate/realize/histories/transformRealizeCollectorCorrectHistory.ts +137 -137
- package/src/orchestrate/realize/histories/transformRealizeCollectorPlanHistory.ts +64 -64
- package/src/orchestrate/realize/histories/transformRealizeCollectorWriteHistory.ts +147 -147
- package/src/orchestrate/realize/histories/transformRealizeCorrectCastingHistory.ts +69 -69
- package/src/orchestrate/realize/histories/transformRealizeOperationCorrectHistory.ts +94 -94
- package/src/orchestrate/realize/histories/transformRealizeOperationWriteHistory.ts +127 -127
- package/src/orchestrate/realize/histories/transformRealizeTransformerCorrectHistory.ts +147 -147
- package/src/orchestrate/realize/histories/transformRealizeTransformerPlanHistory.ts +61 -61
- package/src/orchestrate/realize/histories/transformRealizeTransformerWriteHistory.ts +133 -133
- package/src/orchestrate/realize/histories/transformRealizeWriteMembershipHistory.ts +30 -30
- package/src/orchestrate/realize/index.ts +3 -3
- package/src/orchestrate/realize/internal/orchestrateRealizeCorrectCasting.ts +444 -444
- package/src/orchestrate/realize/internal/orchestrateRealizeCorrectOverall.ts +449 -449
- package/src/orchestrate/realize/orchestrateRealize.ts +143 -143
- package/src/orchestrate/realize/orchestrateRealizeAuthorizationCorrect.ts +207 -207
- package/src/orchestrate/realize/orchestrateRealizeAuthorizationWrite.ts +209 -209
- package/src/orchestrate/realize/orchestrateRealizeCollector.ts +41 -41
- package/src/orchestrate/realize/orchestrateRealizeCollectorCorrectCasting.ts +43 -43
- package/src/orchestrate/realize/orchestrateRealizeCollectorCorrectOverall.ts +157 -157
- package/src/orchestrate/realize/orchestrateRealizeCollectorPlan.ts +264 -264
- package/src/orchestrate/realize/orchestrateRealizeCollectorWrite.ts +225 -225
- package/src/orchestrate/realize/orchestrateRealizeOperation.ts +48 -48
- package/src/orchestrate/realize/orchestrateRealizeOperationCorrectCasting.ts +69 -69
- package/src/orchestrate/realize/orchestrateRealizeOperationCorrectOverall.ts +173 -173
- package/src/orchestrate/realize/orchestrateRealizeOperationWrite.ts +262 -262
- package/src/orchestrate/realize/orchestrateRealizeTransformer.ts +41 -41
- package/src/orchestrate/realize/orchestrateRealizeTransformerCorrectCasting.ts +38 -38
- package/src/orchestrate/realize/orchestrateRealizeTransformerCorrectOverall.ts +160 -160
- package/src/orchestrate/realize/orchestrateRealizeTransformerPlan.ts +254 -254
- package/src/orchestrate/realize/orchestrateRealizeTransformerWrite.ts +226 -226
- package/src/orchestrate/realize/programmers/AutoBeRealizeCollectorProgrammer.ts +424 -424
- package/src/orchestrate/realize/programmers/AutoBeRealizeOperationProgrammer.ts +409 -409
- package/src/orchestrate/realize/programmers/AutoBeRealizeTransformerProgrammer.ts +451 -451
- package/src/orchestrate/realize/programmers/compileRealizeFiles.ts +65 -65
- package/src/orchestrate/realize/structures/IAutoBeRealizeAuthorizationCorrectApplication.ts +119 -119
- package/src/orchestrate/realize/structures/IAutoBeRealizeAuthorizationWriteApplication.ts +167 -167
- package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorCorrectApplication.ts +191 -191
- package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorPlanApplication.ts +177 -177
- package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorWriteApplication.ts +207 -207
- package/src/orchestrate/realize/structures/IAutoBeRealizeFunctionFailure.ts +11 -11
- package/src/orchestrate/realize/structures/IAutoBeRealizeOperationCorrectApplication.ts +134 -134
- package/src/orchestrate/realize/structures/IAutoBeRealizeOperationWriteApplication.ts +138 -138
- package/src/orchestrate/realize/structures/IAutoBeRealizeScenarioResult.ts +32 -32
- package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerCorrectApplication.ts +248 -248
- package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerPlanApplication.ts +150 -150
- package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerWriteApplication.ts +278 -278
- package/src/orchestrate/realize/utils/AuthorizationFileSystem.ts +10 -10
- package/src/orchestrate/realize/utils/AutoBeRealizeAuthorizationFileSystem.ts +9 -9
- package/src/orchestrate/realize/utils/AutoBeRealizeAuthorizationReplaceImport.ts +62 -62
- package/src/orchestrate/realize/utils/InternalFileSystem.ts +12 -12
- package/src/orchestrate/realize/utils/ProviderFileSystem.ts +4 -4
- package/src/orchestrate/realize/utils/filterDiagnostics.ts +25 -25
- package/src/orchestrate/realize/utils/generateTS2339Hints.ts +54 -54
- package/src/orchestrate/realize/utils/getRealizeWriteImportStatements.ts +42 -42
- package/src/orchestrate/realize/utils/getRealizeWriteInputType.ts +88 -88
- package/src/orchestrate/realize/utils/printErrorHints.ts +54 -54
- package/src/orchestrate/test/compile/getTestArtifacts.ts +124 -124
- package/src/orchestrate/test/compile/getTestExternalDeclarations.ts +39 -39
- package/src/orchestrate/test/compile/getTestImportStatements.ts +27 -27
- package/src/orchestrate/test/experimental/orchestrateTestCorrect.ast +237 -237
- package/src/orchestrate/test/experimental/orchestrateTestWrite.ast +322 -322
- package/src/orchestrate/test/experimental/transformTestCorrectHistories.ast +52 -52
- package/src/orchestrate/test/histories/transformTestAuthorizeWriteHistory.ts +80 -80
- package/src/orchestrate/test/histories/transformTestCorrectOverallHistory.ts +116 -116
- package/src/orchestrate/test/histories/transformTestGenerationWriteHistory.ts +118 -118
- package/src/orchestrate/test/histories/transformTestOperationWriteHistory.ts +287 -287
- package/src/orchestrate/test/histories/transformTestPrepareWriteHistory.ts +95 -95
- package/src/orchestrate/test/histories/transformTestScenarioHistory.ts +120 -120
- package/src/orchestrate/test/histories/transformTestScenarioReviewHistory.ts +99 -99
- package/src/orchestrate/test/histories/transformTestValidateEvent.ts +11 -11
- package/src/orchestrate/test/index.ts +5 -5
- package/src/orchestrate/test/internal/orchestrateTestCorrectCasting.ts +96 -96
- package/src/orchestrate/test/internal/orchestrateTestCorrectOverall.ts +219 -219
- package/src/orchestrate/test/internal/orchestrateTestCorrectRequest.ts +206 -206
- package/src/orchestrate/test/orchestrateTest.ts +161 -161
- package/src/orchestrate/test/orchestrateTestAuthorize.ts +125 -125
- package/src/orchestrate/test/orchestrateTestAuthorizeWrite.ts +263 -263
- package/src/orchestrate/test/orchestrateTestGenerate.ts +121 -121
- package/src/orchestrate/test/orchestrateTestGenerateWrite.ts +211 -211
- package/src/orchestrate/test/orchestrateTestOperation.ts +133 -133
- package/src/orchestrate/test/orchestrateTestOperationWrite.ts +221 -221
- package/src/orchestrate/test/orchestrateTestPrepare.ts +117 -117
- package/src/orchestrate/test/orchestrateTestPrepareWrite.ts +264 -264
- package/src/orchestrate/test/orchestrateTestScenario.ts +290 -290
- package/src/orchestrate/test/orchestrateTestScenarioReview.ts +262 -262
- package/src/orchestrate/test/programmers/AutoBeTestAuthorizeProgrammer.ts +149 -149
- package/src/orchestrate/test/programmers/AutoBeTestFunctionProgrammer.ts +90 -90
- package/src/orchestrate/test/programmers/AutoBeTestGenerateProgrammer.ts +170 -170
- package/src/orchestrate/test/programmers/AutoBeTestOperationProgrammer.ts +168 -168
- package/src/orchestrate/test/programmers/AutoBeTestPrepareProgrammer.ts +261 -261
- package/src/orchestrate/test/programmers/AutoBeTestScenarioProgrammer.ts +316 -316
- package/src/orchestrate/test/structures/IAutoBeTestArtifacts.ts +8 -8
- package/src/orchestrate/test/structures/IAutoBeTestAuthorizationWriteApplication.ts +113 -113
- package/src/orchestrate/test/structures/IAutoBeTestAuthorizeWriteResult.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestCorrectOverallApplication.ts +134 -134
- package/src/orchestrate/test/structures/IAutoBeTestCorrectRequestApplication.ts +152 -152
- package/src/orchestrate/test/structures/IAutoBeTestFunction.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestFunctionFailure.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestGenerateProcedure.ts +15 -15
- package/src/orchestrate/test/structures/IAutoBeTestGenerationWriteApplication.ts +145 -145
- package/src/orchestrate/test/structures/IAutoBeTestOperationProcedure.ts +17 -17
- package/src/orchestrate/test/structures/IAutoBeTestOperationWriteApplication.ts +162 -162
- package/src/orchestrate/test/structures/IAutoBeTestPrepareCorrectOverallApplication.ts +194 -194
- package/src/orchestrate/test/structures/IAutoBeTestPrepareProcedure.ts +8 -8
- package/src/orchestrate/test/structures/IAutoBeTestPrepareWriteApplication.ts +147 -147
- package/src/orchestrate/test/structures/IAutoBeTestProcedure.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestScenarioApplication.ts +105 -105
- package/src/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.ts +8 -8
- package/src/orchestrate/test/structures/IAutoBeTestScenarioAuthorizationActor.ts +7 -7
- package/src/orchestrate/test/structures/IAutoBeTestScenarioReviewApplication.ts +133 -133
- package/src/orchestrate/test/utils/getPrerequisites.ts +51 -51
- package/src/orchestrate/test/utils/getReferenceIds.ts +26 -26
- package/src/orchestrate/test/utils/insertScriptToTestResult.ts +14 -14
- package/src/structures/IAutoBeConfig.ts +110 -110
- package/src/structures/IAutoBeOrchestrateHistory.ts +44 -44
- package/src/structures/IAutoBeProps.ts +102 -102
- package/src/structures/IAutoBeVendor.ts +113 -113
- package/src/utils/AutoBePreliminaryExhaustedError.ts +30 -30
- package/src/utils/AutoBeTimeoutError.ts +35 -35
- package/src/utils/EmbeddingProvider.ts +4 -4
- package/src/utils/LocalEmbeddingProvider.ts +145 -145
- package/src/utils/RAGRetrieval.ts +411 -411
- package/src/utils/TimedConversation.ts +125 -125
- package/src/utils/backoffRetry.ts +169 -169
- package/src/utils/divideArray.ts +35 -35
- package/src/utils/emplaceMap.ts +31 -31
- package/src/utils/executeCachedBatch.ts +67 -67
- package/src/utils/forceRetry.ts +15 -15
- package/src/utils/getEmbedder.ts +16 -16
- package/src/utils/parseTextFunctionCall.ts +437 -437
- package/src/utils/predicateStateMessage.ts +131 -131
- package/src/utils/validateEmptyCode.ts +73 -73
- package/src/utils/validateEnglishOnly.ts +224 -224
- package/README.md +0 -261
|
@@ -1,1693 +1,1693 @@
|
|
|
1
|
-
import { AgenticaValidationError } from "@agentica/core";
|
|
2
|
-
import {
|
|
3
|
-
AutoBeAnalyze,
|
|
4
|
-
AutoBeAnalyzeHistory,
|
|
5
|
-
AutoBeAnalyzeScenarioEvent,
|
|
6
|
-
AutoBeAnalyzeScenarioReviewEvent,
|
|
7
|
-
AutoBeAnalyzeSectionReviewEvent,
|
|
8
|
-
AutoBeAnalyzeSectionReviewFileResult,
|
|
9
|
-
AutoBeAnalyzeSectionReviewIssue,
|
|
10
|
-
AutoBeAnalyzeSectionReviewRejectedModuleUnit,
|
|
11
|
-
AutoBeAnalyzeWriteModuleEvent,
|
|
12
|
-
AutoBeAnalyzeWriteSectionEvent,
|
|
13
|
-
AutoBeAnalyzeWriteUnitEvent,
|
|
14
|
-
AutoBeAssistantMessageHistory,
|
|
15
|
-
AutoBeProgressEventBase,
|
|
16
|
-
} from "@autobe/interface";
|
|
17
|
-
import { v7 } from "uuid";
|
|
18
|
-
|
|
19
|
-
import { AutoBeConfigConstant } from "../../constants/AutoBeConfigConstant";
|
|
20
|
-
import { AutoBeContext } from "../../context/AutoBeContext";
|
|
21
|
-
import { AutoBePreliminaryExhaustedError } from "../../utils/AutoBePreliminaryExhaustedError";
|
|
22
|
-
import { AutoBeTimeoutError } from "../../utils/AutoBeTimeoutError";
|
|
23
|
-
import { executeCachedBatch } from "../../utils/executeCachedBatch";
|
|
24
|
-
import { fillTocDeterministic } from "./fillTocDeterministic";
|
|
25
|
-
import { orchestrateAnalyzeScenario } from "./orchestrateAnalyzeScenario";
|
|
26
|
-
import { orchestrateAnalyzeScenarioReview } from "./orchestrateAnalyzeScenarioReview";
|
|
27
|
-
import { orchestrateAnalyzeSectionCrossFileReview } from "./orchestrateAnalyzeSectionCrossFileReview";
|
|
28
|
-
import { orchestrateAnalyzeSectionReview } from "./orchestrateAnalyzeSectionReview";
|
|
29
|
-
import { orchestrateAnalyzeWriteSection } from "./orchestrateAnalyzeWriteSection";
|
|
30
|
-
import { orchestrateAnalyzeWriteSectionPatch } from "./orchestrateAnalyzeWriteSectionPatch";
|
|
31
|
-
import { orchestrateAnalyzeWriteUnit } from "./orchestrateAnalyzeWriteUnit";
|
|
32
|
-
import {
|
|
33
|
-
assembleContent,
|
|
34
|
-
assembleModule,
|
|
35
|
-
} from "./programmers/AutoBeAnalyzeProgrammer";
|
|
36
|
-
import {
|
|
37
|
-
FixedAnalyzeTemplateFeature,
|
|
38
|
-
FixedAnalyzeTemplateUnitTemplate,
|
|
39
|
-
buildFixedAnalyzeExpandedTemplate,
|
|
40
|
-
expandFixedAnalyzeTemplateUnits,
|
|
41
|
-
} from "./structures/FixedAnalyzeTemplate";
|
|
42
|
-
import {
|
|
43
|
-
buildFileAttributeDuplicateMap,
|
|
44
|
-
buildFileConflictMap,
|
|
45
|
-
buildFileEnumConflictMap,
|
|
46
|
-
buildFilePermissionConflictMap,
|
|
47
|
-
buildFileStateFieldConflictMap,
|
|
48
|
-
detectAttributeDuplicates,
|
|
49
|
-
detectConstraintConflicts,
|
|
50
|
-
detectEnumConflicts,
|
|
51
|
-
detectPermissionConflicts,
|
|
52
|
-
detectStateFieldConflicts,
|
|
53
|
-
} from "./utils/buildConstraintConsistencyReport";
|
|
54
|
-
import {
|
|
55
|
-
buildFileErrorCodeConflictMap,
|
|
56
|
-
detectErrorCodeConflicts,
|
|
57
|
-
} from "./utils/buildErrorCodeRegistry";
|
|
58
|
-
import { detectOversizedToc } from "./utils/buildHardValidators";
|
|
59
|
-
import {
|
|
60
|
-
buildFileProseConflictMap,
|
|
61
|
-
detectProseConstraintConflicts,
|
|
62
|
-
} from "./utils/detectProseConstraintConflicts";
|
|
63
|
-
import { validateScenarioBasics } from "./utils/validateScenarioBasics";
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Per-file state tracking across all three stages (Module → Unit → Section).
|
|
67
|
-
*
|
|
68
|
-
* Maintains each file's intermediate results and cross-file review feedback
|
|
69
|
-
* throughout the stage-synchronized pipeline.
|
|
70
|
-
*/
|
|
71
|
-
interface IFileState {
|
|
72
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
73
|
-
moduleResult: AutoBeAnalyzeWriteModuleEvent | null;
|
|
74
|
-
unitResults: AutoBeAnalyzeWriteUnitEvent[] | null;
|
|
75
|
-
sectionResults: AutoBeAnalyzeWriteSectionEvent[][] | null;
|
|
76
|
-
sectionFeedback?: string;
|
|
77
|
-
// Section-stage partial regeneration tracking
|
|
78
|
-
rejectedModuleUnits?: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null;
|
|
79
|
-
sectionRetryCount?: number;
|
|
80
|
-
sectionReviewCount?: number;
|
|
81
|
-
sectionStagnationCount?: number;
|
|
82
|
-
lastSectionContentSignature?: string;
|
|
83
|
-
lastSectionRejectionSignature?: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const ANALYZE_SCENARIO_MAX_RETRY = 2;
|
|
87
|
-
const ANALYZE_SECTION_FILE_MAX_RETRY = 5;
|
|
88
|
-
const ANALYZE_SECTION_FILE_MAX_REVIEW = 2;
|
|
89
|
-
const ANALYZE_SECTION_STAGNATION_MAX = 4;
|
|
90
|
-
const ANALYZE_DEBUG_LOG = process.env.AUTOBE_DEBUG_ANALYZE === "1";
|
|
91
|
-
|
|
92
|
-
const analyzeDebug = (message: string): void => {
|
|
93
|
-
if (!ANALYZE_DEBUG_LOG) return;
|
|
94
|
-
console.log(`[analyze-debug] ${new Date().toISOString()} ${message}`);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export const orchestrateAnalyze = async (
|
|
98
|
-
ctx: AutoBeContext,
|
|
99
|
-
): Promise<AutoBeAssistantMessageHistory | AutoBeAnalyzeHistory> => {
|
|
100
|
-
// Initialize analysis state
|
|
101
|
-
const step: number = (ctx.state().analyze?.step ?? -1) + 1;
|
|
102
|
-
const startTime: Date = new Date();
|
|
103
|
-
|
|
104
|
-
ctx.dispatch({
|
|
105
|
-
type: "analyzeStart",
|
|
106
|
-
id: v7(),
|
|
107
|
-
step,
|
|
108
|
-
created_at: startTime.toISOString(),
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Generate analysis scenario with pre-check + LLM review + retry loop
|
|
112
|
-
let scenario!: AutoBeAnalyzeScenarioEvent;
|
|
113
|
-
let scenarioFeedback: string | undefined;
|
|
114
|
-
|
|
115
|
-
for (let attempt = 0; attempt <= ANALYZE_SCENARIO_MAX_RETRY; attempt++) {
|
|
116
|
-
const rawScenario = await orchestrateAnalyzeScenario(ctx, {
|
|
117
|
-
feedback: scenarioFeedback,
|
|
118
|
-
});
|
|
119
|
-
if (rawScenario.type === "assistantMessage")
|
|
120
|
-
return ctx.assistantMessage(rawScenario);
|
|
121
|
-
|
|
122
|
-
// 1) Programmatic pre-check
|
|
123
|
-
const preCheck = validateScenarioBasics({
|
|
124
|
-
prefix: rawScenario.prefix,
|
|
125
|
-
actors: rawScenario.actors,
|
|
126
|
-
entities: rawScenario.entities,
|
|
127
|
-
});
|
|
128
|
-
if (!preCheck.valid && attempt < ANALYZE_SCENARIO_MAX_RETRY) {
|
|
129
|
-
analyzeDebug(
|
|
130
|
-
`Scenario pre-check failed (attempt ${attempt}): ${preCheck.errors.join("; ")}`,
|
|
131
|
-
);
|
|
132
|
-
scenarioFeedback = `Programmatic validation failed:\n${preCheck.errors.join("\n")}`;
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// 2) LLM review
|
|
137
|
-
let review: AutoBeAnalyzeScenarioReviewEvent;
|
|
138
|
-
try {
|
|
139
|
-
review = await orchestrateAnalyzeScenarioReview(ctx, {
|
|
140
|
-
scenario: rawScenario,
|
|
141
|
-
retry: attempt,
|
|
142
|
-
});
|
|
143
|
-
} catch (e) {
|
|
144
|
-
if (
|
|
145
|
-
e instanceof AgenticaValidationError ||
|
|
146
|
-
e instanceof AutoBePreliminaryExhaustedError ||
|
|
147
|
-
e instanceof AutoBeTimeoutError
|
|
148
|
-
) {
|
|
149
|
-
analyzeDebug(
|
|
150
|
-
`scenario review force-pass (attempt ${attempt}) error=${(e as Error).constructor.name}`,
|
|
151
|
-
);
|
|
152
|
-
review = {
|
|
153
|
-
type: "analyzeScenarioReview",
|
|
154
|
-
id: v7(),
|
|
155
|
-
approved: true,
|
|
156
|
-
feedback:
|
|
157
|
-
"Review could not be completed; proceeding with current scenario.",
|
|
158
|
-
issues: [],
|
|
159
|
-
tokenUsage: {
|
|
160
|
-
total: 0,
|
|
161
|
-
input: { total: 0, cached: 0 },
|
|
162
|
-
output: {
|
|
163
|
-
total: 0,
|
|
164
|
-
reasoning: 0,
|
|
165
|
-
accepted_prediction: 0,
|
|
166
|
-
rejected_prediction: 0,
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
metric: {
|
|
170
|
-
attempt: 0,
|
|
171
|
-
success: 0,
|
|
172
|
-
consent: 0,
|
|
173
|
-
validationFailure: 0,
|
|
174
|
-
invalidJson: 0,
|
|
175
|
-
},
|
|
176
|
-
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
177
|
-
retry: attempt,
|
|
178
|
-
created_at: new Date().toISOString(),
|
|
179
|
-
};
|
|
180
|
-
} else {
|
|
181
|
-
throw e;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (review.approved || attempt >= ANALYZE_SCENARIO_MAX_RETRY) {
|
|
186
|
-
analyzeDebug(
|
|
187
|
-
review.approved
|
|
188
|
-
? `Scenario approved (attempt ${attempt})`
|
|
189
|
-
: `Scenario max retry reached (attempt ${attempt}), proceeding`,
|
|
190
|
-
);
|
|
191
|
-
scenario = rawScenario;
|
|
192
|
-
ctx.dispatch(scenario);
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
analyzeDebug(`Scenario rejected (attempt ${attempt}): ${review.feedback}`);
|
|
197
|
-
scenarioFeedback = review.feedback;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Initialize per-file state
|
|
201
|
-
const fileStates: IFileState[] = scenario.files.map((file) => ({
|
|
202
|
-
file,
|
|
203
|
-
moduleResult: null,
|
|
204
|
-
unitResults: null,
|
|
205
|
-
sectionResults: null,
|
|
206
|
-
}));
|
|
207
|
-
|
|
208
|
-
// Progress tracking for each stage
|
|
209
|
-
const moduleWriteProgress: AutoBeProgressEventBase = {
|
|
210
|
-
total: scenario.files.length,
|
|
211
|
-
completed: 0,
|
|
212
|
-
};
|
|
213
|
-
const unitWriteProgress: AutoBeProgressEventBase = {
|
|
214
|
-
total: 0,
|
|
215
|
-
completed: 0,
|
|
216
|
-
};
|
|
217
|
-
const sectionWriteProgress: AutoBeProgressEventBase = {
|
|
218
|
-
total: 0,
|
|
219
|
-
completed: 0,
|
|
220
|
-
};
|
|
221
|
-
const perFileSectionReviewProgress: AutoBeProgressEventBase = {
|
|
222
|
-
total: 0,
|
|
223
|
-
completed: 0,
|
|
224
|
-
};
|
|
225
|
-
const crossFileSectionReviewProgress: AutoBeProgressEventBase = {
|
|
226
|
-
total: 1,
|
|
227
|
-
completed: 0,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// === STAGE 1: MODULE (deterministic — no LLM) ===
|
|
231
|
-
processStageModuleDeterministic(ctx, {
|
|
232
|
-
scenario,
|
|
233
|
-
fileStates,
|
|
234
|
-
moduleWriteProgress,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// === STAGE 2: UNIT (fixed units deterministic, dynamic units LLM) ===
|
|
238
|
-
await processStageUnit(ctx, {
|
|
239
|
-
scenario,
|
|
240
|
-
fileStates,
|
|
241
|
-
unitWriteProgress,
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// === STAGE 3: SECTION (01-05 only, TOC excluded) ===
|
|
245
|
-
await processStageSection(ctx, {
|
|
246
|
-
scenario,
|
|
247
|
-
fileStates,
|
|
248
|
-
sectionWriteProgress,
|
|
249
|
-
perFileSectionReviewProgress,
|
|
250
|
-
crossFileSectionReviewProgress,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
// === TOC FILL (deterministic — no LLM) ===
|
|
254
|
-
const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
|
|
255
|
-
(scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
|
|
256
|
-
);
|
|
257
|
-
const tocIndex = fileStates.findIndex((s) => s.file.filename === "00-toc.md");
|
|
258
|
-
let tocContent: string | null = null;
|
|
259
|
-
if (tocIndex >= 0) {
|
|
260
|
-
tocContent = fillTocDeterministic(ctx, {
|
|
261
|
-
scenario,
|
|
262
|
-
tocFileState: fileStates[tocIndex]!,
|
|
263
|
-
otherFileStates: fileStates.filter((_, i) => i !== tocIndex),
|
|
264
|
-
expandedTemplate,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// === ASSEMBLE ===
|
|
269
|
-
const files: AutoBeAnalyze.IFile[] = [];
|
|
270
|
-
for (let fileIndex = 0; fileIndex < fileStates.length; fileIndex++) {
|
|
271
|
-
const state = fileStates[fileIndex]!;
|
|
272
|
-
// TOC uses flat content directly (no module/unit hierarchy)
|
|
273
|
-
const content =
|
|
274
|
-
fileIndex === tocIndex
|
|
275
|
-
? tocContent!
|
|
276
|
-
: assembleContent(
|
|
277
|
-
state.moduleResult!,
|
|
278
|
-
state.unitResults!,
|
|
279
|
-
state.sectionResults!,
|
|
280
|
-
);
|
|
281
|
-
const module = assembleModule(
|
|
282
|
-
state.moduleResult!,
|
|
283
|
-
state.unitResults!,
|
|
284
|
-
state.sectionResults!,
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
files.push({
|
|
288
|
-
...state.file,
|
|
289
|
-
title: state.moduleResult!.title,
|
|
290
|
-
summary: state.moduleResult!.summary,
|
|
291
|
-
content,
|
|
292
|
-
module,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Complete the analysis
|
|
297
|
-
return ctx.dispatch({
|
|
298
|
-
type: "analyzeComplete",
|
|
299
|
-
id: v7(),
|
|
300
|
-
actors: scenario.actors,
|
|
301
|
-
prefix: scenario.prefix,
|
|
302
|
-
files,
|
|
303
|
-
aggregates: ctx.getCurrentAggregates("analyze"),
|
|
304
|
-
step,
|
|
305
|
-
elapsed: new Date().getTime() - startTime.getTime(),
|
|
306
|
-
created_at: new Date().toISOString(),
|
|
307
|
-
}) satisfies AutoBeAnalyzeHistory;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
// MODULE (deterministic — no LLM calls)
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Generate module structure deterministically from FixedAnalyzeTemplate.
|
|
314
|
-
*
|
|
315
|
-
* No LLM calls needed — module titles, purposes, and structure are all derived
|
|
316
|
-
* from the fixed 6-file SRS template.
|
|
317
|
-
*/
|
|
318
|
-
function processStageModuleDeterministic(
|
|
319
|
-
ctx: AutoBeContext,
|
|
320
|
-
props: {
|
|
321
|
-
scenario: AutoBeAnalyzeScenarioEvent;
|
|
322
|
-
fileStates: IFileState[];
|
|
323
|
-
moduleWriteProgress: AutoBeProgressEventBase;
|
|
324
|
-
},
|
|
325
|
-
): void {
|
|
326
|
-
const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
|
|
327
|
-
(props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
|
|
328
|
-
);
|
|
329
|
-
for (const [i, state] of props.fileStates.entries()) {
|
|
330
|
-
const template = expandedTemplate[i]!;
|
|
331
|
-
const moduleEvent: AutoBeAnalyzeWriteModuleEvent = {
|
|
332
|
-
type: "analyzeWriteModule",
|
|
333
|
-
id: v7(),
|
|
334
|
-
title: `${props.scenario.prefix} — ${template.description}`,
|
|
335
|
-
summary: template.description,
|
|
336
|
-
moduleSections: template.modules.map((m) => ({
|
|
337
|
-
title: m.title,
|
|
338
|
-
purpose: m.purpose,
|
|
339
|
-
content: m.purpose,
|
|
340
|
-
})),
|
|
341
|
-
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
342
|
-
retry: 0,
|
|
343
|
-
total: props.fileStates.length,
|
|
344
|
-
completed: i + 1,
|
|
345
|
-
tokenUsage: {
|
|
346
|
-
total: 0,
|
|
347
|
-
input: { total: 0, cached: 0 },
|
|
348
|
-
output: {
|
|
349
|
-
total: 0,
|
|
350
|
-
reasoning: 0,
|
|
351
|
-
accepted_prediction: 0,
|
|
352
|
-
rejected_prediction: 0,
|
|
353
|
-
},
|
|
354
|
-
},
|
|
355
|
-
metric: {
|
|
356
|
-
attempt: 0,
|
|
357
|
-
success: 0,
|
|
358
|
-
consent: 0,
|
|
359
|
-
validationFailure: 0,
|
|
360
|
-
invalidJson: 0,
|
|
361
|
-
},
|
|
362
|
-
acquisition: { previousAnalysisSections: [] },
|
|
363
|
-
created_at: new Date().toISOString(),
|
|
364
|
-
};
|
|
365
|
-
state.moduleResult = moduleEvent;
|
|
366
|
-
ctx.dispatch(moduleEvent);
|
|
367
|
-
props.moduleWriteProgress.completed++;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// UNIT
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Process the Unit stage for all files.
|
|
375
|
-
*
|
|
376
|
-
* Fixed-strategy modules get deterministic unit generation (no LLM).
|
|
377
|
-
* Dynamic-strategy modules (perEntity/perActor/perEntityGroup) use LLM. No
|
|
378
|
-
* cross-file unit review — Hard Validators at section stage handle
|
|
379
|
-
* consistency.
|
|
380
|
-
*/
|
|
381
|
-
async function processStageUnit(
|
|
382
|
-
ctx: AutoBeContext,
|
|
383
|
-
props: {
|
|
384
|
-
scenario: AutoBeAnalyzeScenarioEvent;
|
|
385
|
-
fileStates: IFileState[];
|
|
386
|
-
unitWriteProgress: AutoBeProgressEventBase;
|
|
387
|
-
},
|
|
388
|
-
): Promise<void> {
|
|
389
|
-
const promptCacheKey: string = v7();
|
|
390
|
-
const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
|
|
391
|
-
(props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
// Count total units needed for progress tracking
|
|
395
|
-
for (const [fileIndex, state] of props.fileStates.entries()) {
|
|
396
|
-
const template = expandedTemplate[fileIndex]!;
|
|
397
|
-
props.unitWriteProgress.total += template.modules.length;
|
|
398
|
-
void state; // used below
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
await executeCachedBatch(
|
|
402
|
-
ctx,
|
|
403
|
-
props.fileStates.map((state, fileIndex) => async (cacheKey) => {
|
|
404
|
-
// TOC is filled deterministically after all other files complete
|
|
405
|
-
if (state.file.filename === "00-toc.md") return [];
|
|
406
|
-
|
|
407
|
-
const moduleResult: AutoBeAnalyzeWriteModuleEvent = state.moduleResult!;
|
|
408
|
-
const template = expandedTemplate[fileIndex]!;
|
|
409
|
-
analyzeDebug(
|
|
410
|
-
`unit file-start fileIndex=${fileIndex} file="${state.file.filename}"`,
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
const unitResults: AutoBeAnalyzeWriteUnitEvent[] = [];
|
|
414
|
-
for (
|
|
415
|
-
let moduleIndex: number = 0;
|
|
416
|
-
moduleIndex < moduleResult.moduleSections.length;
|
|
417
|
-
moduleIndex++
|
|
418
|
-
) {
|
|
419
|
-
const moduleTemplate = template.modules[moduleIndex]!;
|
|
420
|
-
const strategy = moduleTemplate.unitStrategy;
|
|
421
|
-
|
|
422
|
-
if (strategy.type === "fixed") {
|
|
423
|
-
// Deterministic unit generation — no LLM
|
|
424
|
-
const unitEvent = buildDeterministicUnitEvent(ctx, {
|
|
425
|
-
moduleIndex,
|
|
426
|
-
units: strategy.units,
|
|
427
|
-
progress: props.unitWriteProgress,
|
|
428
|
-
});
|
|
429
|
-
ctx.dispatch(unitEvent);
|
|
430
|
-
unitResults.push(unitEvent);
|
|
431
|
-
} else {
|
|
432
|
-
// Dynamic units — expand from template, then LLM writes content+keywords
|
|
433
|
-
const unitStart: number = Date.now();
|
|
434
|
-
analyzeDebug(
|
|
435
|
-
`unit module-start fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} strategy=${strategy.type}`,
|
|
436
|
-
);
|
|
437
|
-
try {
|
|
438
|
-
const unitEvent: AutoBeAnalyzeWriteUnitEvent =
|
|
439
|
-
await orchestrateAnalyzeWriteUnit(ctx, {
|
|
440
|
-
scenario: props.scenario,
|
|
441
|
-
file: state.file,
|
|
442
|
-
moduleEvent: moduleResult,
|
|
443
|
-
moduleIndex,
|
|
444
|
-
progress: props.unitWriteProgress,
|
|
445
|
-
promptCacheKey: cacheKey,
|
|
446
|
-
retry: 0,
|
|
447
|
-
});
|
|
448
|
-
analyzeDebug(
|
|
449
|
-
`unit module-done fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitCount=${unitEvent.unitSections.length} elapsedMs=${Date.now() - unitStart}`,
|
|
450
|
-
);
|
|
451
|
-
unitResults.push(unitEvent);
|
|
452
|
-
} catch (e) {
|
|
453
|
-
if (
|
|
454
|
-
e instanceof AgenticaValidationError ||
|
|
455
|
-
e instanceof AutoBePreliminaryExhaustedError ||
|
|
456
|
-
e instanceof AutoBeTimeoutError
|
|
457
|
-
) {
|
|
458
|
-
analyzeDebug(
|
|
459
|
-
`unit module-skipped fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} elapsedMs=${Date.now() - unitStart} — using fallback`,
|
|
460
|
-
);
|
|
461
|
-
const expandedUnits = expandFixedAnalyzeTemplateUnits(
|
|
462
|
-
moduleTemplate,
|
|
463
|
-
props.scenario.entities,
|
|
464
|
-
props.scenario.actors,
|
|
465
|
-
);
|
|
466
|
-
const fallbackEvent = buildDeterministicUnitEvent(ctx, {
|
|
467
|
-
moduleIndex,
|
|
468
|
-
units: expandedUnits,
|
|
469
|
-
progress: props.unitWriteProgress,
|
|
470
|
-
});
|
|
471
|
-
ctx.dispatch(fallbackEvent);
|
|
472
|
-
unitResults.push(fallbackEvent);
|
|
473
|
-
} else {
|
|
474
|
-
throw e;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
state.unitResults = unitResults;
|
|
480
|
-
analyzeDebug(
|
|
481
|
-
`unit file-done fileIndex=${fileIndex} file="${state.file.filename}"`,
|
|
482
|
-
);
|
|
483
|
-
return unitResults;
|
|
484
|
-
}),
|
|
485
|
-
promptCacheKey,
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/** Build a deterministic AutoBeAnalyzeWriteUnitEvent for fixed-strategy modules. */
|
|
490
|
-
function buildDeterministicUnitEvent(
|
|
491
|
-
ctx: AutoBeContext,
|
|
492
|
-
props: {
|
|
493
|
-
moduleIndex: number;
|
|
494
|
-
units: FixedAnalyzeTemplateUnitTemplate[];
|
|
495
|
-
progress: AutoBeProgressEventBase;
|
|
496
|
-
},
|
|
497
|
-
): AutoBeAnalyzeWriteUnitEvent {
|
|
498
|
-
props.progress.completed++;
|
|
499
|
-
return {
|
|
500
|
-
type: "analyzeWriteUnit",
|
|
501
|
-
id: v7(),
|
|
502
|
-
moduleIndex: props.moduleIndex,
|
|
503
|
-
unitSections: props.units.map((u) => ({
|
|
504
|
-
title: u.titlePattern,
|
|
505
|
-
purpose: u.purposePattern,
|
|
506
|
-
content: u.purposePattern,
|
|
507
|
-
keywords: [...u.keywords],
|
|
508
|
-
})),
|
|
509
|
-
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
510
|
-
retry: 0,
|
|
511
|
-
total: props.progress.total,
|
|
512
|
-
completed: props.progress.completed,
|
|
513
|
-
tokenUsage: {
|
|
514
|
-
total: 0,
|
|
515
|
-
input: { total: 0, cached: 0 },
|
|
516
|
-
output: {
|
|
517
|
-
total: 0,
|
|
518
|
-
reasoning: 0,
|
|
519
|
-
accepted_prediction: 0,
|
|
520
|
-
rejected_prediction: 0,
|
|
521
|
-
},
|
|
522
|
-
},
|
|
523
|
-
metric: {
|
|
524
|
-
attempt: 0,
|
|
525
|
-
success: 0,
|
|
526
|
-
consent: 0,
|
|
527
|
-
validationFailure: 0,
|
|
528
|
-
invalidJson: 0,
|
|
529
|
-
},
|
|
530
|
-
acquisition: { previousAnalysisSections: [] },
|
|
531
|
-
created_at: new Date().toISOString(),
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// SECTION
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Process the Section stage for all files with 2-pass review.
|
|
539
|
-
*
|
|
540
|
-
* Flow:
|
|
541
|
-
*
|
|
542
|
-
* 1. Write sections for pending files in parallel
|
|
543
|
-
* 2. Pass 1: Per-file detailed review (parallel) — validates EARS format, value
|
|
544
|
-
* consistency, bridge blocks, intra-file deduplication
|
|
545
|
-
* 3. Pass 2: Cross-file lightweight review (single call) — validates terminology
|
|
546
|
-
* alignment, value consistency across files, naming conventions
|
|
547
|
-
* 4. Merge results from both passes — reject if either pass rejects
|
|
548
|
-
* 5. Retry only rejected files (max 3 attempts)
|
|
549
|
-
*/
|
|
550
|
-
async function processStageSection(
|
|
551
|
-
ctx: AutoBeContext,
|
|
552
|
-
props: {
|
|
553
|
-
scenario: AutoBeAnalyzeScenarioEvent;
|
|
554
|
-
fileStates: IFileState[];
|
|
555
|
-
sectionWriteProgress: AutoBeProgressEventBase;
|
|
556
|
-
perFileSectionReviewProgress: AutoBeProgressEventBase;
|
|
557
|
-
crossFileSectionReviewProgress: AutoBeProgressEventBase;
|
|
558
|
-
},
|
|
559
|
-
): Promise<void> {
|
|
560
|
-
// Exclude TOC (00-toc.md) — it is filled deterministically after all files
|
|
561
|
-
const pendingIndices: Set<number> = new Set(
|
|
562
|
-
props.fileStates
|
|
563
|
-
.map((s, i) => (s.file.filename === "00-toc.md" ? -1 : i))
|
|
564
|
-
.filter((i) => i >= 0),
|
|
565
|
-
);
|
|
566
|
-
let crossFileReviewCount: number = 0;
|
|
567
|
-
|
|
568
|
-
for (
|
|
569
|
-
let attempt: number = 0;
|
|
570
|
-
attempt < AutoBeConfigConstant.ANALYZE_RETRY && pendingIndices.size > 0;
|
|
571
|
-
attempt++
|
|
572
|
-
) {
|
|
573
|
-
// Dynamically increase progress for retries (module-level granularity)
|
|
574
|
-
const pendingModuleCount = [...pendingIndices].reduce(
|
|
575
|
-
(sum, fi) => sum + (props.fileStates[fi]?.unitResults?.length ?? 1),
|
|
576
|
-
0,
|
|
577
|
-
);
|
|
578
|
-
props.perFileSectionReviewProgress.total += pendingModuleCount;
|
|
579
|
-
if (attempt > 0) {
|
|
580
|
-
props.crossFileSectionReviewProgress.total++;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// Write sections for pending files in parallel
|
|
584
|
-
const pendingArray: number[] = [...pendingIndices];
|
|
585
|
-
const sectionFileBatches: number[][] = chunkSectionFileIndices(
|
|
586
|
-
pendingArray,
|
|
587
|
-
computeSectionBatchSize({
|
|
588
|
-
attempt,
|
|
589
|
-
pendingCount: pendingArray.length,
|
|
590
|
-
}),
|
|
591
|
-
);
|
|
592
|
-
const promptCacheKey: string = v7();
|
|
593
|
-
|
|
594
|
-
// Build scenario entity name list for invention validation (P0-B)
|
|
595
|
-
const scenarioEntityNames = props.scenario.entities.map((e) => e.name);
|
|
596
|
-
|
|
597
|
-
// Collect per-file review results (populated inside write+review batch)
|
|
598
|
-
const perFileReviewResults: Map<number, AutoBeAnalyzeSectionReviewEvent> =
|
|
599
|
-
new Map();
|
|
600
|
-
|
|
601
|
-
for (const sectionBatch of sectionFileBatches)
|
|
602
|
-
await executeCachedBatch(
|
|
603
|
-
ctx,
|
|
604
|
-
sectionBatch.map((fileIndex) => async (cacheKey) => {
|
|
605
|
-
const state: IFileState = props.fileStates[fileIndex]!;
|
|
606
|
-
const moduleResult: AutoBeAnalyzeWriteModuleEvent =
|
|
607
|
-
state.moduleResult!;
|
|
608
|
-
const unitResults: AutoBeAnalyzeWriteUnitEvent[] = state.unitResults!;
|
|
609
|
-
analyzeDebug(
|
|
610
|
-
`section file-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" batchSize=${sectionBatch.length}`,
|
|
611
|
-
);
|
|
612
|
-
|
|
613
|
-
// Build rejected module/unit lookup for selective regeneration
|
|
614
|
-
const rejectedSet: Set<string> | null = buildRejectedSet(
|
|
615
|
-
state.rejectedModuleUnits,
|
|
616
|
-
);
|
|
617
|
-
const feedbackMap: Map<string, ISectionAwareFeedback> =
|
|
618
|
-
buildFeedbackMap(state.rejectedModuleUnits);
|
|
619
|
-
|
|
620
|
-
// Increase write progress only for sections that will be regenerated
|
|
621
|
-
for (let mi: number = 0; mi < unitResults.length; mi++) {
|
|
622
|
-
const unitEvent: AutoBeAnalyzeWriteUnitEvent = unitResults[mi]!;
|
|
623
|
-
for (let ui: number = 0; ui < unitEvent.unitSections.length; ui++) {
|
|
624
|
-
if (isSectionRejected(rejectedSet, mi, ui)) {
|
|
625
|
-
props.sectionWriteProgress.total++;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// Write sections, skipping approved ones on retry
|
|
631
|
-
const sectionResults: AutoBeAnalyzeWriteSectionEvent[][] = [];
|
|
632
|
-
for (
|
|
633
|
-
let moduleIndex: number = 0;
|
|
634
|
-
moduleIndex < unitResults.length;
|
|
635
|
-
moduleIndex++
|
|
636
|
-
) {
|
|
637
|
-
const unitEvent: AutoBeAnalyzeWriteUnitEvent =
|
|
638
|
-
unitResults[moduleIndex]!;
|
|
639
|
-
const sectionsForModule: AutoBeAnalyzeWriteSectionEvent[] = [];
|
|
640
|
-
|
|
641
|
-
for (
|
|
642
|
-
let unitIndex: number = 0;
|
|
643
|
-
unitIndex < unitEvent.unitSections.length;
|
|
644
|
-
unitIndex++
|
|
645
|
-
) {
|
|
646
|
-
if (isSectionRejected(rejectedSet, moduleIndex, unitIndex)) {
|
|
647
|
-
const sectionStart: number = Date.now();
|
|
648
|
-
// Regenerate this section with targeted feedback
|
|
649
|
-
const targetedInfo: ISectionAwareFeedback | undefined =
|
|
650
|
-
feedbackMap.get(`${moduleIndex}:${unitIndex}`);
|
|
651
|
-
const targetedFeedback: string | undefined =
|
|
652
|
-
targetedInfo?.feedback ?? state.sectionFeedback;
|
|
653
|
-
const targetedSectionIndices: number[] | null =
|
|
654
|
-
targetedInfo?.sectionIndices ?? null;
|
|
655
|
-
analyzeDebug(
|
|
656
|
-
`section unit-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} targetSections=${targetedSectionIndices ? `[${targetedSectionIndices.join(",")}]` : "all"}`,
|
|
657
|
-
);
|
|
658
|
-
const previousSection:
|
|
659
|
-
| AutoBeAnalyzeWriteSectionEvent
|
|
660
|
-
| undefined =
|
|
661
|
-
state.sectionResults?.[moduleIndex]?.[unitIndex];
|
|
662
|
-
let sectionEvent: AutoBeAnalyzeWriteSectionEvent;
|
|
663
|
-
try {
|
|
664
|
-
sectionEvent =
|
|
665
|
-
previousSection && targetedFeedback?.trim()
|
|
666
|
-
? await orchestrateAnalyzeWriteSectionPatch(ctx, {
|
|
667
|
-
scenario: props.scenario,
|
|
668
|
-
file: state.file,
|
|
669
|
-
moduleEvent: moduleResult,
|
|
670
|
-
unitEvent,
|
|
671
|
-
moduleIndex,
|
|
672
|
-
unitIndex,
|
|
673
|
-
previousSectionEvent: previousSection,
|
|
674
|
-
feedback: targetedFeedback,
|
|
675
|
-
progress: props.sectionWriteProgress,
|
|
676
|
-
promptCacheKey: cacheKey,
|
|
677
|
-
retry: attempt,
|
|
678
|
-
scenarioEntityNames,
|
|
679
|
-
sectionIndices: targetedSectionIndices,
|
|
680
|
-
})
|
|
681
|
-
: await orchestrateAnalyzeWriteSection(ctx, {
|
|
682
|
-
scenario: props.scenario,
|
|
683
|
-
file: state.file,
|
|
684
|
-
moduleEvent: moduleResult,
|
|
685
|
-
unitEvent,
|
|
686
|
-
allUnitEvents: unitResults,
|
|
687
|
-
moduleIndex,
|
|
688
|
-
unitIndex,
|
|
689
|
-
progress: props.sectionWriteProgress,
|
|
690
|
-
promptCacheKey: cacheKey,
|
|
691
|
-
feedback: targetedFeedback,
|
|
692
|
-
retry: attempt,
|
|
693
|
-
scenarioEntityNames,
|
|
694
|
-
});
|
|
695
|
-
} catch (e) {
|
|
696
|
-
if (
|
|
697
|
-
e instanceof AgenticaValidationError ||
|
|
698
|
-
e instanceof AutoBePreliminaryExhaustedError ||
|
|
699
|
-
e instanceof AutoBeTimeoutError
|
|
700
|
-
) {
|
|
701
|
-
analyzeDebug(
|
|
702
|
-
`section unit-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} error=${(e as Error).constructor.name} — ${previousSection ? "reusing previous" : "using placeholder"}`,
|
|
703
|
-
);
|
|
704
|
-
if (previousSection) {
|
|
705
|
-
sectionEvent = previousSection;
|
|
706
|
-
} else {
|
|
707
|
-
sectionEvent = {
|
|
708
|
-
type: "analyzeWriteSection",
|
|
709
|
-
id: v7(),
|
|
710
|
-
moduleIndex,
|
|
711
|
-
unitIndex,
|
|
712
|
-
sectionSections: [],
|
|
713
|
-
acquisition: { previousAnalysisSections: [] },
|
|
714
|
-
tokenUsage: {
|
|
715
|
-
total: 0,
|
|
716
|
-
input: { total: 0, cached: 0 },
|
|
717
|
-
output: {
|
|
718
|
-
total: 0,
|
|
719
|
-
reasoning: 0,
|
|
720
|
-
accepted_prediction: 0,
|
|
721
|
-
rejected_prediction: 0,
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
metric: {
|
|
725
|
-
attempt: 0,
|
|
726
|
-
success: 0,
|
|
727
|
-
consent: 0,
|
|
728
|
-
validationFailure: 0,
|
|
729
|
-
invalidJson: 0,
|
|
730
|
-
},
|
|
731
|
-
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
732
|
-
total: props.sectionWriteProgress.total,
|
|
733
|
-
completed: ++props.sectionWriteProgress.completed,
|
|
734
|
-
retry: attempt,
|
|
735
|
-
created_at: new Date().toISOString(),
|
|
736
|
-
};
|
|
737
|
-
ctx.dispatch(sectionEvent);
|
|
738
|
-
}
|
|
739
|
-
} else {
|
|
740
|
-
throw e;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
analyzeDebug(
|
|
744
|
-
`section unit-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} sectionCount=${sectionEvent.sectionSections.length} elapsedMs=${Date.now() - sectionStart}`,
|
|
745
|
-
);
|
|
746
|
-
sectionsForModule.push(sectionEvent);
|
|
747
|
-
} else {
|
|
748
|
-
// Keep existing approved section
|
|
749
|
-
sectionsForModule.push(
|
|
750
|
-
state.sectionResults![moduleIndex]![unitIndex]!,
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
sectionResults.push(sectionsForModule);
|
|
755
|
-
}
|
|
756
|
-
state.sectionResults = sectionResults;
|
|
757
|
-
|
|
758
|
-
analyzeDebug(
|
|
759
|
-
`section file-write-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}"`,
|
|
760
|
-
);
|
|
761
|
-
|
|
762
|
-
// Per-module review immediately after write (removes barrier)
|
|
763
|
-
const reviewStart: number = Date.now();
|
|
764
|
-
analyzeDebug(
|
|
765
|
-
`section per-module-review-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${state.unitResults!.length}`,
|
|
766
|
-
);
|
|
767
|
-
const moduleReviews: AutoBeAnalyzeSectionReviewEvent[] = [];
|
|
768
|
-
for (
|
|
769
|
-
let moduleIndex = 0;
|
|
770
|
-
moduleIndex < state.unitResults!.length;
|
|
771
|
-
moduleIndex++
|
|
772
|
-
) {
|
|
773
|
-
try {
|
|
774
|
-
const moduleReviewEvent = await orchestrateAnalyzeSectionReview(
|
|
775
|
-
ctx,
|
|
776
|
-
{
|
|
777
|
-
scenario: props.scenario,
|
|
778
|
-
fileIndex,
|
|
779
|
-
file: state.file,
|
|
780
|
-
moduleEvent: state.moduleResult!,
|
|
781
|
-
moduleIndex,
|
|
782
|
-
unitEvent: state.unitResults![moduleIndex]!,
|
|
783
|
-
moduleSectionEvents: state.sectionResults![moduleIndex]!,
|
|
784
|
-
siblingModuleSummaries: buildSiblingModuleSummaries(
|
|
785
|
-
state.moduleResult!,
|
|
786
|
-
state.sectionResults!,
|
|
787
|
-
moduleIndex,
|
|
788
|
-
),
|
|
789
|
-
feedback: state.sectionFeedback,
|
|
790
|
-
progress: props.perFileSectionReviewProgress,
|
|
791
|
-
promptCacheKey: cacheKey,
|
|
792
|
-
retry: attempt,
|
|
793
|
-
},
|
|
794
|
-
);
|
|
795
|
-
moduleReviews.push(moduleReviewEvent);
|
|
796
|
-
} catch (e) {
|
|
797
|
-
if (
|
|
798
|
-
e instanceof AgenticaValidationError ||
|
|
799
|
-
e instanceof AutoBeTimeoutError ||
|
|
800
|
-
e instanceof AutoBePreliminaryExhaustedError
|
|
801
|
-
) {
|
|
802
|
-
analyzeDebug(
|
|
803
|
-
`section per-module-review-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} — skipping this module review`,
|
|
804
|
-
);
|
|
805
|
-
} else {
|
|
806
|
-
throw e;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
const reviewEvent = mergeModuleReviewEvents(moduleReviews, fileIndex);
|
|
811
|
-
analyzeDebug(
|
|
812
|
-
`section per-module-review-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${moduleReviews.length} elapsedMs=${Date.now() - reviewStart}`,
|
|
813
|
-
);
|
|
814
|
-
perFileReviewResults.set(fileIndex, reviewEvent!);
|
|
815
|
-
|
|
816
|
-
return sectionResults;
|
|
817
|
-
}),
|
|
818
|
-
promptCacheKey,
|
|
819
|
-
);
|
|
820
|
-
|
|
821
|
-
// Pass 2: Cross-file lightweight review (single call)
|
|
822
|
-
crossFileReviewCount++;
|
|
823
|
-
if (crossFileReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
|
|
824
|
-
analyzeDebug(
|
|
825
|
-
`[orchestrateAnalyze] Section stage: skipping cross-file review (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded)`,
|
|
826
|
-
);
|
|
827
|
-
// Force-pass all pending files
|
|
828
|
-
for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
|
|
829
|
-
break;
|
|
830
|
-
}
|
|
831
|
-
analyzeDebug(`section cross-file-validation-start attempt=${attempt}`);
|
|
832
|
-
const filesWithSections = props.fileStates
|
|
833
|
-
.filter((state) => state.sectionResults !== null)
|
|
834
|
-
.map((state) => ({
|
|
835
|
-
file: state.file,
|
|
836
|
-
sectionEvents: state.sectionResults!,
|
|
837
|
-
}));
|
|
838
|
-
|
|
839
|
-
// Pass 2a: Programmatic cross-file validation (BEFORE LLM review)
|
|
840
|
-
const criticalConflicts = detectConstraintConflicts({
|
|
841
|
-
files: filesWithSections,
|
|
842
|
-
});
|
|
843
|
-
const fileConflictMap: Map<string, string[]> =
|
|
844
|
-
buildFileConflictMap(criticalConflicts);
|
|
845
|
-
|
|
846
|
-
const attributeDuplicates = detectAttributeDuplicates({
|
|
847
|
-
files: filesWithSections,
|
|
848
|
-
});
|
|
849
|
-
const fileAttributeDuplicateMap: Map<string, string[]> =
|
|
850
|
-
buildFileAttributeDuplicateMap(attributeDuplicates);
|
|
851
|
-
|
|
852
|
-
const enumConflicts = detectEnumConflicts({
|
|
853
|
-
files: filesWithSections,
|
|
854
|
-
});
|
|
855
|
-
const fileEnumConflictMap: Map<string, string[]> =
|
|
856
|
-
buildFileEnumConflictMap(enumConflicts);
|
|
857
|
-
|
|
858
|
-
const permissionConflicts = detectPermissionConflicts({
|
|
859
|
-
files: filesWithSections,
|
|
860
|
-
});
|
|
861
|
-
const filePermissionConflictMap: Map<string, string[]> =
|
|
862
|
-
buildFilePermissionConflictMap(permissionConflicts);
|
|
863
|
-
|
|
864
|
-
const stateFieldConflicts = detectStateFieldConflicts({
|
|
865
|
-
files: filesWithSections,
|
|
866
|
-
});
|
|
867
|
-
const fileStateFieldConflictMap: Map<string, string[]> =
|
|
868
|
-
buildFileStateFieldConflictMap(stateFieldConflicts);
|
|
869
|
-
|
|
870
|
-
const errorCodeConflicts = detectErrorCodeConflicts({
|
|
871
|
-
files: filesWithSections,
|
|
872
|
-
});
|
|
873
|
-
const fileErrorCodeConflictMap: Map<string, string[]> =
|
|
874
|
-
buildFileErrorCodeConflictMap(errorCodeConflicts);
|
|
875
|
-
|
|
876
|
-
const proseConflicts = detectProseConstraintConflicts({
|
|
877
|
-
files: filesWithSections,
|
|
878
|
-
});
|
|
879
|
-
const fileProseConflictMap: Map<string, string[]> =
|
|
880
|
-
buildFileProseConflictMap(proseConflicts);
|
|
881
|
-
|
|
882
|
-
const oversizedTocMap: Map<number, string[]> = new Map();
|
|
883
|
-
for (const fileIndex of pendingArray) {
|
|
884
|
-
const state = props.fileStates[fileIndex]!;
|
|
885
|
-
if (state.file.filename === "00-toc.md" && state.sectionResults) {
|
|
886
|
-
const violations = detectOversizedToc(state.sectionResults);
|
|
887
|
-
if (violations.length > 0) {
|
|
888
|
-
oversizedTocMap.set(fileIndex, violations);
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
// Build mechanical violation summary for LLM context
|
|
894
|
-
const allMechanicalViolations: string[] = [
|
|
895
|
-
...criticalConflicts.map(
|
|
896
|
-
(c) =>
|
|
897
|
-
`Constraint conflict: ${c.key} — ${c.values.map((v) => `"${v.display}" in [${v.files.join(", ")}]`).join(" vs ")}`,
|
|
898
|
-
),
|
|
899
|
-
...attributeDuplicates.map(
|
|
900
|
-
(d) => `Attribute duplication: ${d.key} in [${d.files.join(", ")}]`,
|
|
901
|
-
),
|
|
902
|
-
...enumConflicts.map(
|
|
903
|
-
(c) =>
|
|
904
|
-
`Enum conflict: ${c.key} — ${c.values.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`).join(" vs ")}`,
|
|
905
|
-
),
|
|
906
|
-
...errorCodeConflicts.map(
|
|
907
|
-
(c) =>
|
|
908
|
-
`Error code conflict: ${c.conditionKey} — ${c.codes.map((cd) => `HTTP ${cd.httpStatus} in [${cd.files.join(", ")}]`).join(" vs ")}`,
|
|
909
|
-
),
|
|
910
|
-
...proseConflicts.map(
|
|
911
|
-
(c) =>
|
|
912
|
-
`Prose constraint conflict: ${c.entityAttr} — canonical [${c.canonicalValues.join(", ")}] vs prose [${c.proseValues.join(", ")}] in ${c.file}`,
|
|
913
|
-
),
|
|
914
|
-
];
|
|
915
|
-
const mechanicalViolationSummary =
|
|
916
|
-
allMechanicalViolations.length > 0
|
|
917
|
-
? allMechanicalViolations.join("\n")
|
|
918
|
-
: undefined;
|
|
919
|
-
|
|
920
|
-
// Pass 2b: Cross-file semantic LLM review (with mechanical violations excluded)
|
|
921
|
-
analyzeDebug(`section cross-file-review-start attempt=${attempt}`);
|
|
922
|
-
let crossFileReviewEvent: AutoBeAnalyzeSectionReviewEvent | null = null;
|
|
923
|
-
try {
|
|
924
|
-
crossFileReviewEvent = await orchestrateAnalyzeSectionCrossFileReview(
|
|
925
|
-
ctx,
|
|
926
|
-
{
|
|
927
|
-
scenario: props.scenario,
|
|
928
|
-
allFileSummaries: props.fileStates
|
|
929
|
-
.filter((s) => s.file.filename !== "00-toc.md")
|
|
930
|
-
.map((state) => {
|
|
931
|
-
const fi = props.fileStates.indexOf(state);
|
|
932
|
-
return {
|
|
933
|
-
file: state.file,
|
|
934
|
-
moduleEvent: state.moduleResult!,
|
|
935
|
-
unitEvents: state.unitResults!,
|
|
936
|
-
sectionEvents: state.sectionResults!,
|
|
937
|
-
status: pendingIndices.has(fi)
|
|
938
|
-
? attempt === 0
|
|
939
|
-
? ("new" as const)
|
|
940
|
-
: ("rewritten" as const)
|
|
941
|
-
: ("approved" as const),
|
|
942
|
-
};
|
|
943
|
-
}),
|
|
944
|
-
mechanicalViolationSummary,
|
|
945
|
-
progress: props.crossFileSectionReviewProgress,
|
|
946
|
-
promptCacheKey,
|
|
947
|
-
retry: attempt,
|
|
948
|
-
},
|
|
949
|
-
);
|
|
950
|
-
} catch (e) {
|
|
951
|
-
if (
|
|
952
|
-
e instanceof AgenticaValidationError ||
|
|
953
|
-
e instanceof AutoBeTimeoutError ||
|
|
954
|
-
e instanceof AutoBePreliminaryExhaustedError
|
|
955
|
-
) {
|
|
956
|
-
analyzeDebug(
|
|
957
|
-
`section cross-file-review-force-pass attempt=${attempt} error=${(e as Error).constructor.name} — force-passing all pending files`,
|
|
958
|
-
);
|
|
959
|
-
for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
|
|
960
|
-
break;
|
|
961
|
-
}
|
|
962
|
-
throw e;
|
|
963
|
-
}
|
|
964
|
-
analyzeDebug(
|
|
965
|
-
`section cross-file-review-done attempt=${attempt} results=${crossFileReviewEvent.fileResults.length}`,
|
|
966
|
-
);
|
|
967
|
-
|
|
968
|
-
// Merge results from both passes
|
|
969
|
-
const crossFileResultMap: Map<
|
|
970
|
-
number,
|
|
971
|
-
AutoBeAnalyzeSectionReviewFileResult
|
|
972
|
-
> = new Map();
|
|
973
|
-
const validCrossFileResults = filterValidFileResults(
|
|
974
|
-
crossFileReviewEvent.fileResults,
|
|
975
|
-
props.fileStates.length,
|
|
976
|
-
"Section cross-file review",
|
|
977
|
-
);
|
|
978
|
-
for (const fr of validCrossFileResults)
|
|
979
|
-
crossFileResultMap.set(fr.fileIndex, fr);
|
|
980
|
-
|
|
981
|
-
for (const fileIndex of pendingArray) {
|
|
982
|
-
const state: IFileState = props.fileStates[fileIndex]!;
|
|
983
|
-
|
|
984
|
-
// Increment review count and force-pass if exceeded limit
|
|
985
|
-
state.sectionReviewCount = (state.sectionReviewCount ?? 0) + 1;
|
|
986
|
-
if (state.sectionReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
|
|
987
|
-
analyzeDebug(
|
|
988
|
-
`[orchestrateAnalyze] Section stage: force-passing (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded) for file "${state.file.filename}"`,
|
|
989
|
-
);
|
|
990
|
-
pendingIndices.delete(fileIndex);
|
|
991
|
-
continue;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
const perFileEvent = perFileReviewResults.get(fileIndex);
|
|
995
|
-
const perFileResult = perFileEvent?.fileResults[0];
|
|
996
|
-
const crossFileResult = crossFileResultMap.get(fileIndex);
|
|
997
|
-
|
|
998
|
-
const perFileApproved = perFileResult?.approved ?? true;
|
|
999
|
-
const crossFileApproved = crossFileResult?.approved ?? true;
|
|
1000
|
-
|
|
1001
|
-
// Check if this file has programmatically-detected critical conflicts
|
|
1002
|
-
const filename = state.file.filename;
|
|
1003
|
-
const fileCriticalConflicts = fileConflictMap.get(filename) ?? [];
|
|
1004
|
-
const fileAttrDuplicates = fileAttributeDuplicateMap.get(filename) ?? [];
|
|
1005
|
-
const fileEnumConflicts = fileEnumConflictMap.get(filename) ?? [];
|
|
1006
|
-
const filePermissionConflicts =
|
|
1007
|
-
filePermissionConflictMap.get(filename) ?? [];
|
|
1008
|
-
const fileStateFieldConflicts =
|
|
1009
|
-
fileStateFieldConflictMap.get(filename) ?? [];
|
|
1010
|
-
const fileErrorCodeConflicts =
|
|
1011
|
-
fileErrorCodeConflictMap.get(filename) ?? [];
|
|
1012
|
-
const fileOversizedToc = oversizedTocMap.get(fileIndex) ?? [];
|
|
1013
|
-
const fileProseConflicts = fileProseConflictMap.get(filename) ?? [];
|
|
1014
|
-
const hasCriticalConflict =
|
|
1015
|
-
fileCriticalConflicts.length > 0 ||
|
|
1016
|
-
fileAttrDuplicates.length > 0 ||
|
|
1017
|
-
fileEnumConflicts.length > 0 ||
|
|
1018
|
-
filePermissionConflicts.length > 0 ||
|
|
1019
|
-
fileStateFieldConflicts.length > 0 ||
|
|
1020
|
-
fileErrorCodeConflicts.length > 0 ||
|
|
1021
|
-
fileOversizedToc.length > 0 ||
|
|
1022
|
-
fileProseConflicts.length > 0;
|
|
1023
|
-
|
|
1024
|
-
// Decision logic:
|
|
1025
|
-
// 1. per-file reject → reject (unchanged)
|
|
1026
|
-
// 2. per-file approve + critical conflict detected → reject (NEW: patch-first)
|
|
1027
|
-
// 3. per-file approve + no critical conflict → approve (unchanged)
|
|
1028
|
-
const approved = perFileApproved && !hasCriticalConflict;
|
|
1029
|
-
|
|
1030
|
-
const structuredPerFileIssues =
|
|
1031
|
-
collectStructuredReviewIssues(perFileResult);
|
|
1032
|
-
const structuredCrossFileIssues =
|
|
1033
|
-
collectStructuredReviewIssues(crossFileResult);
|
|
1034
|
-
const programmaticIssues = buildProgrammaticSectionIssues({
|
|
1035
|
-
fileCriticalConflicts,
|
|
1036
|
-
fileAttrDuplicates,
|
|
1037
|
-
fileEnumConflicts,
|
|
1038
|
-
filePermissionConflicts,
|
|
1039
|
-
fileStateFieldConflicts,
|
|
1040
|
-
fileErrorCodeConflicts,
|
|
1041
|
-
fileOversizedToc,
|
|
1042
|
-
fileProseConflicts,
|
|
1043
|
-
});
|
|
1044
|
-
|
|
1045
|
-
if (approved) {
|
|
1046
|
-
// NOTE: revisedSections intentionally ignored — approved means pass as-is.
|
|
1047
|
-
// Applying revisedSections caused infinite re-write loops (sections kept growing).
|
|
1048
|
-
// Pass cross-file feedback as advisory for next retry's context
|
|
1049
|
-
if (!crossFileApproved && crossFileResult?.feedback) {
|
|
1050
|
-
state.sectionFeedback = `[Cross-file advisory] ${crossFileResult.feedback}`;
|
|
1051
|
-
}
|
|
1052
|
-
state.sectionRetryCount = 0;
|
|
1053
|
-
state.sectionStagnationCount = 0;
|
|
1054
|
-
state.lastSectionContentSignature = undefined;
|
|
1055
|
-
state.lastSectionRejectionSignature = undefined;
|
|
1056
|
-
pendingIndices.delete(fileIndex);
|
|
1057
|
-
} else if (!perFileApproved) {
|
|
1058
|
-
// Per-file rejected: store only the latest per-file feedback (no accumulation)
|
|
1059
|
-
state.sectionFeedback = formatStructuredIssuesForRetry({
|
|
1060
|
-
fallbackFeedback: perFileResult?.feedback ?? "",
|
|
1061
|
-
issues: structuredPerFileIssues,
|
|
1062
|
-
});
|
|
1063
|
-
|
|
1064
|
-
// Use only per-file rejectedModuleUnits (no cross-file merge)
|
|
1065
|
-
state.rejectedModuleUnits = normalizeRejectedModuleUnits(
|
|
1066
|
-
perFileResult?.rejectedModuleUnits ?? null,
|
|
1067
|
-
structuredPerFileIssues,
|
|
1068
|
-
);
|
|
1069
|
-
// Fallback: infer targets from issues to avoid full-file rewrite
|
|
1070
|
-
if (state.rejectedModuleUnits === null) {
|
|
1071
|
-
state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
|
|
1072
|
-
structuredPerFileIssues,
|
|
1073
|
-
state.unitResults!,
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
analyzeDebug(
|
|
1077
|
-
`section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
|
|
1078
|
-
state.rejectedModuleUnits,
|
|
1079
|
-
)} issues=${formatReviewIssuesSummary(structuredPerFileIssues)} feedback=${truncateForDebug(
|
|
1080
|
-
state.sectionFeedback ?? "",
|
|
1081
|
-
500,
|
|
1082
|
-
)}`,
|
|
1083
|
-
);
|
|
1084
|
-
} else {
|
|
1085
|
-
// Critical conflict rejected (per-file approved but programmatic violations exist)
|
|
1086
|
-
// Use cross-file rejectedModuleUnits for targeted patch if available
|
|
1087
|
-
state.sectionFeedback = formatStructuredIssuesForRetry({
|
|
1088
|
-
fallbackFeedback:
|
|
1089
|
-
`[Critical conflict] ${[
|
|
1090
|
-
...fileCriticalConflicts,
|
|
1091
|
-
...fileAttrDuplicates,
|
|
1092
|
-
...fileEnumConflicts,
|
|
1093
|
-
...fileProseConflicts,
|
|
1094
|
-
].join("; ")}` +
|
|
1095
|
-
(crossFileResult?.feedback ? `\n${crossFileResult.feedback}` : ""),
|
|
1096
|
-
issues: [...programmaticIssues, ...structuredCrossFileIssues],
|
|
1097
|
-
});
|
|
1098
|
-
state.rejectedModuleUnits = normalizeRejectedModuleUnits(
|
|
1099
|
-
crossFileResult?.rejectedModuleUnits ?? null,
|
|
1100
|
-
[...programmaticIssues, ...structuredCrossFileIssues],
|
|
1101
|
-
);
|
|
1102
|
-
// Fallback: infer targets from issues to avoid full-file rewrite
|
|
1103
|
-
if (state.rejectedModuleUnits === null) {
|
|
1104
|
-
state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
|
|
1105
|
-
[...programmaticIssues, ...structuredCrossFileIssues],
|
|
1106
|
-
state.unitResults!,
|
|
1107
|
-
);
|
|
1108
|
-
}
|
|
1109
|
-
analyzeDebug(
|
|
1110
|
-
`section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
|
|
1111
|
-
state.rejectedModuleUnits,
|
|
1112
|
-
)} issues=${formatReviewIssuesSummary([
|
|
1113
|
-
...programmaticIssues,
|
|
1114
|
-
...structuredCrossFileIssues,
|
|
1115
|
-
])} feedback=${truncateForDebug(state.sectionFeedback ?? "", 500)}`,
|
|
1116
|
-
);
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
if (!approved) {
|
|
1120
|
-
const contentSignature = buildSectionContentSignature(state);
|
|
1121
|
-
const rejectionSignature = buildSectionRejectionSignature({
|
|
1122
|
-
rejectedModuleUnits: state.rejectedModuleUnits ?? null,
|
|
1123
|
-
feedback: state.sectionFeedback ?? "",
|
|
1124
|
-
});
|
|
1125
|
-
const isStagnant =
|
|
1126
|
-
state.lastSectionContentSignature === contentSignature &&
|
|
1127
|
-
state.lastSectionRejectionSignature === rejectionSignature;
|
|
1128
|
-
state.sectionStagnationCount = isStagnant
|
|
1129
|
-
? (state.sectionStagnationCount ?? 0) + 1
|
|
1130
|
-
: 0;
|
|
1131
|
-
state.sectionRetryCount = (state.sectionRetryCount ?? 0) + 1;
|
|
1132
|
-
state.lastSectionContentSignature = contentSignature;
|
|
1133
|
-
state.lastSectionRejectionSignature = rejectionSignature;
|
|
1134
|
-
|
|
1135
|
-
if ((state.sectionRetryCount ?? 0) > ANALYZE_SECTION_FILE_MAX_RETRY) {
|
|
1136
|
-
analyzeDebug(
|
|
1137
|
-
`[orchestrateAnalyze] Section stage: force-passing (max retry exceeded: ${ANALYZE_SECTION_FILE_MAX_RETRY}) for file "${state.file.filename}"`,
|
|
1138
|
-
);
|
|
1139
|
-
pendingIndices.delete(fileIndex);
|
|
1140
|
-
continue;
|
|
1141
|
-
}
|
|
1142
|
-
if (
|
|
1143
|
-
(state.sectionStagnationCount ?? 0) >= ANALYZE_SECTION_STAGNATION_MAX
|
|
1144
|
-
) {
|
|
1145
|
-
analyzeDebug(
|
|
1146
|
-
`[orchestrateAnalyze] Section stage: force-passing (stagnation detected ${state.sectionStagnationCount}x) for file "${state.file.filename}"`,
|
|
1147
|
-
);
|
|
1148
|
-
pendingIndices.delete(fileIndex);
|
|
1149
|
-
continue;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
if (pendingIndices.size > 0) {
|
|
1156
|
-
analyzeDebug(
|
|
1157
|
-
`[orchestrateAnalyze] Section stage: force-passing after max retries for files: ${[
|
|
1158
|
-
...pendingIndices,
|
|
1159
|
-
]
|
|
1160
|
-
.map((i) => props.fileStates[i]!.file.filename)
|
|
1161
|
-
.join(", ")}`,
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
// ─── Section-stage helper functions ───
|
|
1167
|
-
|
|
1168
|
-
function computeSectionBatchSize(props: {
|
|
1169
|
-
attempt: number;
|
|
1170
|
-
pendingCount: number;
|
|
1171
|
-
}): number {
|
|
1172
|
-
return Math.min(8, props.pendingCount);
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
function chunkSectionFileIndices(indices: number[], size: number): number[][] {
|
|
1176
|
-
if (indices.length === 0) return [];
|
|
1177
|
-
if (size <= 0 || size >= indices.length) return [indices];
|
|
1178
|
-
const chunks: number[][] = [];
|
|
1179
|
-
for (let i = 0; i < indices.length; i += size)
|
|
1180
|
-
chunks.push(indices.slice(i, i + size));
|
|
1181
|
-
return chunks;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function buildRejectedSet(
|
|
1185
|
-
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1186
|
-
): Set<string> | null {
|
|
1187
|
-
if (rejected == null) return null;
|
|
1188
|
-
if (rejected.length === 0) return null;
|
|
1189
|
-
const set: Set<string> = new Set();
|
|
1190
|
-
for (const entry of rejected) {
|
|
1191
|
-
for (const ui of entry.unitIndices) {
|
|
1192
|
-
set.add(`${entry.moduleIndex}:${ui}`);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
return set.size > 0 ? set : null;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
interface ISectionAwareFeedback {
|
|
1199
|
-
feedback: string;
|
|
1200
|
-
sectionIndices: number[] | null;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
function buildFeedbackMap(
|
|
1204
|
-
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1205
|
-
): Map<string, ISectionAwareFeedback> {
|
|
1206
|
-
const map: Map<string, ISectionAwareFeedback> = new Map();
|
|
1207
|
-
if (rejected == null) return map;
|
|
1208
|
-
for (const entry of rejected) {
|
|
1209
|
-
for (const ui of entry.unitIndices) {
|
|
1210
|
-
map.set(`${entry.moduleIndex}:${ui}`, {
|
|
1211
|
-
feedback: formatRejectedModuleUnitFeedback(entry, ui),
|
|
1212
|
-
sectionIndices: entry.sectionIndicesPerUnit?.[ui] ?? null,
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
return map;
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
function isSectionRejected(
|
|
1220
|
-
rejectedSet: Set<string> | null,
|
|
1221
|
-
moduleIndex: number,
|
|
1222
|
-
unitIndex: number,
|
|
1223
|
-
): boolean {
|
|
1224
|
-
if (rejectedSet === null) return true;
|
|
1225
|
-
return rejectedSet.has(`${moduleIndex}:${unitIndex}`);
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
function filterValidFileResults<T extends { fileIndex: number }>(
|
|
1229
|
-
fileResults: T[],
|
|
1230
|
-
fileCount: number,
|
|
1231
|
-
stage: string,
|
|
1232
|
-
): T[] {
|
|
1233
|
-
return fileResults.filter((fr) => {
|
|
1234
|
-
if (
|
|
1235
|
-
Number.isInteger(fr.fileIndex) &&
|
|
1236
|
-
fr.fileIndex >= 0 &&
|
|
1237
|
-
fr.fileIndex < fileCount
|
|
1238
|
-
) {
|
|
1239
|
-
return true;
|
|
1240
|
-
}
|
|
1241
|
-
console.warn(
|
|
1242
|
-
`[orchestrateAnalyze] ${stage}: invalid fileIndex ${fr.fileIndex} (valid: 0-${fileCount - 1})`,
|
|
1243
|
-
);
|
|
1244
|
-
return false;
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
function formatRejectedModuleUnitFeedback(
|
|
1249
|
-
entry: AutoBeAnalyzeSectionReviewRejectedModuleUnit,
|
|
1250
|
-
unitIndex: number,
|
|
1251
|
-
): string {
|
|
1252
|
-
const scopedIssues = (entry.issues ?? []).filter(
|
|
1253
|
-
(issue) =>
|
|
1254
|
-
issue.moduleIndex === entry.moduleIndex &&
|
|
1255
|
-
(issue.unitIndex === null || issue.unitIndex === unitIndex),
|
|
1256
|
-
);
|
|
1257
|
-
if (scopedIssues.length === 0) return entry.feedback;
|
|
1258
|
-
return [
|
|
1259
|
-
entry.feedback,
|
|
1260
|
-
...scopedIssues.map(
|
|
1261
|
-
(issue) =>
|
|
1262
|
-
`- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}`,
|
|
1263
|
-
),
|
|
1264
|
-
].join("\n");
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
function collectStructuredReviewIssues(
|
|
1268
|
-
result:
|
|
1269
|
-
| {
|
|
1270
|
-
feedback: string;
|
|
1271
|
-
rejectedModuleUnits?:
|
|
1272
|
-
| AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
|
|
1273
|
-
| null;
|
|
1274
|
-
issues?: AutoBeAnalyzeSectionReviewIssue[] | null;
|
|
1275
|
-
}
|
|
1276
|
-
| undefined,
|
|
1277
|
-
): AutoBeAnalyzeSectionReviewIssue[] {
|
|
1278
|
-
if (!result) return [];
|
|
1279
|
-
const collected: AutoBeAnalyzeSectionReviewIssue[] = [];
|
|
1280
|
-
|
|
1281
|
-
for (const issue of result.issues ?? []) collected.push(issue);
|
|
1282
|
-
for (const group of result.rejectedModuleUnits ?? []) {
|
|
1283
|
-
for (const issue of group.issues ?? []) collected.push(issue);
|
|
1284
|
-
if ((group.issues?.length ?? 0) === 0) {
|
|
1285
|
-
for (const unitIndex of group.unitIndices) {
|
|
1286
|
-
collected.push({
|
|
1287
|
-
ruleCode: "section_review_reject",
|
|
1288
|
-
moduleIndex: group.moduleIndex,
|
|
1289
|
-
unitIndex,
|
|
1290
|
-
fixInstruction:
|
|
1291
|
-
group.feedback || result.feedback || "Fix review issues.",
|
|
1292
|
-
evidence: null,
|
|
1293
|
-
});
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
if (collected.length === 0 && result.feedback.trim().length > 0) {
|
|
1299
|
-
collected.push({
|
|
1300
|
-
ruleCode: "section_review_reject",
|
|
1301
|
-
moduleIndex: null,
|
|
1302
|
-
unitIndex: null,
|
|
1303
|
-
fixInstruction: result.feedback,
|
|
1304
|
-
evidence: null,
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
return dedupeReviewIssues(collected);
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
function buildProgrammaticSectionIssues(props: {
|
|
1311
|
-
fileCriticalConflicts: string[];
|
|
1312
|
-
fileAttrDuplicates: string[];
|
|
1313
|
-
fileEnumConflicts: string[];
|
|
1314
|
-
filePermissionConflicts: string[];
|
|
1315
|
-
fileStateFieldConflicts: string[];
|
|
1316
|
-
fileErrorCodeConflicts: string[];
|
|
1317
|
-
fileOversizedToc: string[];
|
|
1318
|
-
fileProseConflicts: string[];
|
|
1319
|
-
}): AutoBeAnalyzeSectionReviewIssue[] {
|
|
1320
|
-
return [
|
|
1321
|
-
...props.fileCriticalConflicts.map((detail) => ({
|
|
1322
|
-
ruleCode: "cross_file_constraint_conflict",
|
|
1323
|
-
moduleIndex: null,
|
|
1324
|
-
unitIndex: null,
|
|
1325
|
-
fixInstruction:
|
|
1326
|
-
"Align conflicting constraints/values with other files and preserve one canonical value.",
|
|
1327
|
-
evidence: detail,
|
|
1328
|
-
})),
|
|
1329
|
-
...props.fileAttrDuplicates.map((detail) => ({
|
|
1330
|
-
ruleCode: "cross_file_attribute_duplicate",
|
|
1331
|
-
moduleIndex: null,
|
|
1332
|
-
unitIndex: null,
|
|
1333
|
-
fixInstruction:
|
|
1334
|
-
"Remove duplicate attribute specifications across files and keep ownership in one file.",
|
|
1335
|
-
evidence: detail,
|
|
1336
|
-
})),
|
|
1337
|
-
...props.fileEnumConflicts.map((detail) => ({
|
|
1338
|
-
ruleCode: "cross_file_enum_conflict",
|
|
1339
|
-
moduleIndex: null,
|
|
1340
|
-
unitIndex: null,
|
|
1341
|
-
fixInstruction:
|
|
1342
|
-
"Align enum values with the canonical definition from the first file that specified this attribute. Use the exact same enum set.",
|
|
1343
|
-
evidence: detail,
|
|
1344
|
-
})),
|
|
1345
|
-
...props.filePermissionConflicts.map((detail) => ({
|
|
1346
|
-
ruleCode: "cross_file_permission_conflict",
|
|
1347
|
-
moduleIndex: null,
|
|
1348
|
-
unitIndex: null,
|
|
1349
|
-
fixInstruction:
|
|
1350
|
-
"Align permission rules with the canonical definition. If the first file says 'denied', all files must say 'denied' for the same actor→operation.",
|
|
1351
|
-
evidence: detail,
|
|
1352
|
-
})),
|
|
1353
|
-
...props.fileStateFieldConflicts.map((detail) => ({
|
|
1354
|
-
ruleCode: "cross_file_state_field_conflict",
|
|
1355
|
-
moduleIndex: null,
|
|
1356
|
-
unitIndex: null,
|
|
1357
|
-
fixInstruction:
|
|
1358
|
-
"Use ONE canonical approach for state fields. If other files use 'deletedAt: datetime', do NOT use 'isDeleted: boolean'. Pick one and align.",
|
|
1359
|
-
evidence: detail,
|
|
1360
|
-
})),
|
|
1361
|
-
...props.fileErrorCodeConflicts.map((detail) => ({
|
|
1362
|
-
ruleCode: "cross_file_error_code_conflict",
|
|
1363
|
-
moduleIndex: null,
|
|
1364
|
-
unitIndex: null,
|
|
1365
|
-
fixInstruction:
|
|
1366
|
-
"Use the canonical error code defined in the first file. Do NOT invent alternative error codes for the same condition.",
|
|
1367
|
-
evidence: detail,
|
|
1368
|
-
})),
|
|
1369
|
-
...props.fileOversizedToc.map((detail) => ({
|
|
1370
|
-
ruleCode: "oversized_toc",
|
|
1371
|
-
moduleIndex: null,
|
|
1372
|
-
unitIndex: null,
|
|
1373
|
-
fixInstruction:
|
|
1374
|
-
"TOC must be a concise navigation aid. Remove detailed requirements, keep only navigation tables and brief summaries.",
|
|
1375
|
-
evidence: detail,
|
|
1376
|
-
})),
|
|
1377
|
-
...props.fileProseConflicts.map((detail) => ({
|
|
1378
|
-
ruleCode: "cross_file_prose_constraint_conflict",
|
|
1379
|
-
moduleIndex: null,
|
|
1380
|
-
unitIndex: null,
|
|
1381
|
-
fixInstruction:
|
|
1382
|
-
"Remove the restated constraint value and use a backtick reference to the canonical definition in 02-domain-model instead. Example: 'THE system SHALL validate `User.bio` per entity constraints (see 02-domain-model)'",
|
|
1383
|
-
evidence: detail,
|
|
1384
|
-
})),
|
|
1385
|
-
];
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
function buildSectionIndicesPerUnit(
|
|
1389
|
-
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1390
|
-
moduleIndex: number,
|
|
1391
|
-
unitIndices: number[],
|
|
1392
|
-
): Record<number, number[]> | null {
|
|
1393
|
-
const map: Record<number, Set<number>> = {};
|
|
1394
|
-
let hasSectionLevel = false;
|
|
1395
|
-
|
|
1396
|
-
for (const issue of issues) {
|
|
1397
|
-
if (
|
|
1398
|
-
issue.moduleIndex === moduleIndex &&
|
|
1399
|
-
issue.unitIndex !== null &&
|
|
1400
|
-
unitIndices.includes(issue.unitIndex) &&
|
|
1401
|
-
issue.sectionIndex !== null &&
|
|
1402
|
-
issue.sectionIndex !== undefined &&
|
|
1403
|
-
Number.isInteger(issue.sectionIndex) &&
|
|
1404
|
-
issue.sectionIndex >= 0
|
|
1405
|
-
) {
|
|
1406
|
-
if (!map[issue.unitIndex]) map[issue.unitIndex] = new Set();
|
|
1407
|
-
map[issue.unitIndex]!.add(issue.sectionIndex);
|
|
1408
|
-
hasSectionLevel = true;
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
if (!hasSectionLevel) return null;
|
|
1413
|
-
|
|
1414
|
-
const result: Record<number, number[]> = {};
|
|
1415
|
-
for (const [ui, sectionSet] of Object.entries(map)) {
|
|
1416
|
-
result[Number(ui)] = [...sectionSet].sort((a, b) => a - b);
|
|
1417
|
-
}
|
|
1418
|
-
return result;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
function normalizeRejectedModuleUnits(
|
|
1422
|
-
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1423
|
-
fileIssues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1424
|
-
): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
|
|
1425
|
-
if (rejected == null) return null;
|
|
1426
|
-
return rejected.map((entry) => {
|
|
1427
|
-
const enrichedIssues =
|
|
1428
|
-
(entry.issues?.length ?? 0) > 0
|
|
1429
|
-
? dedupeReviewIssues(entry.issues ?? [])
|
|
1430
|
-
: dedupeReviewIssues(
|
|
1431
|
-
fileIssues.filter(
|
|
1432
|
-
(issue) =>
|
|
1433
|
-
issue.moduleIndex === entry.moduleIndex &&
|
|
1434
|
-
(issue.unitIndex === null ||
|
|
1435
|
-
entry.unitIndices.includes(issue.unitIndex)),
|
|
1436
|
-
),
|
|
1437
|
-
);
|
|
1438
|
-
|
|
1439
|
-
const sectionIndicesPerUnit =
|
|
1440
|
-
entry.sectionIndicesPerUnit ??
|
|
1441
|
-
buildSectionIndicesPerUnit(
|
|
1442
|
-
enrichedIssues,
|
|
1443
|
-
entry.moduleIndex,
|
|
1444
|
-
entry.unitIndices,
|
|
1445
|
-
);
|
|
1446
|
-
|
|
1447
|
-
return {
|
|
1448
|
-
...entry,
|
|
1449
|
-
issues: enrichedIssues,
|
|
1450
|
-
sectionIndicesPerUnit,
|
|
1451
|
-
};
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
/**
|
|
1456
|
-
* Infer rejectedModuleUnits from structured issues when the LLM review didn't
|
|
1457
|
-
* provide explicit rejection targets. This prevents full-file rewrites when
|
|
1458
|
-
* only specific module/unit pairs have issues.
|
|
1459
|
-
*/
|
|
1460
|
-
function inferRejectedModuleUnitsFromIssues(
|
|
1461
|
-
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1462
|
-
unitResults: AutoBeAnalyzeWriteUnitEvent[],
|
|
1463
|
-
): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
|
|
1464
|
-
const moduleUnitMap = new Map<number, Set<number>>();
|
|
1465
|
-
let hasTargetedIssue = false;
|
|
1466
|
-
|
|
1467
|
-
for (const issue of issues) {
|
|
1468
|
-
if (issue.moduleIndex !== null && issue.moduleIndex !== undefined) {
|
|
1469
|
-
hasTargetedIssue = true;
|
|
1470
|
-
if (!moduleUnitMap.has(issue.moduleIndex)) {
|
|
1471
|
-
moduleUnitMap.set(issue.moduleIndex, new Set());
|
|
1472
|
-
}
|
|
1473
|
-
if (issue.unitIndex !== null && issue.unitIndex !== undefined) {
|
|
1474
|
-
moduleUnitMap.get(issue.moduleIndex)!.add(issue.unitIndex);
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
if (!hasTargetedIssue) return null;
|
|
1480
|
-
|
|
1481
|
-
const result: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] = [];
|
|
1482
|
-
for (const [moduleIndex, unitIndexSet] of moduleUnitMap) {
|
|
1483
|
-
// If no specific units targeted, include all units for this module
|
|
1484
|
-
let unitIndices: number[];
|
|
1485
|
-
if (unitIndexSet.size === 0) {
|
|
1486
|
-
const unitEvent = unitResults[moduleIndex];
|
|
1487
|
-
unitIndices = unitEvent
|
|
1488
|
-
? Array.from({ length: unitEvent.unitSections.length }, (_, i) => i)
|
|
1489
|
-
: [];
|
|
1490
|
-
} else {
|
|
1491
|
-
unitIndices = [...unitIndexSet].sort((a, b) => a - b);
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
const moduleIssues = dedupeReviewIssues(
|
|
1495
|
-
issues.filter(
|
|
1496
|
-
(i) =>
|
|
1497
|
-
i.moduleIndex === moduleIndex &&
|
|
1498
|
-
(i.unitIndex === null || unitIndices.includes(i.unitIndex)),
|
|
1499
|
-
),
|
|
1500
|
-
);
|
|
1501
|
-
|
|
1502
|
-
result.push({
|
|
1503
|
-
moduleIndex,
|
|
1504
|
-
unitIndices,
|
|
1505
|
-
feedback: moduleIssues.map((i) => i.fixInstruction).join("; "),
|
|
1506
|
-
issues: moduleIssues,
|
|
1507
|
-
sectionIndicesPerUnit: buildSectionIndicesPerUnit(
|
|
1508
|
-
moduleIssues,
|
|
1509
|
-
moduleIndex,
|
|
1510
|
-
unitIndices,
|
|
1511
|
-
),
|
|
1512
|
-
});
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
return result.length > 0 ? result : null;
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
function formatStructuredIssuesForRetry(props: {
|
|
1519
|
-
fallbackFeedback: string;
|
|
1520
|
-
issues: AutoBeAnalyzeSectionReviewIssue[];
|
|
1521
|
-
}): string {
|
|
1522
|
-
if (props.issues.length === 0) return props.fallbackFeedback;
|
|
1523
|
-
const lines = props.issues.map(
|
|
1524
|
-
(issue) =>
|
|
1525
|
-
`- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}` +
|
|
1526
|
-
(issue.evidence ? ` | evidence=${issue.evidence}` : ""),
|
|
1527
|
-
);
|
|
1528
|
-
return `${props.fallbackFeedback}\n\n[STRUCTURED REVIEW ISSUES]\n${lines.join("\n")}`.trim();
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
function formatIssueTarget(
|
|
1532
|
-
issue: Pick<AutoBeAnalyzeSectionReviewIssue, "moduleIndex" | "unitIndex"> &
|
|
1533
|
-
Partial<Pick<AutoBeAnalyzeSectionReviewIssue, "sectionIndex">>,
|
|
1534
|
-
): string {
|
|
1535
|
-
const parts: string[] = [];
|
|
1536
|
-
if (issue.moduleIndex !== null && issue.moduleIndex !== undefined)
|
|
1537
|
-
parts.push(`m${issue.moduleIndex}`);
|
|
1538
|
-
if (issue.unitIndex !== null && issue.unitIndex !== undefined)
|
|
1539
|
-
parts.push(`u${issue.unitIndex}`);
|
|
1540
|
-
if (issue.sectionIndex !== null && issue.sectionIndex !== undefined)
|
|
1541
|
-
parts.push(`s${issue.sectionIndex}`);
|
|
1542
|
-
return parts.length ? parts.join(".") : "file";
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
function dedupeReviewIssues(
|
|
1546
|
-
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1547
|
-
): AutoBeAnalyzeSectionReviewIssue[] {
|
|
1548
|
-
const map = new Map<string, AutoBeAnalyzeSectionReviewIssue>();
|
|
1549
|
-
for (const issue of issues) {
|
|
1550
|
-
const key = [
|
|
1551
|
-
issue.ruleCode,
|
|
1552
|
-
issue.moduleIndex ?? "x",
|
|
1553
|
-
issue.unitIndex ?? "x",
|
|
1554
|
-
issue.sectionIndex ?? "x",
|
|
1555
|
-
issue.fixInstruction,
|
|
1556
|
-
].join("|");
|
|
1557
|
-
if (!map.has(key)) map.set(key, issue);
|
|
1558
|
-
}
|
|
1559
|
-
return [...map.values()];
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
// ─── Per-module review helpers ───
|
|
1563
|
-
|
|
1564
|
-
function buildSiblingModuleSummaries(
|
|
1565
|
-
moduleEvent: AutoBeAnalyzeWriteModuleEvent,
|
|
1566
|
-
sectionResults: AutoBeAnalyzeWriteSectionEvent[][],
|
|
1567
|
-
excludeModuleIndex: number,
|
|
1568
|
-
): Array<{
|
|
1569
|
-
moduleIndex: number;
|
|
1570
|
-
title: string;
|
|
1571
|
-
sectionTitles: string[];
|
|
1572
|
-
}> {
|
|
1573
|
-
return sectionResults
|
|
1574
|
-
.map((sectionsForModule, moduleIndex) => ({
|
|
1575
|
-
moduleIndex,
|
|
1576
|
-
title: moduleEvent.moduleSections[moduleIndex]?.title ?? "Unknown",
|
|
1577
|
-
sectionTitles: sectionsForModule.flatMap((se) =>
|
|
1578
|
-
se.sectionSections.map((s) => s.title),
|
|
1579
|
-
),
|
|
1580
|
-
}))
|
|
1581
|
-
.filter((s) => s.moduleIndex !== excludeModuleIndex);
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
function mergeModuleReviewEvents(
|
|
1585
|
-
moduleReviews: AutoBeAnalyzeSectionReviewEvent[],
|
|
1586
|
-
fileIndex: number,
|
|
1587
|
-
): AutoBeAnalyzeSectionReviewEvent | null {
|
|
1588
|
-
if (moduleReviews.length === 0) return null;
|
|
1589
|
-
|
|
1590
|
-
const allApproved = moduleReviews.every(
|
|
1591
|
-
(r) => r.fileResults[0]?.approved ?? true,
|
|
1592
|
-
);
|
|
1593
|
-
const allFeedback = moduleReviews
|
|
1594
|
-
.map((r) => r.fileResults[0]?.feedback)
|
|
1595
|
-
.filter(Boolean)
|
|
1596
|
-
.join("\n");
|
|
1597
|
-
const allRejectedModuleUnits = moduleReviews.flatMap(
|
|
1598
|
-
(r) => r.fileResults[0]?.rejectedModuleUnits ?? [],
|
|
1599
|
-
);
|
|
1600
|
-
const allIssues = moduleReviews.flatMap(
|
|
1601
|
-
(r) => r.fileResults[0]?.issues ?? [],
|
|
1602
|
-
);
|
|
1603
|
-
|
|
1604
|
-
// Use the last review event as base (for tokenUsage, metric, etc.)
|
|
1605
|
-
const base = moduleReviews[moduleReviews.length - 1]!;
|
|
1606
|
-
return {
|
|
1607
|
-
...base,
|
|
1608
|
-
fileResults: [
|
|
1609
|
-
{
|
|
1610
|
-
fileIndex,
|
|
1611
|
-
approved: allApproved,
|
|
1612
|
-
feedback: allFeedback,
|
|
1613
|
-
revisedSections: null,
|
|
1614
|
-
rejectedModuleUnits:
|
|
1615
|
-
allRejectedModuleUnits.length > 0 ? allRejectedModuleUnits : null,
|
|
1616
|
-
issues: allIssues.length > 0 ? allIssues : null,
|
|
1617
|
-
},
|
|
1618
|
-
],
|
|
1619
|
-
};
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
// ─── Section-stage helpers ───
|
|
1623
|
-
|
|
1624
|
-
function buildSectionContentSignature(state: IFileState): string {
|
|
1625
|
-
if (!state.sectionResults) return "none";
|
|
1626
|
-
return JSON.stringify(
|
|
1627
|
-
state.sectionResults.map((moduleSections) =>
|
|
1628
|
-
moduleSections.map((unit) =>
|
|
1629
|
-
unit.sectionSections.map((section) => ({
|
|
1630
|
-
title: section.title,
|
|
1631
|
-
// content text included to detect no-progress rewrites
|
|
1632
|
-
content: section.content,
|
|
1633
|
-
})),
|
|
1634
|
-
),
|
|
1635
|
-
),
|
|
1636
|
-
);
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
function buildSectionRejectionSignature(props: {
|
|
1640
|
-
rejectedModuleUnits:
|
|
1641
|
-
| AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
|
|
1642
|
-
| null
|
|
1643
|
-
| undefined;
|
|
1644
|
-
feedback: string;
|
|
1645
|
-
}): string {
|
|
1646
|
-
return JSON.stringify({
|
|
1647
|
-
rejectedModuleUnits: (props.rejectedModuleUnits ?? []).map((entry) => ({
|
|
1648
|
-
moduleIndex: entry.moduleIndex,
|
|
1649
|
-
unitIndices: [...entry.unitIndices].sort((a, b) => a - b),
|
|
1650
|
-
feedback: entry.feedback,
|
|
1651
|
-
issues: (entry.issues ?? []).map((issue) => ({
|
|
1652
|
-
ruleCode: issue.ruleCode,
|
|
1653
|
-
moduleIndex: issue.moduleIndex,
|
|
1654
|
-
unitIndex: issue.unitIndex,
|
|
1655
|
-
sectionIndex: issue.sectionIndex ?? null,
|
|
1656
|
-
fixInstruction: issue.fixInstruction,
|
|
1657
|
-
})),
|
|
1658
|
-
})),
|
|
1659
|
-
feedback: props.feedback,
|
|
1660
|
-
});
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
function formatRejectedModuleUnitsSummary(
|
|
1664
|
-
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1665
|
-
): string {
|
|
1666
|
-
if (!rejected || rejected.length === 0) return "all-or-unknown";
|
|
1667
|
-
return rejected
|
|
1668
|
-
.slice(0, 6)
|
|
1669
|
-
.map((entry) => {
|
|
1670
|
-
const unitParts = entry.unitIndices.map((ui) => {
|
|
1671
|
-
const sectionPart = entry.sectionIndicesPerUnit?.[ui];
|
|
1672
|
-
return sectionPart ? `u${ui}(s${sectionPart.join(",s")})` : `u${ui}`;
|
|
1673
|
-
});
|
|
1674
|
-
return `m${entry.moduleIndex}:${unitParts.join(",") || "-"}`;
|
|
1675
|
-
})
|
|
1676
|
-
.join(" | ");
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
function formatReviewIssuesSummary(
|
|
1680
|
-
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1681
|
-
): string {
|
|
1682
|
-
if (issues.length === 0) return "none";
|
|
1683
|
-
return issues
|
|
1684
|
-
.slice(0, 8)
|
|
1685
|
-
.map((issue) => `${issue.ruleCode}@${formatIssueTarget(issue)}`)
|
|
1686
|
-
.join(", ");
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
function truncateForDebug(text: string, max: number): string {
|
|
1690
|
-
const singleLine = text.replace(/\s+/g, " ").trim();
|
|
1691
|
-
if (singleLine.length <= max) return singleLine;
|
|
1692
|
-
return `${singleLine.slice(0, max)}...`;
|
|
1693
|
-
}
|
|
1
|
+
import { AgenticaValidationError } from "@agentica/core";
|
|
2
|
+
import {
|
|
3
|
+
AutoBeAnalyze,
|
|
4
|
+
AutoBeAnalyzeHistory,
|
|
5
|
+
AutoBeAnalyzeScenarioEvent,
|
|
6
|
+
AutoBeAnalyzeScenarioReviewEvent,
|
|
7
|
+
AutoBeAnalyzeSectionReviewEvent,
|
|
8
|
+
AutoBeAnalyzeSectionReviewFileResult,
|
|
9
|
+
AutoBeAnalyzeSectionReviewIssue,
|
|
10
|
+
AutoBeAnalyzeSectionReviewRejectedModuleUnit,
|
|
11
|
+
AutoBeAnalyzeWriteModuleEvent,
|
|
12
|
+
AutoBeAnalyzeWriteSectionEvent,
|
|
13
|
+
AutoBeAnalyzeWriteUnitEvent,
|
|
14
|
+
AutoBeAssistantMessageHistory,
|
|
15
|
+
AutoBeProgressEventBase,
|
|
16
|
+
} from "@autobe/interface";
|
|
17
|
+
import { v7 } from "uuid";
|
|
18
|
+
|
|
19
|
+
import { AutoBeConfigConstant } from "../../constants/AutoBeConfigConstant";
|
|
20
|
+
import { AutoBeContext } from "../../context/AutoBeContext";
|
|
21
|
+
import { AutoBePreliminaryExhaustedError } from "../../utils/AutoBePreliminaryExhaustedError";
|
|
22
|
+
import { AutoBeTimeoutError } from "../../utils/AutoBeTimeoutError";
|
|
23
|
+
import { executeCachedBatch } from "../../utils/executeCachedBatch";
|
|
24
|
+
import { fillTocDeterministic } from "./fillTocDeterministic";
|
|
25
|
+
import { orchestrateAnalyzeScenario } from "./orchestrateAnalyzeScenario";
|
|
26
|
+
import { orchestrateAnalyzeScenarioReview } from "./orchestrateAnalyzeScenarioReview";
|
|
27
|
+
import { orchestrateAnalyzeSectionCrossFileReview } from "./orchestrateAnalyzeSectionCrossFileReview";
|
|
28
|
+
import { orchestrateAnalyzeSectionReview } from "./orchestrateAnalyzeSectionReview";
|
|
29
|
+
import { orchestrateAnalyzeWriteSection } from "./orchestrateAnalyzeWriteSection";
|
|
30
|
+
import { orchestrateAnalyzeWriteSectionPatch } from "./orchestrateAnalyzeWriteSectionPatch";
|
|
31
|
+
import { orchestrateAnalyzeWriteUnit } from "./orchestrateAnalyzeWriteUnit";
|
|
32
|
+
import {
|
|
33
|
+
assembleContent,
|
|
34
|
+
assembleModule,
|
|
35
|
+
} from "./programmers/AutoBeAnalyzeProgrammer";
|
|
36
|
+
import {
|
|
37
|
+
FixedAnalyzeTemplateFeature,
|
|
38
|
+
FixedAnalyzeTemplateUnitTemplate,
|
|
39
|
+
buildFixedAnalyzeExpandedTemplate,
|
|
40
|
+
expandFixedAnalyzeTemplateUnits,
|
|
41
|
+
} from "./structures/FixedAnalyzeTemplate";
|
|
42
|
+
import {
|
|
43
|
+
buildFileAttributeDuplicateMap,
|
|
44
|
+
buildFileConflictMap,
|
|
45
|
+
buildFileEnumConflictMap,
|
|
46
|
+
buildFilePermissionConflictMap,
|
|
47
|
+
buildFileStateFieldConflictMap,
|
|
48
|
+
detectAttributeDuplicates,
|
|
49
|
+
detectConstraintConflicts,
|
|
50
|
+
detectEnumConflicts,
|
|
51
|
+
detectPermissionConflicts,
|
|
52
|
+
detectStateFieldConflicts,
|
|
53
|
+
} from "./utils/buildConstraintConsistencyReport";
|
|
54
|
+
import {
|
|
55
|
+
buildFileErrorCodeConflictMap,
|
|
56
|
+
detectErrorCodeConflicts,
|
|
57
|
+
} from "./utils/buildErrorCodeRegistry";
|
|
58
|
+
import { detectOversizedToc } from "./utils/buildHardValidators";
|
|
59
|
+
import {
|
|
60
|
+
buildFileProseConflictMap,
|
|
61
|
+
detectProseConstraintConflicts,
|
|
62
|
+
} from "./utils/detectProseConstraintConflicts";
|
|
63
|
+
import { validateScenarioBasics } from "./utils/validateScenarioBasics";
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Per-file state tracking across all three stages (Module → Unit → Section).
|
|
67
|
+
*
|
|
68
|
+
* Maintains each file's intermediate results and cross-file review feedback
|
|
69
|
+
* throughout the stage-synchronized pipeline.
|
|
70
|
+
*/
|
|
71
|
+
interface IFileState {
|
|
72
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
73
|
+
moduleResult: AutoBeAnalyzeWriteModuleEvent | null;
|
|
74
|
+
unitResults: AutoBeAnalyzeWriteUnitEvent[] | null;
|
|
75
|
+
sectionResults: AutoBeAnalyzeWriteSectionEvent[][] | null;
|
|
76
|
+
sectionFeedback?: string;
|
|
77
|
+
// Section-stage partial regeneration tracking
|
|
78
|
+
rejectedModuleUnits?: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null;
|
|
79
|
+
sectionRetryCount?: number;
|
|
80
|
+
sectionReviewCount?: number;
|
|
81
|
+
sectionStagnationCount?: number;
|
|
82
|
+
lastSectionContentSignature?: string;
|
|
83
|
+
lastSectionRejectionSignature?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ANALYZE_SCENARIO_MAX_RETRY = 2;
|
|
87
|
+
const ANALYZE_SECTION_FILE_MAX_RETRY = 5;
|
|
88
|
+
const ANALYZE_SECTION_FILE_MAX_REVIEW = 2;
|
|
89
|
+
const ANALYZE_SECTION_STAGNATION_MAX = 4;
|
|
90
|
+
const ANALYZE_DEBUG_LOG = process.env.AUTOBE_DEBUG_ANALYZE === "1";
|
|
91
|
+
|
|
92
|
+
const analyzeDebug = (message: string): void => {
|
|
93
|
+
if (!ANALYZE_DEBUG_LOG) return;
|
|
94
|
+
console.log(`[analyze-debug] ${new Date().toISOString()} ${message}`);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const orchestrateAnalyze = async (
|
|
98
|
+
ctx: AutoBeContext,
|
|
99
|
+
): Promise<AutoBeAssistantMessageHistory | AutoBeAnalyzeHistory> => {
|
|
100
|
+
// Initialize analysis state
|
|
101
|
+
const step: number = (ctx.state().analyze?.step ?? -1) + 1;
|
|
102
|
+
const startTime: Date = new Date();
|
|
103
|
+
|
|
104
|
+
ctx.dispatch({
|
|
105
|
+
type: "analyzeStart",
|
|
106
|
+
id: v7(),
|
|
107
|
+
step,
|
|
108
|
+
created_at: startTime.toISOString(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Generate analysis scenario with pre-check + LLM review + retry loop
|
|
112
|
+
let scenario!: AutoBeAnalyzeScenarioEvent;
|
|
113
|
+
let scenarioFeedback: string | undefined;
|
|
114
|
+
|
|
115
|
+
for (let attempt = 0; attempt <= ANALYZE_SCENARIO_MAX_RETRY; attempt++) {
|
|
116
|
+
const rawScenario = await orchestrateAnalyzeScenario(ctx, {
|
|
117
|
+
feedback: scenarioFeedback,
|
|
118
|
+
});
|
|
119
|
+
if (rawScenario.type === "assistantMessage")
|
|
120
|
+
return ctx.assistantMessage(rawScenario);
|
|
121
|
+
|
|
122
|
+
// 1) Programmatic pre-check
|
|
123
|
+
const preCheck = validateScenarioBasics({
|
|
124
|
+
prefix: rawScenario.prefix,
|
|
125
|
+
actors: rawScenario.actors,
|
|
126
|
+
entities: rawScenario.entities,
|
|
127
|
+
});
|
|
128
|
+
if (!preCheck.valid && attempt < ANALYZE_SCENARIO_MAX_RETRY) {
|
|
129
|
+
analyzeDebug(
|
|
130
|
+
`Scenario pre-check failed (attempt ${attempt}): ${preCheck.errors.join("; ")}`,
|
|
131
|
+
);
|
|
132
|
+
scenarioFeedback = `Programmatic validation failed:\n${preCheck.errors.join("\n")}`;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 2) LLM review
|
|
137
|
+
let review: AutoBeAnalyzeScenarioReviewEvent;
|
|
138
|
+
try {
|
|
139
|
+
review = await orchestrateAnalyzeScenarioReview(ctx, {
|
|
140
|
+
scenario: rawScenario,
|
|
141
|
+
retry: attempt,
|
|
142
|
+
});
|
|
143
|
+
} catch (e) {
|
|
144
|
+
if (
|
|
145
|
+
e instanceof AgenticaValidationError ||
|
|
146
|
+
e instanceof AutoBePreliminaryExhaustedError ||
|
|
147
|
+
e instanceof AutoBeTimeoutError
|
|
148
|
+
) {
|
|
149
|
+
analyzeDebug(
|
|
150
|
+
`scenario review force-pass (attempt ${attempt}) error=${(e as Error).constructor.name}`,
|
|
151
|
+
);
|
|
152
|
+
review = {
|
|
153
|
+
type: "analyzeScenarioReview",
|
|
154
|
+
id: v7(),
|
|
155
|
+
approved: true,
|
|
156
|
+
feedback:
|
|
157
|
+
"Review could not be completed; proceeding with current scenario.",
|
|
158
|
+
issues: [],
|
|
159
|
+
tokenUsage: {
|
|
160
|
+
total: 0,
|
|
161
|
+
input: { total: 0, cached: 0 },
|
|
162
|
+
output: {
|
|
163
|
+
total: 0,
|
|
164
|
+
reasoning: 0,
|
|
165
|
+
accepted_prediction: 0,
|
|
166
|
+
rejected_prediction: 0,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
metric: {
|
|
170
|
+
attempt: 0,
|
|
171
|
+
success: 0,
|
|
172
|
+
consent: 0,
|
|
173
|
+
validationFailure: 0,
|
|
174
|
+
invalidJson: 0,
|
|
175
|
+
},
|
|
176
|
+
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
177
|
+
retry: attempt,
|
|
178
|
+
created_at: new Date().toISOString(),
|
|
179
|
+
};
|
|
180
|
+
} else {
|
|
181
|
+
throw e;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (review.approved || attempt >= ANALYZE_SCENARIO_MAX_RETRY) {
|
|
186
|
+
analyzeDebug(
|
|
187
|
+
review.approved
|
|
188
|
+
? `Scenario approved (attempt ${attempt})`
|
|
189
|
+
: `Scenario max retry reached (attempt ${attempt}), proceeding`,
|
|
190
|
+
);
|
|
191
|
+
scenario = rawScenario;
|
|
192
|
+
ctx.dispatch(scenario);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
analyzeDebug(`Scenario rejected (attempt ${attempt}): ${review.feedback}`);
|
|
197
|
+
scenarioFeedback = review.feedback;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Initialize per-file state
|
|
201
|
+
const fileStates: IFileState[] = scenario.files.map((file) => ({
|
|
202
|
+
file,
|
|
203
|
+
moduleResult: null,
|
|
204
|
+
unitResults: null,
|
|
205
|
+
sectionResults: null,
|
|
206
|
+
}));
|
|
207
|
+
|
|
208
|
+
// Progress tracking for each stage
|
|
209
|
+
const moduleWriteProgress: AutoBeProgressEventBase = {
|
|
210
|
+
total: scenario.files.length,
|
|
211
|
+
completed: 0,
|
|
212
|
+
};
|
|
213
|
+
const unitWriteProgress: AutoBeProgressEventBase = {
|
|
214
|
+
total: 0,
|
|
215
|
+
completed: 0,
|
|
216
|
+
};
|
|
217
|
+
const sectionWriteProgress: AutoBeProgressEventBase = {
|
|
218
|
+
total: 0,
|
|
219
|
+
completed: 0,
|
|
220
|
+
};
|
|
221
|
+
const perFileSectionReviewProgress: AutoBeProgressEventBase = {
|
|
222
|
+
total: 0,
|
|
223
|
+
completed: 0,
|
|
224
|
+
};
|
|
225
|
+
const crossFileSectionReviewProgress: AutoBeProgressEventBase = {
|
|
226
|
+
total: 1,
|
|
227
|
+
completed: 0,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// === STAGE 1: MODULE (deterministic — no LLM) ===
|
|
231
|
+
processStageModuleDeterministic(ctx, {
|
|
232
|
+
scenario,
|
|
233
|
+
fileStates,
|
|
234
|
+
moduleWriteProgress,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// === STAGE 2: UNIT (fixed units deterministic, dynamic units LLM) ===
|
|
238
|
+
await processStageUnit(ctx, {
|
|
239
|
+
scenario,
|
|
240
|
+
fileStates,
|
|
241
|
+
unitWriteProgress,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// === STAGE 3: SECTION (01-05 only, TOC excluded) ===
|
|
245
|
+
await processStageSection(ctx, {
|
|
246
|
+
scenario,
|
|
247
|
+
fileStates,
|
|
248
|
+
sectionWriteProgress,
|
|
249
|
+
perFileSectionReviewProgress,
|
|
250
|
+
crossFileSectionReviewProgress,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// === TOC FILL (deterministic — no LLM) ===
|
|
254
|
+
const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
|
|
255
|
+
(scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
|
|
256
|
+
);
|
|
257
|
+
const tocIndex = fileStates.findIndex((s) => s.file.filename === "00-toc.md");
|
|
258
|
+
let tocContent: string | null = null;
|
|
259
|
+
if (tocIndex >= 0) {
|
|
260
|
+
tocContent = fillTocDeterministic(ctx, {
|
|
261
|
+
scenario,
|
|
262
|
+
tocFileState: fileStates[tocIndex]!,
|
|
263
|
+
otherFileStates: fileStates.filter((_, i) => i !== tocIndex),
|
|
264
|
+
expandedTemplate,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// === ASSEMBLE ===
|
|
269
|
+
const files: AutoBeAnalyze.IFile[] = [];
|
|
270
|
+
for (let fileIndex = 0; fileIndex < fileStates.length; fileIndex++) {
|
|
271
|
+
const state = fileStates[fileIndex]!;
|
|
272
|
+
// TOC uses flat content directly (no module/unit hierarchy)
|
|
273
|
+
const content =
|
|
274
|
+
fileIndex === tocIndex
|
|
275
|
+
? tocContent!
|
|
276
|
+
: assembleContent(
|
|
277
|
+
state.moduleResult!,
|
|
278
|
+
state.unitResults!,
|
|
279
|
+
state.sectionResults!,
|
|
280
|
+
);
|
|
281
|
+
const module = assembleModule(
|
|
282
|
+
state.moduleResult!,
|
|
283
|
+
state.unitResults!,
|
|
284
|
+
state.sectionResults!,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
files.push({
|
|
288
|
+
...state.file,
|
|
289
|
+
title: state.moduleResult!.title,
|
|
290
|
+
summary: state.moduleResult!.summary,
|
|
291
|
+
content,
|
|
292
|
+
module,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Complete the analysis
|
|
297
|
+
return ctx.dispatch({
|
|
298
|
+
type: "analyzeComplete",
|
|
299
|
+
id: v7(),
|
|
300
|
+
actors: scenario.actors,
|
|
301
|
+
prefix: scenario.prefix,
|
|
302
|
+
files,
|
|
303
|
+
aggregates: ctx.getCurrentAggregates("analyze"),
|
|
304
|
+
step,
|
|
305
|
+
elapsed: new Date().getTime() - startTime.getTime(),
|
|
306
|
+
created_at: new Date().toISOString(),
|
|
307
|
+
}) satisfies AutoBeAnalyzeHistory;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// MODULE (deterministic — no LLM calls)
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Generate module structure deterministically from FixedAnalyzeTemplate.
|
|
314
|
+
*
|
|
315
|
+
* No LLM calls needed — module titles, purposes, and structure are all derived
|
|
316
|
+
* from the fixed 6-file SRS template.
|
|
317
|
+
*/
|
|
318
|
+
function processStageModuleDeterministic(
|
|
319
|
+
ctx: AutoBeContext,
|
|
320
|
+
props: {
|
|
321
|
+
scenario: AutoBeAnalyzeScenarioEvent;
|
|
322
|
+
fileStates: IFileState[];
|
|
323
|
+
moduleWriteProgress: AutoBeProgressEventBase;
|
|
324
|
+
},
|
|
325
|
+
): void {
|
|
326
|
+
const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
|
|
327
|
+
(props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
|
|
328
|
+
);
|
|
329
|
+
for (const [i, state] of props.fileStates.entries()) {
|
|
330
|
+
const template = expandedTemplate[i]!;
|
|
331
|
+
const moduleEvent: AutoBeAnalyzeWriteModuleEvent = {
|
|
332
|
+
type: "analyzeWriteModule",
|
|
333
|
+
id: v7(),
|
|
334
|
+
title: `${props.scenario.prefix} — ${template.description}`,
|
|
335
|
+
summary: template.description,
|
|
336
|
+
moduleSections: template.modules.map((m) => ({
|
|
337
|
+
title: m.title,
|
|
338
|
+
purpose: m.purpose,
|
|
339
|
+
content: m.purpose,
|
|
340
|
+
})),
|
|
341
|
+
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
342
|
+
retry: 0,
|
|
343
|
+
total: props.fileStates.length,
|
|
344
|
+
completed: i + 1,
|
|
345
|
+
tokenUsage: {
|
|
346
|
+
total: 0,
|
|
347
|
+
input: { total: 0, cached: 0 },
|
|
348
|
+
output: {
|
|
349
|
+
total: 0,
|
|
350
|
+
reasoning: 0,
|
|
351
|
+
accepted_prediction: 0,
|
|
352
|
+
rejected_prediction: 0,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
metric: {
|
|
356
|
+
attempt: 0,
|
|
357
|
+
success: 0,
|
|
358
|
+
consent: 0,
|
|
359
|
+
validationFailure: 0,
|
|
360
|
+
invalidJson: 0,
|
|
361
|
+
},
|
|
362
|
+
acquisition: { previousAnalysisSections: [] },
|
|
363
|
+
created_at: new Date().toISOString(),
|
|
364
|
+
};
|
|
365
|
+
state.moduleResult = moduleEvent;
|
|
366
|
+
ctx.dispatch(moduleEvent);
|
|
367
|
+
props.moduleWriteProgress.completed++;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// UNIT
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Process the Unit stage for all files.
|
|
375
|
+
*
|
|
376
|
+
* Fixed-strategy modules get deterministic unit generation (no LLM).
|
|
377
|
+
* Dynamic-strategy modules (perEntity/perActor/perEntityGroup) use LLM. No
|
|
378
|
+
* cross-file unit review — Hard Validators at section stage handle
|
|
379
|
+
* consistency.
|
|
380
|
+
*/
|
|
381
|
+
async function processStageUnit(
|
|
382
|
+
ctx: AutoBeContext,
|
|
383
|
+
props: {
|
|
384
|
+
scenario: AutoBeAnalyzeScenarioEvent;
|
|
385
|
+
fileStates: IFileState[];
|
|
386
|
+
unitWriteProgress: AutoBeProgressEventBase;
|
|
387
|
+
},
|
|
388
|
+
): Promise<void> {
|
|
389
|
+
const promptCacheKey: string = v7();
|
|
390
|
+
const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
|
|
391
|
+
(props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// Count total units needed for progress tracking
|
|
395
|
+
for (const [fileIndex, state] of props.fileStates.entries()) {
|
|
396
|
+
const template = expandedTemplate[fileIndex]!;
|
|
397
|
+
props.unitWriteProgress.total += template.modules.length;
|
|
398
|
+
void state; // used below
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
await executeCachedBatch(
|
|
402
|
+
ctx,
|
|
403
|
+
props.fileStates.map((state, fileIndex) => async (cacheKey) => {
|
|
404
|
+
// TOC is filled deterministically after all other files complete
|
|
405
|
+
if (state.file.filename === "00-toc.md") return [];
|
|
406
|
+
|
|
407
|
+
const moduleResult: AutoBeAnalyzeWriteModuleEvent = state.moduleResult!;
|
|
408
|
+
const template = expandedTemplate[fileIndex]!;
|
|
409
|
+
analyzeDebug(
|
|
410
|
+
`unit file-start fileIndex=${fileIndex} file="${state.file.filename}"`,
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
const unitResults: AutoBeAnalyzeWriteUnitEvent[] = [];
|
|
414
|
+
for (
|
|
415
|
+
let moduleIndex: number = 0;
|
|
416
|
+
moduleIndex < moduleResult.moduleSections.length;
|
|
417
|
+
moduleIndex++
|
|
418
|
+
) {
|
|
419
|
+
const moduleTemplate = template.modules[moduleIndex]!;
|
|
420
|
+
const strategy = moduleTemplate.unitStrategy;
|
|
421
|
+
|
|
422
|
+
if (strategy.type === "fixed") {
|
|
423
|
+
// Deterministic unit generation — no LLM
|
|
424
|
+
const unitEvent = buildDeterministicUnitEvent(ctx, {
|
|
425
|
+
moduleIndex,
|
|
426
|
+
units: strategy.units,
|
|
427
|
+
progress: props.unitWriteProgress,
|
|
428
|
+
});
|
|
429
|
+
ctx.dispatch(unitEvent);
|
|
430
|
+
unitResults.push(unitEvent);
|
|
431
|
+
} else {
|
|
432
|
+
// Dynamic units — expand from template, then LLM writes content+keywords
|
|
433
|
+
const unitStart: number = Date.now();
|
|
434
|
+
analyzeDebug(
|
|
435
|
+
`unit module-start fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} strategy=${strategy.type}`,
|
|
436
|
+
);
|
|
437
|
+
try {
|
|
438
|
+
const unitEvent: AutoBeAnalyzeWriteUnitEvent =
|
|
439
|
+
await orchestrateAnalyzeWriteUnit(ctx, {
|
|
440
|
+
scenario: props.scenario,
|
|
441
|
+
file: state.file,
|
|
442
|
+
moduleEvent: moduleResult,
|
|
443
|
+
moduleIndex,
|
|
444
|
+
progress: props.unitWriteProgress,
|
|
445
|
+
promptCacheKey: cacheKey,
|
|
446
|
+
retry: 0,
|
|
447
|
+
});
|
|
448
|
+
analyzeDebug(
|
|
449
|
+
`unit module-done fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitCount=${unitEvent.unitSections.length} elapsedMs=${Date.now() - unitStart}`,
|
|
450
|
+
);
|
|
451
|
+
unitResults.push(unitEvent);
|
|
452
|
+
} catch (e) {
|
|
453
|
+
if (
|
|
454
|
+
e instanceof AgenticaValidationError ||
|
|
455
|
+
e instanceof AutoBePreliminaryExhaustedError ||
|
|
456
|
+
e instanceof AutoBeTimeoutError
|
|
457
|
+
) {
|
|
458
|
+
analyzeDebug(
|
|
459
|
+
`unit module-skipped fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} elapsedMs=${Date.now() - unitStart} — using fallback`,
|
|
460
|
+
);
|
|
461
|
+
const expandedUnits = expandFixedAnalyzeTemplateUnits(
|
|
462
|
+
moduleTemplate,
|
|
463
|
+
props.scenario.entities,
|
|
464
|
+
props.scenario.actors,
|
|
465
|
+
);
|
|
466
|
+
const fallbackEvent = buildDeterministicUnitEvent(ctx, {
|
|
467
|
+
moduleIndex,
|
|
468
|
+
units: expandedUnits,
|
|
469
|
+
progress: props.unitWriteProgress,
|
|
470
|
+
});
|
|
471
|
+
ctx.dispatch(fallbackEvent);
|
|
472
|
+
unitResults.push(fallbackEvent);
|
|
473
|
+
} else {
|
|
474
|
+
throw e;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
state.unitResults = unitResults;
|
|
480
|
+
analyzeDebug(
|
|
481
|
+
`unit file-done fileIndex=${fileIndex} file="${state.file.filename}"`,
|
|
482
|
+
);
|
|
483
|
+
return unitResults;
|
|
484
|
+
}),
|
|
485
|
+
promptCacheKey,
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Build a deterministic AutoBeAnalyzeWriteUnitEvent for fixed-strategy modules. */
|
|
490
|
+
function buildDeterministicUnitEvent(
|
|
491
|
+
ctx: AutoBeContext,
|
|
492
|
+
props: {
|
|
493
|
+
moduleIndex: number;
|
|
494
|
+
units: FixedAnalyzeTemplateUnitTemplate[];
|
|
495
|
+
progress: AutoBeProgressEventBase;
|
|
496
|
+
},
|
|
497
|
+
): AutoBeAnalyzeWriteUnitEvent {
|
|
498
|
+
props.progress.completed++;
|
|
499
|
+
return {
|
|
500
|
+
type: "analyzeWriteUnit",
|
|
501
|
+
id: v7(),
|
|
502
|
+
moduleIndex: props.moduleIndex,
|
|
503
|
+
unitSections: props.units.map((u) => ({
|
|
504
|
+
title: u.titlePattern,
|
|
505
|
+
purpose: u.purposePattern,
|
|
506
|
+
content: u.purposePattern,
|
|
507
|
+
keywords: [...u.keywords],
|
|
508
|
+
})),
|
|
509
|
+
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
510
|
+
retry: 0,
|
|
511
|
+
total: props.progress.total,
|
|
512
|
+
completed: props.progress.completed,
|
|
513
|
+
tokenUsage: {
|
|
514
|
+
total: 0,
|
|
515
|
+
input: { total: 0, cached: 0 },
|
|
516
|
+
output: {
|
|
517
|
+
total: 0,
|
|
518
|
+
reasoning: 0,
|
|
519
|
+
accepted_prediction: 0,
|
|
520
|
+
rejected_prediction: 0,
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
metric: {
|
|
524
|
+
attempt: 0,
|
|
525
|
+
success: 0,
|
|
526
|
+
consent: 0,
|
|
527
|
+
validationFailure: 0,
|
|
528
|
+
invalidJson: 0,
|
|
529
|
+
},
|
|
530
|
+
acquisition: { previousAnalysisSections: [] },
|
|
531
|
+
created_at: new Date().toISOString(),
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// SECTION
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Process the Section stage for all files with 2-pass review.
|
|
539
|
+
*
|
|
540
|
+
* Flow:
|
|
541
|
+
*
|
|
542
|
+
* 1. Write sections for pending files in parallel
|
|
543
|
+
* 2. Pass 1: Per-file detailed review (parallel) — validates EARS format, value
|
|
544
|
+
* consistency, bridge blocks, intra-file deduplication
|
|
545
|
+
* 3. Pass 2: Cross-file lightweight review (single call) — validates terminology
|
|
546
|
+
* alignment, value consistency across files, naming conventions
|
|
547
|
+
* 4. Merge results from both passes — reject if either pass rejects
|
|
548
|
+
* 5. Retry only rejected files (max 3 attempts)
|
|
549
|
+
*/
|
|
550
|
+
async function processStageSection(
|
|
551
|
+
ctx: AutoBeContext,
|
|
552
|
+
props: {
|
|
553
|
+
scenario: AutoBeAnalyzeScenarioEvent;
|
|
554
|
+
fileStates: IFileState[];
|
|
555
|
+
sectionWriteProgress: AutoBeProgressEventBase;
|
|
556
|
+
perFileSectionReviewProgress: AutoBeProgressEventBase;
|
|
557
|
+
crossFileSectionReviewProgress: AutoBeProgressEventBase;
|
|
558
|
+
},
|
|
559
|
+
): Promise<void> {
|
|
560
|
+
// Exclude TOC (00-toc.md) — it is filled deterministically after all files
|
|
561
|
+
const pendingIndices: Set<number> = new Set(
|
|
562
|
+
props.fileStates
|
|
563
|
+
.map((s, i) => (s.file.filename === "00-toc.md" ? -1 : i))
|
|
564
|
+
.filter((i) => i >= 0),
|
|
565
|
+
);
|
|
566
|
+
let crossFileReviewCount: number = 0;
|
|
567
|
+
|
|
568
|
+
for (
|
|
569
|
+
let attempt: number = 0;
|
|
570
|
+
attempt < AutoBeConfigConstant.ANALYZE_RETRY && pendingIndices.size > 0;
|
|
571
|
+
attempt++
|
|
572
|
+
) {
|
|
573
|
+
// Dynamically increase progress for retries (module-level granularity)
|
|
574
|
+
const pendingModuleCount = [...pendingIndices].reduce(
|
|
575
|
+
(sum, fi) => sum + (props.fileStates[fi]?.unitResults?.length ?? 1),
|
|
576
|
+
0,
|
|
577
|
+
);
|
|
578
|
+
props.perFileSectionReviewProgress.total += pendingModuleCount;
|
|
579
|
+
if (attempt > 0) {
|
|
580
|
+
props.crossFileSectionReviewProgress.total++;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Write sections for pending files in parallel
|
|
584
|
+
const pendingArray: number[] = [...pendingIndices];
|
|
585
|
+
const sectionFileBatches: number[][] = chunkSectionFileIndices(
|
|
586
|
+
pendingArray,
|
|
587
|
+
computeSectionBatchSize({
|
|
588
|
+
attempt,
|
|
589
|
+
pendingCount: pendingArray.length,
|
|
590
|
+
}),
|
|
591
|
+
);
|
|
592
|
+
const promptCacheKey: string = v7();
|
|
593
|
+
|
|
594
|
+
// Build scenario entity name list for invention validation (P0-B)
|
|
595
|
+
const scenarioEntityNames = props.scenario.entities.map((e) => e.name);
|
|
596
|
+
|
|
597
|
+
// Collect per-file review results (populated inside write+review batch)
|
|
598
|
+
const perFileReviewResults: Map<number, AutoBeAnalyzeSectionReviewEvent> =
|
|
599
|
+
new Map();
|
|
600
|
+
|
|
601
|
+
for (const sectionBatch of sectionFileBatches)
|
|
602
|
+
await executeCachedBatch(
|
|
603
|
+
ctx,
|
|
604
|
+
sectionBatch.map((fileIndex) => async (cacheKey) => {
|
|
605
|
+
const state: IFileState = props.fileStates[fileIndex]!;
|
|
606
|
+
const moduleResult: AutoBeAnalyzeWriteModuleEvent =
|
|
607
|
+
state.moduleResult!;
|
|
608
|
+
const unitResults: AutoBeAnalyzeWriteUnitEvent[] = state.unitResults!;
|
|
609
|
+
analyzeDebug(
|
|
610
|
+
`section file-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" batchSize=${sectionBatch.length}`,
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
// Build rejected module/unit lookup for selective regeneration
|
|
614
|
+
const rejectedSet: Set<string> | null = buildRejectedSet(
|
|
615
|
+
state.rejectedModuleUnits,
|
|
616
|
+
);
|
|
617
|
+
const feedbackMap: Map<string, ISectionAwareFeedback> =
|
|
618
|
+
buildFeedbackMap(state.rejectedModuleUnits);
|
|
619
|
+
|
|
620
|
+
// Increase write progress only for sections that will be regenerated
|
|
621
|
+
for (let mi: number = 0; mi < unitResults.length; mi++) {
|
|
622
|
+
const unitEvent: AutoBeAnalyzeWriteUnitEvent = unitResults[mi]!;
|
|
623
|
+
for (let ui: number = 0; ui < unitEvent.unitSections.length; ui++) {
|
|
624
|
+
if (isSectionRejected(rejectedSet, mi, ui)) {
|
|
625
|
+
props.sectionWriteProgress.total++;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Write sections, skipping approved ones on retry
|
|
631
|
+
const sectionResults: AutoBeAnalyzeWriteSectionEvent[][] = [];
|
|
632
|
+
for (
|
|
633
|
+
let moduleIndex: number = 0;
|
|
634
|
+
moduleIndex < unitResults.length;
|
|
635
|
+
moduleIndex++
|
|
636
|
+
) {
|
|
637
|
+
const unitEvent: AutoBeAnalyzeWriteUnitEvent =
|
|
638
|
+
unitResults[moduleIndex]!;
|
|
639
|
+
const sectionsForModule: AutoBeAnalyzeWriteSectionEvent[] = [];
|
|
640
|
+
|
|
641
|
+
for (
|
|
642
|
+
let unitIndex: number = 0;
|
|
643
|
+
unitIndex < unitEvent.unitSections.length;
|
|
644
|
+
unitIndex++
|
|
645
|
+
) {
|
|
646
|
+
if (isSectionRejected(rejectedSet, moduleIndex, unitIndex)) {
|
|
647
|
+
const sectionStart: number = Date.now();
|
|
648
|
+
// Regenerate this section with targeted feedback
|
|
649
|
+
const targetedInfo: ISectionAwareFeedback | undefined =
|
|
650
|
+
feedbackMap.get(`${moduleIndex}:${unitIndex}`);
|
|
651
|
+
const targetedFeedback: string | undefined =
|
|
652
|
+
targetedInfo?.feedback ?? state.sectionFeedback;
|
|
653
|
+
const targetedSectionIndices: number[] | null =
|
|
654
|
+
targetedInfo?.sectionIndices ?? null;
|
|
655
|
+
analyzeDebug(
|
|
656
|
+
`section unit-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} targetSections=${targetedSectionIndices ? `[${targetedSectionIndices.join(",")}]` : "all"}`,
|
|
657
|
+
);
|
|
658
|
+
const previousSection:
|
|
659
|
+
| AutoBeAnalyzeWriteSectionEvent
|
|
660
|
+
| undefined =
|
|
661
|
+
state.sectionResults?.[moduleIndex]?.[unitIndex];
|
|
662
|
+
let sectionEvent: AutoBeAnalyzeWriteSectionEvent;
|
|
663
|
+
try {
|
|
664
|
+
sectionEvent =
|
|
665
|
+
previousSection && targetedFeedback?.trim()
|
|
666
|
+
? await orchestrateAnalyzeWriteSectionPatch(ctx, {
|
|
667
|
+
scenario: props.scenario,
|
|
668
|
+
file: state.file,
|
|
669
|
+
moduleEvent: moduleResult,
|
|
670
|
+
unitEvent,
|
|
671
|
+
moduleIndex,
|
|
672
|
+
unitIndex,
|
|
673
|
+
previousSectionEvent: previousSection,
|
|
674
|
+
feedback: targetedFeedback,
|
|
675
|
+
progress: props.sectionWriteProgress,
|
|
676
|
+
promptCacheKey: cacheKey,
|
|
677
|
+
retry: attempt,
|
|
678
|
+
scenarioEntityNames,
|
|
679
|
+
sectionIndices: targetedSectionIndices,
|
|
680
|
+
})
|
|
681
|
+
: await orchestrateAnalyzeWriteSection(ctx, {
|
|
682
|
+
scenario: props.scenario,
|
|
683
|
+
file: state.file,
|
|
684
|
+
moduleEvent: moduleResult,
|
|
685
|
+
unitEvent,
|
|
686
|
+
allUnitEvents: unitResults,
|
|
687
|
+
moduleIndex,
|
|
688
|
+
unitIndex,
|
|
689
|
+
progress: props.sectionWriteProgress,
|
|
690
|
+
promptCacheKey: cacheKey,
|
|
691
|
+
feedback: targetedFeedback,
|
|
692
|
+
retry: attempt,
|
|
693
|
+
scenarioEntityNames,
|
|
694
|
+
});
|
|
695
|
+
} catch (e) {
|
|
696
|
+
if (
|
|
697
|
+
e instanceof AgenticaValidationError ||
|
|
698
|
+
e instanceof AutoBePreliminaryExhaustedError ||
|
|
699
|
+
e instanceof AutoBeTimeoutError
|
|
700
|
+
) {
|
|
701
|
+
analyzeDebug(
|
|
702
|
+
`section unit-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} error=${(e as Error).constructor.name} — ${previousSection ? "reusing previous" : "using placeholder"}`,
|
|
703
|
+
);
|
|
704
|
+
if (previousSection) {
|
|
705
|
+
sectionEvent = previousSection;
|
|
706
|
+
} else {
|
|
707
|
+
sectionEvent = {
|
|
708
|
+
type: "analyzeWriteSection",
|
|
709
|
+
id: v7(),
|
|
710
|
+
moduleIndex,
|
|
711
|
+
unitIndex,
|
|
712
|
+
sectionSections: [],
|
|
713
|
+
acquisition: { previousAnalysisSections: [] },
|
|
714
|
+
tokenUsage: {
|
|
715
|
+
total: 0,
|
|
716
|
+
input: { total: 0, cached: 0 },
|
|
717
|
+
output: {
|
|
718
|
+
total: 0,
|
|
719
|
+
reasoning: 0,
|
|
720
|
+
accepted_prediction: 0,
|
|
721
|
+
rejected_prediction: 0,
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
metric: {
|
|
725
|
+
attempt: 0,
|
|
726
|
+
success: 0,
|
|
727
|
+
consent: 0,
|
|
728
|
+
validationFailure: 0,
|
|
729
|
+
invalidJson: 0,
|
|
730
|
+
},
|
|
731
|
+
step: (ctx.state().analyze?.step ?? -1) + 1,
|
|
732
|
+
total: props.sectionWriteProgress.total,
|
|
733
|
+
completed: ++props.sectionWriteProgress.completed,
|
|
734
|
+
retry: attempt,
|
|
735
|
+
created_at: new Date().toISOString(),
|
|
736
|
+
};
|
|
737
|
+
ctx.dispatch(sectionEvent);
|
|
738
|
+
}
|
|
739
|
+
} else {
|
|
740
|
+
throw e;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
analyzeDebug(
|
|
744
|
+
`section unit-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} sectionCount=${sectionEvent.sectionSections.length} elapsedMs=${Date.now() - sectionStart}`,
|
|
745
|
+
);
|
|
746
|
+
sectionsForModule.push(sectionEvent);
|
|
747
|
+
} else {
|
|
748
|
+
// Keep existing approved section
|
|
749
|
+
sectionsForModule.push(
|
|
750
|
+
state.sectionResults![moduleIndex]![unitIndex]!,
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
sectionResults.push(sectionsForModule);
|
|
755
|
+
}
|
|
756
|
+
state.sectionResults = sectionResults;
|
|
757
|
+
|
|
758
|
+
analyzeDebug(
|
|
759
|
+
`section file-write-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}"`,
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
// Per-module review immediately after write (removes barrier)
|
|
763
|
+
const reviewStart: number = Date.now();
|
|
764
|
+
analyzeDebug(
|
|
765
|
+
`section per-module-review-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${state.unitResults!.length}`,
|
|
766
|
+
);
|
|
767
|
+
const moduleReviews: AutoBeAnalyzeSectionReviewEvent[] = [];
|
|
768
|
+
for (
|
|
769
|
+
let moduleIndex = 0;
|
|
770
|
+
moduleIndex < state.unitResults!.length;
|
|
771
|
+
moduleIndex++
|
|
772
|
+
) {
|
|
773
|
+
try {
|
|
774
|
+
const moduleReviewEvent = await orchestrateAnalyzeSectionReview(
|
|
775
|
+
ctx,
|
|
776
|
+
{
|
|
777
|
+
scenario: props.scenario,
|
|
778
|
+
fileIndex,
|
|
779
|
+
file: state.file,
|
|
780
|
+
moduleEvent: state.moduleResult!,
|
|
781
|
+
moduleIndex,
|
|
782
|
+
unitEvent: state.unitResults![moduleIndex]!,
|
|
783
|
+
moduleSectionEvents: state.sectionResults![moduleIndex]!,
|
|
784
|
+
siblingModuleSummaries: buildSiblingModuleSummaries(
|
|
785
|
+
state.moduleResult!,
|
|
786
|
+
state.sectionResults!,
|
|
787
|
+
moduleIndex,
|
|
788
|
+
),
|
|
789
|
+
feedback: state.sectionFeedback,
|
|
790
|
+
progress: props.perFileSectionReviewProgress,
|
|
791
|
+
promptCacheKey: cacheKey,
|
|
792
|
+
retry: attempt,
|
|
793
|
+
},
|
|
794
|
+
);
|
|
795
|
+
moduleReviews.push(moduleReviewEvent);
|
|
796
|
+
} catch (e) {
|
|
797
|
+
if (
|
|
798
|
+
e instanceof AgenticaValidationError ||
|
|
799
|
+
e instanceof AutoBeTimeoutError ||
|
|
800
|
+
e instanceof AutoBePreliminaryExhaustedError
|
|
801
|
+
) {
|
|
802
|
+
analyzeDebug(
|
|
803
|
+
`section per-module-review-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} — skipping this module review`,
|
|
804
|
+
);
|
|
805
|
+
} else {
|
|
806
|
+
throw e;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
const reviewEvent = mergeModuleReviewEvents(moduleReviews, fileIndex);
|
|
811
|
+
analyzeDebug(
|
|
812
|
+
`section per-module-review-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${moduleReviews.length} elapsedMs=${Date.now() - reviewStart}`,
|
|
813
|
+
);
|
|
814
|
+
perFileReviewResults.set(fileIndex, reviewEvent!);
|
|
815
|
+
|
|
816
|
+
return sectionResults;
|
|
817
|
+
}),
|
|
818
|
+
promptCacheKey,
|
|
819
|
+
);
|
|
820
|
+
|
|
821
|
+
// Pass 2: Cross-file lightweight review (single call)
|
|
822
|
+
crossFileReviewCount++;
|
|
823
|
+
if (crossFileReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
|
|
824
|
+
analyzeDebug(
|
|
825
|
+
`[orchestrateAnalyze] Section stage: skipping cross-file review (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded)`,
|
|
826
|
+
);
|
|
827
|
+
// Force-pass all pending files
|
|
828
|
+
for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
831
|
+
analyzeDebug(`section cross-file-validation-start attempt=${attempt}`);
|
|
832
|
+
const filesWithSections = props.fileStates
|
|
833
|
+
.filter((state) => state.sectionResults !== null)
|
|
834
|
+
.map((state) => ({
|
|
835
|
+
file: state.file,
|
|
836
|
+
sectionEvents: state.sectionResults!,
|
|
837
|
+
}));
|
|
838
|
+
|
|
839
|
+
// Pass 2a: Programmatic cross-file validation (BEFORE LLM review)
|
|
840
|
+
const criticalConflicts = detectConstraintConflicts({
|
|
841
|
+
files: filesWithSections,
|
|
842
|
+
});
|
|
843
|
+
const fileConflictMap: Map<string, string[]> =
|
|
844
|
+
buildFileConflictMap(criticalConflicts);
|
|
845
|
+
|
|
846
|
+
const attributeDuplicates = detectAttributeDuplicates({
|
|
847
|
+
files: filesWithSections,
|
|
848
|
+
});
|
|
849
|
+
const fileAttributeDuplicateMap: Map<string, string[]> =
|
|
850
|
+
buildFileAttributeDuplicateMap(attributeDuplicates);
|
|
851
|
+
|
|
852
|
+
const enumConflicts = detectEnumConflicts({
|
|
853
|
+
files: filesWithSections,
|
|
854
|
+
});
|
|
855
|
+
const fileEnumConflictMap: Map<string, string[]> =
|
|
856
|
+
buildFileEnumConflictMap(enumConflicts);
|
|
857
|
+
|
|
858
|
+
const permissionConflicts = detectPermissionConflicts({
|
|
859
|
+
files: filesWithSections,
|
|
860
|
+
});
|
|
861
|
+
const filePermissionConflictMap: Map<string, string[]> =
|
|
862
|
+
buildFilePermissionConflictMap(permissionConflicts);
|
|
863
|
+
|
|
864
|
+
const stateFieldConflicts = detectStateFieldConflicts({
|
|
865
|
+
files: filesWithSections,
|
|
866
|
+
});
|
|
867
|
+
const fileStateFieldConflictMap: Map<string, string[]> =
|
|
868
|
+
buildFileStateFieldConflictMap(stateFieldConflicts);
|
|
869
|
+
|
|
870
|
+
const errorCodeConflicts = detectErrorCodeConflicts({
|
|
871
|
+
files: filesWithSections,
|
|
872
|
+
});
|
|
873
|
+
const fileErrorCodeConflictMap: Map<string, string[]> =
|
|
874
|
+
buildFileErrorCodeConflictMap(errorCodeConflicts);
|
|
875
|
+
|
|
876
|
+
const proseConflicts = detectProseConstraintConflicts({
|
|
877
|
+
files: filesWithSections,
|
|
878
|
+
});
|
|
879
|
+
const fileProseConflictMap: Map<string, string[]> =
|
|
880
|
+
buildFileProseConflictMap(proseConflicts);
|
|
881
|
+
|
|
882
|
+
const oversizedTocMap: Map<number, string[]> = new Map();
|
|
883
|
+
for (const fileIndex of pendingArray) {
|
|
884
|
+
const state = props.fileStates[fileIndex]!;
|
|
885
|
+
if (state.file.filename === "00-toc.md" && state.sectionResults) {
|
|
886
|
+
const violations = detectOversizedToc(state.sectionResults);
|
|
887
|
+
if (violations.length > 0) {
|
|
888
|
+
oversizedTocMap.set(fileIndex, violations);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Build mechanical violation summary for LLM context
|
|
894
|
+
const allMechanicalViolations: string[] = [
|
|
895
|
+
...criticalConflicts.map(
|
|
896
|
+
(c) =>
|
|
897
|
+
`Constraint conflict: ${c.key} — ${c.values.map((v) => `"${v.display}" in [${v.files.join(", ")}]`).join(" vs ")}`,
|
|
898
|
+
),
|
|
899
|
+
...attributeDuplicates.map(
|
|
900
|
+
(d) => `Attribute duplication: ${d.key} in [${d.files.join(", ")}]`,
|
|
901
|
+
),
|
|
902
|
+
...enumConflicts.map(
|
|
903
|
+
(c) =>
|
|
904
|
+
`Enum conflict: ${c.key} — ${c.values.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`).join(" vs ")}`,
|
|
905
|
+
),
|
|
906
|
+
...errorCodeConflicts.map(
|
|
907
|
+
(c) =>
|
|
908
|
+
`Error code conflict: ${c.conditionKey} — ${c.codes.map((cd) => `HTTP ${cd.httpStatus} in [${cd.files.join(", ")}]`).join(" vs ")}`,
|
|
909
|
+
),
|
|
910
|
+
...proseConflicts.map(
|
|
911
|
+
(c) =>
|
|
912
|
+
`Prose constraint conflict: ${c.entityAttr} — canonical [${c.canonicalValues.join(", ")}] vs prose [${c.proseValues.join(", ")}] in ${c.file}`,
|
|
913
|
+
),
|
|
914
|
+
];
|
|
915
|
+
const mechanicalViolationSummary =
|
|
916
|
+
allMechanicalViolations.length > 0
|
|
917
|
+
? allMechanicalViolations.join("\n")
|
|
918
|
+
: undefined;
|
|
919
|
+
|
|
920
|
+
// Pass 2b: Cross-file semantic LLM review (with mechanical violations excluded)
|
|
921
|
+
analyzeDebug(`section cross-file-review-start attempt=${attempt}`);
|
|
922
|
+
let crossFileReviewEvent: AutoBeAnalyzeSectionReviewEvent | null = null;
|
|
923
|
+
try {
|
|
924
|
+
crossFileReviewEvent = await orchestrateAnalyzeSectionCrossFileReview(
|
|
925
|
+
ctx,
|
|
926
|
+
{
|
|
927
|
+
scenario: props.scenario,
|
|
928
|
+
allFileSummaries: props.fileStates
|
|
929
|
+
.filter((s) => s.file.filename !== "00-toc.md")
|
|
930
|
+
.map((state) => {
|
|
931
|
+
const fi = props.fileStates.indexOf(state);
|
|
932
|
+
return {
|
|
933
|
+
file: state.file,
|
|
934
|
+
moduleEvent: state.moduleResult!,
|
|
935
|
+
unitEvents: state.unitResults!,
|
|
936
|
+
sectionEvents: state.sectionResults!,
|
|
937
|
+
status: pendingIndices.has(fi)
|
|
938
|
+
? attempt === 0
|
|
939
|
+
? ("new" as const)
|
|
940
|
+
: ("rewritten" as const)
|
|
941
|
+
: ("approved" as const),
|
|
942
|
+
};
|
|
943
|
+
}),
|
|
944
|
+
mechanicalViolationSummary,
|
|
945
|
+
progress: props.crossFileSectionReviewProgress,
|
|
946
|
+
promptCacheKey,
|
|
947
|
+
retry: attempt,
|
|
948
|
+
},
|
|
949
|
+
);
|
|
950
|
+
} catch (e) {
|
|
951
|
+
if (
|
|
952
|
+
e instanceof AgenticaValidationError ||
|
|
953
|
+
e instanceof AutoBeTimeoutError ||
|
|
954
|
+
e instanceof AutoBePreliminaryExhaustedError
|
|
955
|
+
) {
|
|
956
|
+
analyzeDebug(
|
|
957
|
+
`section cross-file-review-force-pass attempt=${attempt} error=${(e as Error).constructor.name} — force-passing all pending files`,
|
|
958
|
+
);
|
|
959
|
+
for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
throw e;
|
|
963
|
+
}
|
|
964
|
+
analyzeDebug(
|
|
965
|
+
`section cross-file-review-done attempt=${attempt} results=${crossFileReviewEvent.fileResults.length}`,
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
// Merge results from both passes
|
|
969
|
+
const crossFileResultMap: Map<
|
|
970
|
+
number,
|
|
971
|
+
AutoBeAnalyzeSectionReviewFileResult
|
|
972
|
+
> = new Map();
|
|
973
|
+
const validCrossFileResults = filterValidFileResults(
|
|
974
|
+
crossFileReviewEvent.fileResults,
|
|
975
|
+
props.fileStates.length,
|
|
976
|
+
"Section cross-file review",
|
|
977
|
+
);
|
|
978
|
+
for (const fr of validCrossFileResults)
|
|
979
|
+
crossFileResultMap.set(fr.fileIndex, fr);
|
|
980
|
+
|
|
981
|
+
for (const fileIndex of pendingArray) {
|
|
982
|
+
const state: IFileState = props.fileStates[fileIndex]!;
|
|
983
|
+
|
|
984
|
+
// Increment review count and force-pass if exceeded limit
|
|
985
|
+
state.sectionReviewCount = (state.sectionReviewCount ?? 0) + 1;
|
|
986
|
+
if (state.sectionReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
|
|
987
|
+
analyzeDebug(
|
|
988
|
+
`[orchestrateAnalyze] Section stage: force-passing (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded) for file "${state.file.filename}"`,
|
|
989
|
+
);
|
|
990
|
+
pendingIndices.delete(fileIndex);
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const perFileEvent = perFileReviewResults.get(fileIndex);
|
|
995
|
+
const perFileResult = perFileEvent?.fileResults[0];
|
|
996
|
+
const crossFileResult = crossFileResultMap.get(fileIndex);
|
|
997
|
+
|
|
998
|
+
const perFileApproved = perFileResult?.approved ?? true;
|
|
999
|
+
const crossFileApproved = crossFileResult?.approved ?? true;
|
|
1000
|
+
|
|
1001
|
+
// Check if this file has programmatically-detected critical conflicts
|
|
1002
|
+
const filename = state.file.filename;
|
|
1003
|
+
const fileCriticalConflicts = fileConflictMap.get(filename) ?? [];
|
|
1004
|
+
const fileAttrDuplicates = fileAttributeDuplicateMap.get(filename) ?? [];
|
|
1005
|
+
const fileEnumConflicts = fileEnumConflictMap.get(filename) ?? [];
|
|
1006
|
+
const filePermissionConflicts =
|
|
1007
|
+
filePermissionConflictMap.get(filename) ?? [];
|
|
1008
|
+
const fileStateFieldConflicts =
|
|
1009
|
+
fileStateFieldConflictMap.get(filename) ?? [];
|
|
1010
|
+
const fileErrorCodeConflicts =
|
|
1011
|
+
fileErrorCodeConflictMap.get(filename) ?? [];
|
|
1012
|
+
const fileOversizedToc = oversizedTocMap.get(fileIndex) ?? [];
|
|
1013
|
+
const fileProseConflicts = fileProseConflictMap.get(filename) ?? [];
|
|
1014
|
+
const hasCriticalConflict =
|
|
1015
|
+
fileCriticalConflicts.length > 0 ||
|
|
1016
|
+
fileAttrDuplicates.length > 0 ||
|
|
1017
|
+
fileEnumConflicts.length > 0 ||
|
|
1018
|
+
filePermissionConflicts.length > 0 ||
|
|
1019
|
+
fileStateFieldConflicts.length > 0 ||
|
|
1020
|
+
fileErrorCodeConflicts.length > 0 ||
|
|
1021
|
+
fileOversizedToc.length > 0 ||
|
|
1022
|
+
fileProseConflicts.length > 0;
|
|
1023
|
+
|
|
1024
|
+
// Decision logic:
|
|
1025
|
+
// 1. per-file reject → reject (unchanged)
|
|
1026
|
+
// 2. per-file approve + critical conflict detected → reject (NEW: patch-first)
|
|
1027
|
+
// 3. per-file approve + no critical conflict → approve (unchanged)
|
|
1028
|
+
const approved = perFileApproved && !hasCriticalConflict;
|
|
1029
|
+
|
|
1030
|
+
const structuredPerFileIssues =
|
|
1031
|
+
collectStructuredReviewIssues(perFileResult);
|
|
1032
|
+
const structuredCrossFileIssues =
|
|
1033
|
+
collectStructuredReviewIssues(crossFileResult);
|
|
1034
|
+
const programmaticIssues = buildProgrammaticSectionIssues({
|
|
1035
|
+
fileCriticalConflicts,
|
|
1036
|
+
fileAttrDuplicates,
|
|
1037
|
+
fileEnumConflicts,
|
|
1038
|
+
filePermissionConflicts,
|
|
1039
|
+
fileStateFieldConflicts,
|
|
1040
|
+
fileErrorCodeConflicts,
|
|
1041
|
+
fileOversizedToc,
|
|
1042
|
+
fileProseConflicts,
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
if (approved) {
|
|
1046
|
+
// NOTE: revisedSections intentionally ignored — approved means pass as-is.
|
|
1047
|
+
// Applying revisedSections caused infinite re-write loops (sections kept growing).
|
|
1048
|
+
// Pass cross-file feedback as advisory for next retry's context
|
|
1049
|
+
if (!crossFileApproved && crossFileResult?.feedback) {
|
|
1050
|
+
state.sectionFeedback = `[Cross-file advisory] ${crossFileResult.feedback}`;
|
|
1051
|
+
}
|
|
1052
|
+
state.sectionRetryCount = 0;
|
|
1053
|
+
state.sectionStagnationCount = 0;
|
|
1054
|
+
state.lastSectionContentSignature = undefined;
|
|
1055
|
+
state.lastSectionRejectionSignature = undefined;
|
|
1056
|
+
pendingIndices.delete(fileIndex);
|
|
1057
|
+
} else if (!perFileApproved) {
|
|
1058
|
+
// Per-file rejected: store only the latest per-file feedback (no accumulation)
|
|
1059
|
+
state.sectionFeedback = formatStructuredIssuesForRetry({
|
|
1060
|
+
fallbackFeedback: perFileResult?.feedback ?? "",
|
|
1061
|
+
issues: structuredPerFileIssues,
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
// Use only per-file rejectedModuleUnits (no cross-file merge)
|
|
1065
|
+
state.rejectedModuleUnits = normalizeRejectedModuleUnits(
|
|
1066
|
+
perFileResult?.rejectedModuleUnits ?? null,
|
|
1067
|
+
structuredPerFileIssues,
|
|
1068
|
+
);
|
|
1069
|
+
// Fallback: infer targets from issues to avoid full-file rewrite
|
|
1070
|
+
if (state.rejectedModuleUnits === null) {
|
|
1071
|
+
state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
|
|
1072
|
+
structuredPerFileIssues,
|
|
1073
|
+
state.unitResults!,
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
analyzeDebug(
|
|
1077
|
+
`section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
|
|
1078
|
+
state.rejectedModuleUnits,
|
|
1079
|
+
)} issues=${formatReviewIssuesSummary(structuredPerFileIssues)} feedback=${truncateForDebug(
|
|
1080
|
+
state.sectionFeedback ?? "",
|
|
1081
|
+
500,
|
|
1082
|
+
)}`,
|
|
1083
|
+
);
|
|
1084
|
+
} else {
|
|
1085
|
+
// Critical conflict rejected (per-file approved but programmatic violations exist)
|
|
1086
|
+
// Use cross-file rejectedModuleUnits for targeted patch if available
|
|
1087
|
+
state.sectionFeedback = formatStructuredIssuesForRetry({
|
|
1088
|
+
fallbackFeedback:
|
|
1089
|
+
`[Critical conflict] ${[
|
|
1090
|
+
...fileCriticalConflicts,
|
|
1091
|
+
...fileAttrDuplicates,
|
|
1092
|
+
...fileEnumConflicts,
|
|
1093
|
+
...fileProseConflicts,
|
|
1094
|
+
].join("; ")}` +
|
|
1095
|
+
(crossFileResult?.feedback ? `\n${crossFileResult.feedback}` : ""),
|
|
1096
|
+
issues: [...programmaticIssues, ...structuredCrossFileIssues],
|
|
1097
|
+
});
|
|
1098
|
+
state.rejectedModuleUnits = normalizeRejectedModuleUnits(
|
|
1099
|
+
crossFileResult?.rejectedModuleUnits ?? null,
|
|
1100
|
+
[...programmaticIssues, ...structuredCrossFileIssues],
|
|
1101
|
+
);
|
|
1102
|
+
// Fallback: infer targets from issues to avoid full-file rewrite
|
|
1103
|
+
if (state.rejectedModuleUnits === null) {
|
|
1104
|
+
state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
|
|
1105
|
+
[...programmaticIssues, ...structuredCrossFileIssues],
|
|
1106
|
+
state.unitResults!,
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
analyzeDebug(
|
|
1110
|
+
`section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
|
|
1111
|
+
state.rejectedModuleUnits,
|
|
1112
|
+
)} issues=${formatReviewIssuesSummary([
|
|
1113
|
+
...programmaticIssues,
|
|
1114
|
+
...structuredCrossFileIssues,
|
|
1115
|
+
])} feedback=${truncateForDebug(state.sectionFeedback ?? "", 500)}`,
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (!approved) {
|
|
1120
|
+
const contentSignature = buildSectionContentSignature(state);
|
|
1121
|
+
const rejectionSignature = buildSectionRejectionSignature({
|
|
1122
|
+
rejectedModuleUnits: state.rejectedModuleUnits ?? null,
|
|
1123
|
+
feedback: state.sectionFeedback ?? "",
|
|
1124
|
+
});
|
|
1125
|
+
const isStagnant =
|
|
1126
|
+
state.lastSectionContentSignature === contentSignature &&
|
|
1127
|
+
state.lastSectionRejectionSignature === rejectionSignature;
|
|
1128
|
+
state.sectionStagnationCount = isStagnant
|
|
1129
|
+
? (state.sectionStagnationCount ?? 0) + 1
|
|
1130
|
+
: 0;
|
|
1131
|
+
state.sectionRetryCount = (state.sectionRetryCount ?? 0) + 1;
|
|
1132
|
+
state.lastSectionContentSignature = contentSignature;
|
|
1133
|
+
state.lastSectionRejectionSignature = rejectionSignature;
|
|
1134
|
+
|
|
1135
|
+
if ((state.sectionRetryCount ?? 0) > ANALYZE_SECTION_FILE_MAX_RETRY) {
|
|
1136
|
+
analyzeDebug(
|
|
1137
|
+
`[orchestrateAnalyze] Section stage: force-passing (max retry exceeded: ${ANALYZE_SECTION_FILE_MAX_RETRY}) for file "${state.file.filename}"`,
|
|
1138
|
+
);
|
|
1139
|
+
pendingIndices.delete(fileIndex);
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
if (
|
|
1143
|
+
(state.sectionStagnationCount ?? 0) >= ANALYZE_SECTION_STAGNATION_MAX
|
|
1144
|
+
) {
|
|
1145
|
+
analyzeDebug(
|
|
1146
|
+
`[orchestrateAnalyze] Section stage: force-passing (stagnation detected ${state.sectionStagnationCount}x) for file "${state.file.filename}"`,
|
|
1147
|
+
);
|
|
1148
|
+
pendingIndices.delete(fileIndex);
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
if (pendingIndices.size > 0) {
|
|
1156
|
+
analyzeDebug(
|
|
1157
|
+
`[orchestrateAnalyze] Section stage: force-passing after max retries for files: ${[
|
|
1158
|
+
...pendingIndices,
|
|
1159
|
+
]
|
|
1160
|
+
.map((i) => props.fileStates[i]!.file.filename)
|
|
1161
|
+
.join(", ")}`,
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// ─── Section-stage helper functions ───
|
|
1167
|
+
|
|
1168
|
+
function computeSectionBatchSize(props: {
|
|
1169
|
+
attempt: number;
|
|
1170
|
+
pendingCount: number;
|
|
1171
|
+
}): number {
|
|
1172
|
+
return Math.min(8, props.pendingCount);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function chunkSectionFileIndices(indices: number[], size: number): number[][] {
|
|
1176
|
+
if (indices.length === 0) return [];
|
|
1177
|
+
if (size <= 0 || size >= indices.length) return [indices];
|
|
1178
|
+
const chunks: number[][] = [];
|
|
1179
|
+
for (let i = 0; i < indices.length; i += size)
|
|
1180
|
+
chunks.push(indices.slice(i, i + size));
|
|
1181
|
+
return chunks;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
function buildRejectedSet(
|
|
1185
|
+
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1186
|
+
): Set<string> | null {
|
|
1187
|
+
if (rejected == null) return null;
|
|
1188
|
+
if (rejected.length === 0) return null;
|
|
1189
|
+
const set: Set<string> = new Set();
|
|
1190
|
+
for (const entry of rejected) {
|
|
1191
|
+
for (const ui of entry.unitIndices) {
|
|
1192
|
+
set.add(`${entry.moduleIndex}:${ui}`);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return set.size > 0 ? set : null;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
interface ISectionAwareFeedback {
|
|
1199
|
+
feedback: string;
|
|
1200
|
+
sectionIndices: number[] | null;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function buildFeedbackMap(
|
|
1204
|
+
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1205
|
+
): Map<string, ISectionAwareFeedback> {
|
|
1206
|
+
const map: Map<string, ISectionAwareFeedback> = new Map();
|
|
1207
|
+
if (rejected == null) return map;
|
|
1208
|
+
for (const entry of rejected) {
|
|
1209
|
+
for (const ui of entry.unitIndices) {
|
|
1210
|
+
map.set(`${entry.moduleIndex}:${ui}`, {
|
|
1211
|
+
feedback: formatRejectedModuleUnitFeedback(entry, ui),
|
|
1212
|
+
sectionIndices: entry.sectionIndicesPerUnit?.[ui] ?? null,
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return map;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function isSectionRejected(
|
|
1220
|
+
rejectedSet: Set<string> | null,
|
|
1221
|
+
moduleIndex: number,
|
|
1222
|
+
unitIndex: number,
|
|
1223
|
+
): boolean {
|
|
1224
|
+
if (rejectedSet === null) return true;
|
|
1225
|
+
return rejectedSet.has(`${moduleIndex}:${unitIndex}`);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
function filterValidFileResults<T extends { fileIndex: number }>(
|
|
1229
|
+
fileResults: T[],
|
|
1230
|
+
fileCount: number,
|
|
1231
|
+
stage: string,
|
|
1232
|
+
): T[] {
|
|
1233
|
+
return fileResults.filter((fr) => {
|
|
1234
|
+
if (
|
|
1235
|
+
Number.isInteger(fr.fileIndex) &&
|
|
1236
|
+
fr.fileIndex >= 0 &&
|
|
1237
|
+
fr.fileIndex < fileCount
|
|
1238
|
+
) {
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
1241
|
+
console.warn(
|
|
1242
|
+
`[orchestrateAnalyze] ${stage}: invalid fileIndex ${fr.fileIndex} (valid: 0-${fileCount - 1})`,
|
|
1243
|
+
);
|
|
1244
|
+
return false;
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
function formatRejectedModuleUnitFeedback(
|
|
1249
|
+
entry: AutoBeAnalyzeSectionReviewRejectedModuleUnit,
|
|
1250
|
+
unitIndex: number,
|
|
1251
|
+
): string {
|
|
1252
|
+
const scopedIssues = (entry.issues ?? []).filter(
|
|
1253
|
+
(issue) =>
|
|
1254
|
+
issue.moduleIndex === entry.moduleIndex &&
|
|
1255
|
+
(issue.unitIndex === null || issue.unitIndex === unitIndex),
|
|
1256
|
+
);
|
|
1257
|
+
if (scopedIssues.length === 0) return entry.feedback;
|
|
1258
|
+
return [
|
|
1259
|
+
entry.feedback,
|
|
1260
|
+
...scopedIssues.map(
|
|
1261
|
+
(issue) =>
|
|
1262
|
+
`- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}`,
|
|
1263
|
+
),
|
|
1264
|
+
].join("\n");
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function collectStructuredReviewIssues(
|
|
1268
|
+
result:
|
|
1269
|
+
| {
|
|
1270
|
+
feedback: string;
|
|
1271
|
+
rejectedModuleUnits?:
|
|
1272
|
+
| AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
|
|
1273
|
+
| null;
|
|
1274
|
+
issues?: AutoBeAnalyzeSectionReviewIssue[] | null;
|
|
1275
|
+
}
|
|
1276
|
+
| undefined,
|
|
1277
|
+
): AutoBeAnalyzeSectionReviewIssue[] {
|
|
1278
|
+
if (!result) return [];
|
|
1279
|
+
const collected: AutoBeAnalyzeSectionReviewIssue[] = [];
|
|
1280
|
+
|
|
1281
|
+
for (const issue of result.issues ?? []) collected.push(issue);
|
|
1282
|
+
for (const group of result.rejectedModuleUnits ?? []) {
|
|
1283
|
+
for (const issue of group.issues ?? []) collected.push(issue);
|
|
1284
|
+
if ((group.issues?.length ?? 0) === 0) {
|
|
1285
|
+
for (const unitIndex of group.unitIndices) {
|
|
1286
|
+
collected.push({
|
|
1287
|
+
ruleCode: "section_review_reject",
|
|
1288
|
+
moduleIndex: group.moduleIndex,
|
|
1289
|
+
unitIndex,
|
|
1290
|
+
fixInstruction:
|
|
1291
|
+
group.feedback || result.feedback || "Fix review issues.",
|
|
1292
|
+
evidence: null,
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
if (collected.length === 0 && result.feedback.trim().length > 0) {
|
|
1299
|
+
collected.push({
|
|
1300
|
+
ruleCode: "section_review_reject",
|
|
1301
|
+
moduleIndex: null,
|
|
1302
|
+
unitIndex: null,
|
|
1303
|
+
fixInstruction: result.feedback,
|
|
1304
|
+
evidence: null,
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
return dedupeReviewIssues(collected);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function buildProgrammaticSectionIssues(props: {
|
|
1311
|
+
fileCriticalConflicts: string[];
|
|
1312
|
+
fileAttrDuplicates: string[];
|
|
1313
|
+
fileEnumConflicts: string[];
|
|
1314
|
+
filePermissionConflicts: string[];
|
|
1315
|
+
fileStateFieldConflicts: string[];
|
|
1316
|
+
fileErrorCodeConflicts: string[];
|
|
1317
|
+
fileOversizedToc: string[];
|
|
1318
|
+
fileProseConflicts: string[];
|
|
1319
|
+
}): AutoBeAnalyzeSectionReviewIssue[] {
|
|
1320
|
+
return [
|
|
1321
|
+
...props.fileCriticalConflicts.map((detail) => ({
|
|
1322
|
+
ruleCode: "cross_file_constraint_conflict",
|
|
1323
|
+
moduleIndex: null,
|
|
1324
|
+
unitIndex: null,
|
|
1325
|
+
fixInstruction:
|
|
1326
|
+
"Align conflicting constraints/values with other files and preserve one canonical value.",
|
|
1327
|
+
evidence: detail,
|
|
1328
|
+
})),
|
|
1329
|
+
...props.fileAttrDuplicates.map((detail) => ({
|
|
1330
|
+
ruleCode: "cross_file_attribute_duplicate",
|
|
1331
|
+
moduleIndex: null,
|
|
1332
|
+
unitIndex: null,
|
|
1333
|
+
fixInstruction:
|
|
1334
|
+
"Remove duplicate attribute specifications across files and keep ownership in one file.",
|
|
1335
|
+
evidence: detail,
|
|
1336
|
+
})),
|
|
1337
|
+
...props.fileEnumConflicts.map((detail) => ({
|
|
1338
|
+
ruleCode: "cross_file_enum_conflict",
|
|
1339
|
+
moduleIndex: null,
|
|
1340
|
+
unitIndex: null,
|
|
1341
|
+
fixInstruction:
|
|
1342
|
+
"Align enum values with the canonical definition from the first file that specified this attribute. Use the exact same enum set.",
|
|
1343
|
+
evidence: detail,
|
|
1344
|
+
})),
|
|
1345
|
+
...props.filePermissionConflicts.map((detail) => ({
|
|
1346
|
+
ruleCode: "cross_file_permission_conflict",
|
|
1347
|
+
moduleIndex: null,
|
|
1348
|
+
unitIndex: null,
|
|
1349
|
+
fixInstruction:
|
|
1350
|
+
"Align permission rules with the canonical definition. If the first file says 'denied', all files must say 'denied' for the same actor→operation.",
|
|
1351
|
+
evidence: detail,
|
|
1352
|
+
})),
|
|
1353
|
+
...props.fileStateFieldConflicts.map((detail) => ({
|
|
1354
|
+
ruleCode: "cross_file_state_field_conflict",
|
|
1355
|
+
moduleIndex: null,
|
|
1356
|
+
unitIndex: null,
|
|
1357
|
+
fixInstruction:
|
|
1358
|
+
"Use ONE canonical approach for state fields. If other files use 'deletedAt: datetime', do NOT use 'isDeleted: boolean'. Pick one and align.",
|
|
1359
|
+
evidence: detail,
|
|
1360
|
+
})),
|
|
1361
|
+
...props.fileErrorCodeConflicts.map((detail) => ({
|
|
1362
|
+
ruleCode: "cross_file_error_code_conflict",
|
|
1363
|
+
moduleIndex: null,
|
|
1364
|
+
unitIndex: null,
|
|
1365
|
+
fixInstruction:
|
|
1366
|
+
"Use the canonical error code defined in the first file. Do NOT invent alternative error codes for the same condition.",
|
|
1367
|
+
evidence: detail,
|
|
1368
|
+
})),
|
|
1369
|
+
...props.fileOversizedToc.map((detail) => ({
|
|
1370
|
+
ruleCode: "oversized_toc",
|
|
1371
|
+
moduleIndex: null,
|
|
1372
|
+
unitIndex: null,
|
|
1373
|
+
fixInstruction:
|
|
1374
|
+
"TOC must be a concise navigation aid. Remove detailed requirements, keep only navigation tables and brief summaries.",
|
|
1375
|
+
evidence: detail,
|
|
1376
|
+
})),
|
|
1377
|
+
...props.fileProseConflicts.map((detail) => ({
|
|
1378
|
+
ruleCode: "cross_file_prose_constraint_conflict",
|
|
1379
|
+
moduleIndex: null,
|
|
1380
|
+
unitIndex: null,
|
|
1381
|
+
fixInstruction:
|
|
1382
|
+
"Remove the restated constraint value and use a backtick reference to the canonical definition in 02-domain-model instead. Example: 'THE system SHALL validate `User.bio` per entity constraints (see 02-domain-model)'",
|
|
1383
|
+
evidence: detail,
|
|
1384
|
+
})),
|
|
1385
|
+
];
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
function buildSectionIndicesPerUnit(
|
|
1389
|
+
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1390
|
+
moduleIndex: number,
|
|
1391
|
+
unitIndices: number[],
|
|
1392
|
+
): Record<number, number[]> | null {
|
|
1393
|
+
const map: Record<number, Set<number>> = {};
|
|
1394
|
+
let hasSectionLevel = false;
|
|
1395
|
+
|
|
1396
|
+
for (const issue of issues) {
|
|
1397
|
+
if (
|
|
1398
|
+
issue.moduleIndex === moduleIndex &&
|
|
1399
|
+
issue.unitIndex !== null &&
|
|
1400
|
+
unitIndices.includes(issue.unitIndex) &&
|
|
1401
|
+
issue.sectionIndex !== null &&
|
|
1402
|
+
issue.sectionIndex !== undefined &&
|
|
1403
|
+
Number.isInteger(issue.sectionIndex) &&
|
|
1404
|
+
issue.sectionIndex >= 0
|
|
1405
|
+
) {
|
|
1406
|
+
if (!map[issue.unitIndex]) map[issue.unitIndex] = new Set();
|
|
1407
|
+
map[issue.unitIndex]!.add(issue.sectionIndex);
|
|
1408
|
+
hasSectionLevel = true;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
if (!hasSectionLevel) return null;
|
|
1413
|
+
|
|
1414
|
+
const result: Record<number, number[]> = {};
|
|
1415
|
+
for (const [ui, sectionSet] of Object.entries(map)) {
|
|
1416
|
+
result[Number(ui)] = [...sectionSet].sort((a, b) => a - b);
|
|
1417
|
+
}
|
|
1418
|
+
return result;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
function normalizeRejectedModuleUnits(
|
|
1422
|
+
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1423
|
+
fileIssues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1424
|
+
): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
|
|
1425
|
+
if (rejected == null) return null;
|
|
1426
|
+
return rejected.map((entry) => {
|
|
1427
|
+
const enrichedIssues =
|
|
1428
|
+
(entry.issues?.length ?? 0) > 0
|
|
1429
|
+
? dedupeReviewIssues(entry.issues ?? [])
|
|
1430
|
+
: dedupeReviewIssues(
|
|
1431
|
+
fileIssues.filter(
|
|
1432
|
+
(issue) =>
|
|
1433
|
+
issue.moduleIndex === entry.moduleIndex &&
|
|
1434
|
+
(issue.unitIndex === null ||
|
|
1435
|
+
entry.unitIndices.includes(issue.unitIndex)),
|
|
1436
|
+
),
|
|
1437
|
+
);
|
|
1438
|
+
|
|
1439
|
+
const sectionIndicesPerUnit =
|
|
1440
|
+
entry.sectionIndicesPerUnit ??
|
|
1441
|
+
buildSectionIndicesPerUnit(
|
|
1442
|
+
enrichedIssues,
|
|
1443
|
+
entry.moduleIndex,
|
|
1444
|
+
entry.unitIndices,
|
|
1445
|
+
);
|
|
1446
|
+
|
|
1447
|
+
return {
|
|
1448
|
+
...entry,
|
|
1449
|
+
issues: enrichedIssues,
|
|
1450
|
+
sectionIndicesPerUnit,
|
|
1451
|
+
};
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* Infer rejectedModuleUnits from structured issues when the LLM review didn't
|
|
1457
|
+
* provide explicit rejection targets. This prevents full-file rewrites when
|
|
1458
|
+
* only specific module/unit pairs have issues.
|
|
1459
|
+
*/
|
|
1460
|
+
function inferRejectedModuleUnitsFromIssues(
|
|
1461
|
+
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1462
|
+
unitResults: AutoBeAnalyzeWriteUnitEvent[],
|
|
1463
|
+
): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
|
|
1464
|
+
const moduleUnitMap = new Map<number, Set<number>>();
|
|
1465
|
+
let hasTargetedIssue = false;
|
|
1466
|
+
|
|
1467
|
+
for (const issue of issues) {
|
|
1468
|
+
if (issue.moduleIndex !== null && issue.moduleIndex !== undefined) {
|
|
1469
|
+
hasTargetedIssue = true;
|
|
1470
|
+
if (!moduleUnitMap.has(issue.moduleIndex)) {
|
|
1471
|
+
moduleUnitMap.set(issue.moduleIndex, new Set());
|
|
1472
|
+
}
|
|
1473
|
+
if (issue.unitIndex !== null && issue.unitIndex !== undefined) {
|
|
1474
|
+
moduleUnitMap.get(issue.moduleIndex)!.add(issue.unitIndex);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
if (!hasTargetedIssue) return null;
|
|
1480
|
+
|
|
1481
|
+
const result: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] = [];
|
|
1482
|
+
for (const [moduleIndex, unitIndexSet] of moduleUnitMap) {
|
|
1483
|
+
// If no specific units targeted, include all units for this module
|
|
1484
|
+
let unitIndices: number[];
|
|
1485
|
+
if (unitIndexSet.size === 0) {
|
|
1486
|
+
const unitEvent = unitResults[moduleIndex];
|
|
1487
|
+
unitIndices = unitEvent
|
|
1488
|
+
? Array.from({ length: unitEvent.unitSections.length }, (_, i) => i)
|
|
1489
|
+
: [];
|
|
1490
|
+
} else {
|
|
1491
|
+
unitIndices = [...unitIndexSet].sort((a, b) => a - b);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
const moduleIssues = dedupeReviewIssues(
|
|
1495
|
+
issues.filter(
|
|
1496
|
+
(i) =>
|
|
1497
|
+
i.moduleIndex === moduleIndex &&
|
|
1498
|
+
(i.unitIndex === null || unitIndices.includes(i.unitIndex)),
|
|
1499
|
+
),
|
|
1500
|
+
);
|
|
1501
|
+
|
|
1502
|
+
result.push({
|
|
1503
|
+
moduleIndex,
|
|
1504
|
+
unitIndices,
|
|
1505
|
+
feedback: moduleIssues.map((i) => i.fixInstruction).join("; "),
|
|
1506
|
+
issues: moduleIssues,
|
|
1507
|
+
sectionIndicesPerUnit: buildSectionIndicesPerUnit(
|
|
1508
|
+
moduleIssues,
|
|
1509
|
+
moduleIndex,
|
|
1510
|
+
unitIndices,
|
|
1511
|
+
),
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
return result.length > 0 ? result : null;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
function formatStructuredIssuesForRetry(props: {
|
|
1519
|
+
fallbackFeedback: string;
|
|
1520
|
+
issues: AutoBeAnalyzeSectionReviewIssue[];
|
|
1521
|
+
}): string {
|
|
1522
|
+
if (props.issues.length === 0) return props.fallbackFeedback;
|
|
1523
|
+
const lines = props.issues.map(
|
|
1524
|
+
(issue) =>
|
|
1525
|
+
`- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}` +
|
|
1526
|
+
(issue.evidence ? ` | evidence=${issue.evidence}` : ""),
|
|
1527
|
+
);
|
|
1528
|
+
return `${props.fallbackFeedback}\n\n[STRUCTURED REVIEW ISSUES]\n${lines.join("\n")}`.trim();
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
function formatIssueTarget(
|
|
1532
|
+
issue: Pick<AutoBeAnalyzeSectionReviewIssue, "moduleIndex" | "unitIndex"> &
|
|
1533
|
+
Partial<Pick<AutoBeAnalyzeSectionReviewIssue, "sectionIndex">>,
|
|
1534
|
+
): string {
|
|
1535
|
+
const parts: string[] = [];
|
|
1536
|
+
if (issue.moduleIndex !== null && issue.moduleIndex !== undefined)
|
|
1537
|
+
parts.push(`m${issue.moduleIndex}`);
|
|
1538
|
+
if (issue.unitIndex !== null && issue.unitIndex !== undefined)
|
|
1539
|
+
parts.push(`u${issue.unitIndex}`);
|
|
1540
|
+
if (issue.sectionIndex !== null && issue.sectionIndex !== undefined)
|
|
1541
|
+
parts.push(`s${issue.sectionIndex}`);
|
|
1542
|
+
return parts.length ? parts.join(".") : "file";
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
function dedupeReviewIssues(
|
|
1546
|
+
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1547
|
+
): AutoBeAnalyzeSectionReviewIssue[] {
|
|
1548
|
+
const map = new Map<string, AutoBeAnalyzeSectionReviewIssue>();
|
|
1549
|
+
for (const issue of issues) {
|
|
1550
|
+
const key = [
|
|
1551
|
+
issue.ruleCode,
|
|
1552
|
+
issue.moduleIndex ?? "x",
|
|
1553
|
+
issue.unitIndex ?? "x",
|
|
1554
|
+
issue.sectionIndex ?? "x",
|
|
1555
|
+
issue.fixInstruction,
|
|
1556
|
+
].join("|");
|
|
1557
|
+
if (!map.has(key)) map.set(key, issue);
|
|
1558
|
+
}
|
|
1559
|
+
return [...map.values()];
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// ─── Per-module review helpers ───
|
|
1563
|
+
|
|
1564
|
+
function buildSiblingModuleSummaries(
|
|
1565
|
+
moduleEvent: AutoBeAnalyzeWriteModuleEvent,
|
|
1566
|
+
sectionResults: AutoBeAnalyzeWriteSectionEvent[][],
|
|
1567
|
+
excludeModuleIndex: number,
|
|
1568
|
+
): Array<{
|
|
1569
|
+
moduleIndex: number;
|
|
1570
|
+
title: string;
|
|
1571
|
+
sectionTitles: string[];
|
|
1572
|
+
}> {
|
|
1573
|
+
return sectionResults
|
|
1574
|
+
.map((sectionsForModule, moduleIndex) => ({
|
|
1575
|
+
moduleIndex,
|
|
1576
|
+
title: moduleEvent.moduleSections[moduleIndex]?.title ?? "Unknown",
|
|
1577
|
+
sectionTitles: sectionsForModule.flatMap((se) =>
|
|
1578
|
+
se.sectionSections.map((s) => s.title),
|
|
1579
|
+
),
|
|
1580
|
+
}))
|
|
1581
|
+
.filter((s) => s.moduleIndex !== excludeModuleIndex);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function mergeModuleReviewEvents(
|
|
1585
|
+
moduleReviews: AutoBeAnalyzeSectionReviewEvent[],
|
|
1586
|
+
fileIndex: number,
|
|
1587
|
+
): AutoBeAnalyzeSectionReviewEvent | null {
|
|
1588
|
+
if (moduleReviews.length === 0) return null;
|
|
1589
|
+
|
|
1590
|
+
const allApproved = moduleReviews.every(
|
|
1591
|
+
(r) => r.fileResults[0]?.approved ?? true,
|
|
1592
|
+
);
|
|
1593
|
+
const allFeedback = moduleReviews
|
|
1594
|
+
.map((r) => r.fileResults[0]?.feedback)
|
|
1595
|
+
.filter(Boolean)
|
|
1596
|
+
.join("\n");
|
|
1597
|
+
const allRejectedModuleUnits = moduleReviews.flatMap(
|
|
1598
|
+
(r) => r.fileResults[0]?.rejectedModuleUnits ?? [],
|
|
1599
|
+
);
|
|
1600
|
+
const allIssues = moduleReviews.flatMap(
|
|
1601
|
+
(r) => r.fileResults[0]?.issues ?? [],
|
|
1602
|
+
);
|
|
1603
|
+
|
|
1604
|
+
// Use the last review event as base (for tokenUsage, metric, etc.)
|
|
1605
|
+
const base = moduleReviews[moduleReviews.length - 1]!;
|
|
1606
|
+
return {
|
|
1607
|
+
...base,
|
|
1608
|
+
fileResults: [
|
|
1609
|
+
{
|
|
1610
|
+
fileIndex,
|
|
1611
|
+
approved: allApproved,
|
|
1612
|
+
feedback: allFeedback,
|
|
1613
|
+
revisedSections: null,
|
|
1614
|
+
rejectedModuleUnits:
|
|
1615
|
+
allRejectedModuleUnits.length > 0 ? allRejectedModuleUnits : null,
|
|
1616
|
+
issues: allIssues.length > 0 ? allIssues : null,
|
|
1617
|
+
},
|
|
1618
|
+
],
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// ─── Section-stage helpers ───
|
|
1623
|
+
|
|
1624
|
+
function buildSectionContentSignature(state: IFileState): string {
|
|
1625
|
+
if (!state.sectionResults) return "none";
|
|
1626
|
+
return JSON.stringify(
|
|
1627
|
+
state.sectionResults.map((moduleSections) =>
|
|
1628
|
+
moduleSections.map((unit) =>
|
|
1629
|
+
unit.sectionSections.map((section) => ({
|
|
1630
|
+
title: section.title,
|
|
1631
|
+
// content text included to detect no-progress rewrites
|
|
1632
|
+
content: section.content,
|
|
1633
|
+
})),
|
|
1634
|
+
),
|
|
1635
|
+
),
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function buildSectionRejectionSignature(props: {
|
|
1640
|
+
rejectedModuleUnits:
|
|
1641
|
+
| AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
|
|
1642
|
+
| null
|
|
1643
|
+
| undefined;
|
|
1644
|
+
feedback: string;
|
|
1645
|
+
}): string {
|
|
1646
|
+
return JSON.stringify({
|
|
1647
|
+
rejectedModuleUnits: (props.rejectedModuleUnits ?? []).map((entry) => ({
|
|
1648
|
+
moduleIndex: entry.moduleIndex,
|
|
1649
|
+
unitIndices: [...entry.unitIndices].sort((a, b) => a - b),
|
|
1650
|
+
feedback: entry.feedback,
|
|
1651
|
+
issues: (entry.issues ?? []).map((issue) => ({
|
|
1652
|
+
ruleCode: issue.ruleCode,
|
|
1653
|
+
moduleIndex: issue.moduleIndex,
|
|
1654
|
+
unitIndex: issue.unitIndex,
|
|
1655
|
+
sectionIndex: issue.sectionIndex ?? null,
|
|
1656
|
+
fixInstruction: issue.fixInstruction,
|
|
1657
|
+
})),
|
|
1658
|
+
})),
|
|
1659
|
+
feedback: props.feedback,
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
function formatRejectedModuleUnitsSummary(
|
|
1664
|
+
rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
|
|
1665
|
+
): string {
|
|
1666
|
+
if (!rejected || rejected.length === 0) return "all-or-unknown";
|
|
1667
|
+
return rejected
|
|
1668
|
+
.slice(0, 6)
|
|
1669
|
+
.map((entry) => {
|
|
1670
|
+
const unitParts = entry.unitIndices.map((ui) => {
|
|
1671
|
+
const sectionPart = entry.sectionIndicesPerUnit?.[ui];
|
|
1672
|
+
return sectionPart ? `u${ui}(s${sectionPart.join(",s")})` : `u${ui}`;
|
|
1673
|
+
});
|
|
1674
|
+
return `m${entry.moduleIndex}:${unitParts.join(",") || "-"}`;
|
|
1675
|
+
})
|
|
1676
|
+
.join(" | ");
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
function formatReviewIssuesSummary(
|
|
1680
|
+
issues: AutoBeAnalyzeSectionReviewIssue[],
|
|
1681
|
+
): string {
|
|
1682
|
+
if (issues.length === 0) return "none";
|
|
1683
|
+
return issues
|
|
1684
|
+
.slice(0, 8)
|
|
1685
|
+
.map((issue) => `${issue.ruleCode}@${formatIssueTarget(issue)}`)
|
|
1686
|
+
.join(", ");
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
function truncateForDebug(text: string, max: number): string {
|
|
1690
|
+
const singleLine = text.replace(/\s+/g, " ").trim();
|
|
1691
|
+
if (singleLine.length <= max) return singleLine;
|
|
1692
|
+
return `${singleLine.slice(0, max)}...`;
|
|
1693
|
+
}
|