scai 0.1.178 → 1.0.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/README.md +162 -267
- package/dist/__tests__/CommitSuggesterCmd.test.js +112 -0
- package/dist/__tests__/CommitSuggesterCmd.test.js.map +1 -0
- package/dist/__tests__/EvalReportCmd.test.js +645 -0
- package/dist/__tests__/EvalReportCmd.test.js.map +1 -0
- package/dist/__tests__/ModelCmd.test.js +64 -0
- package/dist/__tests__/ModelCmd.test.js.map +1 -0
- package/dist/__tests__/agents/agentActions.test.js +345 -0
- package/dist/__tests__/agents/agentActions.test.js.map +1 -0
- package/dist/__tests__/agents/agentFeedback.test.js +118 -0
- package/dist/__tests__/agents/agentFeedback.test.js.map +1 -0
- package/dist/__tests__/agents/agentGeneralScope.test.js +74 -0
- package/dist/__tests__/agents/agentGeneralScope.test.js.map +1 -0
- package/dist/__tests__/agents/agentLoop.test.js +1723 -0
- package/dist/__tests__/agents/agentLoop.test.js.map +1 -0
- package/dist/__tests__/agents/agentPolicyState.test.js +948 -0
- package/dist/__tests__/agents/agentPolicyState.test.js.map +1 -0
- package/dist/__tests__/agents/agentReadEvidence.test.js +170 -0
- package/dist/__tests__/agents/agentReadEvidence.test.js.map +1 -0
- package/dist/__tests__/agents/agentReadPersistence.test.js +129 -0
- package/dist/__tests__/agents/agentReadPersistence.test.js.map +1 -0
- package/dist/__tests__/agents/agentResumeCheckpoint.test.js +90 -0
- package/dist/__tests__/agents/agentResumeCheckpoint.test.js.map +1 -0
- package/dist/__tests__/agents/agentSearchBatchPlanner.test.js +289 -0
- package/dist/__tests__/agents/agentSearchBatchPlanner.test.js.map +1 -0
- package/dist/__tests__/agents/agentSearchOwnership.test.js +166 -0
- package/dist/__tests__/agents/agentSearchOwnership.test.js.map +1 -0
- package/dist/__tests__/agents/agentSearchRanking.test.js +139 -0
- package/dist/__tests__/agents/agentSearchRanking.test.js.map +1 -0
- package/dist/__tests__/agents/agentSearchRouting.test.js +584 -0
- package/dist/__tests__/agents/agentSearchRouting.test.js.map +1 -0
- package/dist/__tests__/agents/agentSearchScoring.test.js +23 -0
- package/dist/__tests__/agents/agentSearchScoring.test.js.map +1 -0
- package/dist/__tests__/agents/agentSearchShared.test.js +78 -0
- package/dist/__tests__/agents/agentSearchShared.test.js.map +1 -0
- package/dist/__tests__/agents/agentStateMachine.test.js +58 -0
- package/dist/__tests__/agents/agentStateMachine.test.js.map +1 -0
- package/dist/__tests__/agents/agentTaskPersistence.test.js +156 -0
- package/dist/__tests__/agents/agentTaskPersistence.test.js.map +1 -0
- package/dist/__tests__/agents/agentTools.test.js +69 -0
- package/dist/__tests__/agents/agentTools.test.js.map +1 -0
- package/dist/__tests__/agents/agentTransform.test.js +779 -0
- package/dist/__tests__/agents/agentTransform.test.js.map +1 -0
- package/dist/__tests__/agents/analysisPlanGenStep.test.js +157 -0
- package/dist/__tests__/agents/analysisPlanGenStep.test.js.map +1 -0
- package/dist/__tests__/agents/answerOnlyCompletion.test.js +75 -0
- package/dist/__tests__/agents/answerOnlyCompletion.test.js.map +1 -0
- package/dist/__tests__/agents/decideNextAction.test.js +1662 -0
- package/dist/__tests__/agents/decideNextAction.test.js.map +1 -0
- package/dist/__tests__/agents/deriveFocusFromSearchStep.test.js +258 -0
- package/dist/__tests__/agents/deriveFocusFromSearchStep.test.js.map +1 -0
- package/dist/__tests__/agents/evidenceVerifierStep.test.js +113 -0
- package/dist/__tests__/agents/evidenceVerifierStep.test.js.map +1 -0
- package/dist/__tests__/agents/executionPolicyResolver.test.js +208 -0
- package/dist/__tests__/agents/executionPolicyResolver.test.js.map +1 -0
- package/dist/__tests__/agents/fileCheckStep.test.js +299 -0
- package/dist/__tests__/agents/fileCheckStep.test.js.map +1 -0
- package/dist/__tests__/agents/giveUpEvaluatorStep.test.js +35 -0
- package/dist/__tests__/agents/giveUpEvaluatorStep.test.js.map +1 -0
- package/dist/__tests__/agents/guardState.test.js +297 -0
- package/dist/__tests__/agents/guardState.test.js.map +1 -0
- package/dist/__tests__/agents/mainAgentHeuristics.test.js +72 -0
- package/dist/__tests__/agents/mainAgentHeuristics.test.js.map +1 -0
- package/dist/__tests__/agents/objectiveEvaluatorStep.test.js +60 -0
- package/dist/__tests__/agents/objectiveEvaluatorStep.test.js.map +1 -0
- package/dist/__tests__/agents/outerLoopRecoveryEvaluator.test.js +207 -0
- package/dist/__tests__/agents/outerLoopRecoveryEvaluator.test.js.map +1 -0
- package/dist/__tests__/agents/prompting.test.js +363 -0
- package/dist/__tests__/agents/prompting.test.js.map +1 -0
- package/dist/__tests__/agents/readinessGateStep.test.js +180 -0
- package/dist/__tests__/agents/readinessGateStep.test.js.map +1 -0
- package/dist/__tests__/agents/reasonNextStep.test.js +56 -0
- package/dist/__tests__/agents/reasonNextStep.test.js.map +1 -0
- package/dist/__tests__/agents/reasonNextTaskStep.test.js +284 -0
- package/dist/__tests__/agents/reasonNextTaskStep.test.js.map +1 -0
- package/dist/__tests__/agents/resolveAgentTargetClassification.test.js +170 -0
- package/dist/__tests__/agents/resolveAgentTargetClassification.test.js.map +1 -0
- package/dist/__tests__/agents/resolveProgressState.test.js +526 -0
- package/dist/__tests__/agents/resolveProgressState.test.js.map +1 -0
- package/dist/__tests__/agents/resumeCheckpoint.test.js +50 -0
- package/dist/__tests__/agents/resumeCheckpoint.test.js.map +1 -0
- package/dist/__tests__/agents/routingDecisionStep.test.js +134 -0
- package/dist/__tests__/agents/routingDecisionStep.test.js.map +1 -0
- package/dist/__tests__/agents/scopeClassificationStep.test.js +118 -0
- package/dist/__tests__/agents/scopeClassificationStep.test.js.map +1 -0
- package/dist/__tests__/agents/searchContext.test.js +97 -0
- package/dist/__tests__/agents/searchContext.test.js.map +1 -0
- package/dist/__tests__/agents/selectRelevantSourcesStep.test.js +73 -0
- package/dist/__tests__/agents/selectRelevantSourcesStep.test.js.map +1 -0
- package/dist/__tests__/agents/structuredOutput.test.js +45 -0
- package/dist/__tests__/agents/structuredOutput.test.js.map +1 -0
- package/dist/__tests__/agents/transformPlanGenStep.fallback.test.js +59 -0
- package/dist/__tests__/agents/transformPlanGenStep.fallback.test.js.map +1 -0
- package/dist/__tests__/agents/transformPlanGenStep.test.js +92 -0
- package/dist/__tests__/agents/transformPlanGenStep.test.js.map +1 -0
- package/dist/__tests__/agents/understandIntentStep.test.js +237 -0
- package/dist/__tests__/agents/understandIntentStep.test.js.map +1 -0
- package/dist/__tests__/agents/understandResumeContext.test.js +65 -0
- package/dist/__tests__/agents/understandResumeContext.test.js.map +1 -0
- package/dist/__tests__/agents/understandScope.test.js +227 -0
- package/dist/__tests__/agents/understandScope.test.js.map +1 -0
- package/dist/__tests__/agents/validateChangesStep.test.js +52 -0
- package/dist/__tests__/agents/validateChangesStep.test.js.map +1 -0
- package/dist/__tests__/askCommandTaskBinding.test.js +176 -0
- package/dist/__tests__/askCommandTaskBinding.test.js.map +1 -0
- package/dist/__tests__/commandVisibility.test.js +25 -0
- package/dist/__tests__/commandVisibility.test.js.map +1 -0
- package/dist/__tests__/config.devOutput.test.js +82 -0
- package/dist/__tests__/config.devOutput.test.js.map +1 -0
- package/dist/__tests__/currentContext.test.js +43 -0
- package/dist/__tests__/currentContext.test.js.map +1 -0
- package/dist/__tests__/daemonWorker.test.js +51 -0
- package/dist/__tests__/daemonWorker.test.js.map +1 -0
- package/dist/__tests__/dialogState.test.js +113 -0
- package/dist/__tests__/dialogState.test.js.map +1 -0
- package/dist/__tests__/evalCommands.test.js +506 -0
- package/dist/__tests__/evalCommands.test.js.map +1 -0
- package/dist/__tests__/evalCommandsSummary.test.js +68 -0
- package/dist/__tests__/evalCommandsSummary.test.js.map +1 -0
- package/dist/__tests__/example.test.js +1 -0
- package/dist/__tests__/example.test.js.map +1 -0
- package/dist/__tests__/factory.commitCommand.test.js +45 -0
- package/dist/__tests__/factory.commitCommand.test.js.map +1 -0
- package/dist/__tests__/factory.devOutputCommand.test.js +122 -0
- package/dist/__tests__/factory.devOutputCommand.test.js.map +1 -0
- package/dist/__tests__/factory.evalCommands.test.js +38 -0
- package/dist/__tests__/factory.evalCommands.test.js.map +1 -0
- package/dist/__tests__/factory.planCommand.test.js +35 -0
- package/dist/__tests__/factory.planCommand.test.js.map +1 -0
- package/dist/__tests__/factory.setupCommand.test.js +34 -0
- package/dist/__tests__/factory.setupCommand.test.js.map +1 -0
- package/dist/__tests__/factory.statusCommand.test.js +54 -0
- package/dist/__tests__/factory.statusCommand.test.js.map +1 -0
- package/dist/__tests__/fileRules/queryTokenRules.test.js +35 -0
- package/dist/__tests__/fileRules/queryTokenRules.test.js.map +1 -0
- package/dist/__tests__/fileRules/searchPathClassification.test.js +57 -0
- package/dist/__tests__/fileRules/searchPathClassification.test.js.map +1 -0
- package/dist/__tests__/generate.ollamaRecovery.test.js +344 -0
- package/dist/__tests__/generate.ollamaRecovery.test.js.map +1 -0
- package/dist/__tests__/index.modelStartup.test.js +24 -0
- package/dist/__tests__/index.modelStartup.test.js.map +1 -0
- package/dist/__tests__/indexCmd.test.js +85 -0
- package/dist/__tests__/indexCmd.test.js.map +1 -0
- package/dist/__tests__/indexSlashCommand.test.js +50 -0
- package/dist/__tests__/indexSlashCommand.test.js.map +1 -0
- package/dist/__tests__/ollamaService.test.js +103 -0
- package/dist/__tests__/ollamaService.test.js.map +1 -0
- package/dist/__tests__/pipeline/modules/codeTransformModule.small-file.test.js +68 -0
- package/dist/__tests__/pipeline/modules/codeTransformModule.small-file.test.js.map +1 -0
- package/dist/__tests__/pipeline/modules/commitSuggesterModule.test.js +68 -0
- package/dist/__tests__/pipeline/modules/commitSuggesterModule.test.js.map +1 -0
- package/dist/__tests__/pipeline/modules/fileSearchModule.test.js +284 -0
- package/dist/__tests__/pipeline/modules/fileSearchModule.test.js.map +1 -0
- package/dist/__tests__/pipeline/modules/finalAnswerModule.test.js +1139 -0
- package/dist/__tests__/pipeline/modules/finalAnswerModule.test.js.map +1 -0
- package/dist/__tests__/pipeline/modules/readFileModule.test.js +146 -0
- package/dist/__tests__/pipeline/modules/readFileModule.test.js.map +1 -0
- package/dist/__tests__/pipeline/modules/semanticAnalysisModule.test.js +192 -0
- package/dist/__tests__/pipeline/modules/semanticAnalysisModule.test.js.map +1 -0
- package/dist/__tests__/repoIdentity.test.js +31 -0
- package/dist/__tests__/repoIdentity.test.js.map +1 -0
- package/dist/__tests__/resumeContext.test.js +87 -0
- package/dist/__tests__/resumeContext.test.js.map +1 -0
- package/dist/__tests__/resumeState.test.js +239 -0
- package/dist/__tests__/resumeState.test.js.map +1 -0
- package/dist/__tests__/search/SearchOrchestrator.test.js +836 -0
- package/dist/__tests__/search/SearchOrchestrator.test.js.map +1 -0
- package/dist/__tests__/shellDialogUi.test.js +52 -0
- package/dist/__tests__/shellDialogUi.test.js.map +1 -0
- package/dist/__tests__/shellSession.test.js +102 -0
- package/dist/__tests__/shellSession.test.js.map +1 -0
- package/dist/__tests__/statusOwner.test.js +215 -0
- package/dist/__tests__/statusOwner.test.js.map +1 -0
- package/dist/__tests__/testing/contextEval.test.js +244 -0
- package/dist/__tests__/testing/contextEval.test.js.map +1 -0
- package/dist/__tests__/testing/harnessArtifacts.test.js +124 -0
- package/dist/__tests__/testing/harnessArtifacts.test.js.map +1 -0
- package/dist/__tests__/testing/llmTraceSession.test.js +67 -0
- package/dist/__tests__/testing/llmTraceSession.test.js.map +1 -0
- package/dist/__tests__/testing/registerDevCliCommands.test.js +35 -0
- package/dist/__tests__/testing/registerDevCliCommands.test.js.map +1 -0
- package/dist/__tests__/testing/runDiagnosis.test.js +159 -0
- package/dist/__tests__/testing/runDiagnosis.test.js.map +1 -0
- package/dist/__tests__/testing/runtimeLogReader.test.js +66 -0
- package/dist/__tests__/testing/runtimeLogReader.test.js.map +1 -0
- package/dist/__tests__/testing/testCommands.test.js +53 -0
- package/dist/__tests__/testing/testCommands.test.js.map +1 -0
- package/dist/__tests__/utils/compileSearchQuery.test.js +38 -0
- package/dist/__tests__/utils/compileSearchQuery.test.js.map +1 -0
- package/dist/__tests__/utils/consolePresentation.test.js +105 -0
- package/dist/__tests__/utils/consolePresentation.test.js.map +1 -0
- package/dist/__tests__/utils/extractFileReferences.test.js +41 -0
- package/dist/__tests__/utils/extractFileReferences.test.js.map +1 -0
- package/dist/__tests__/utils/log.test.js +34 -0
- package/dist/__tests__/utils/log.test.js.map +1 -0
- package/dist/__tests__/utils/runtimeLogger.test.js +200 -0
- package/dist/__tests__/utils/runtimeLogger.test.js.map +1 -0
- package/dist/__tests__/utils/spinner.test.js +31 -0
- package/dist/__tests__/utils/spinner.test.js.map +1 -0
- package/dist/__tests__/utils/verifyFocusPreference.test.js +41 -0
- package/dist/__tests__/utils/verifyFocusPreference.test.js.map +1 -0
- package/dist/agent/actions/index.js +301 -0
- package/dist/agent/actions/index.js.map +1 -0
- package/dist/agent/actions/normalize.js +360 -0
- package/dist/agent/actions/normalize.js.map +1 -0
- package/dist/agent/actions/schemas.js +129 -0
- package/dist/agent/actions/schemas.js.map +1 -0
- package/dist/agent/evidence/index.js +320 -0
- package/dist/agent/evidence/index.js.map +1 -0
- package/dist/agent/feedback/index.js +187 -0
- package/dist/agent/feedback/index.js.map +1 -0
- package/dist/agent/finalization/index.js +35 -0
- package/dist/agent/finalization/index.js.map +1 -0
- package/dist/agent/index.js +126 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/logging/index.js +350 -0
- package/dist/agent/logging/index.js.map +1 -0
- package/dist/agent/persistence/boot.js +58 -0
- package/dist/agent/persistence/boot.js.map +1 -0
- package/dist/agent/persistence/currentTask.js +36 -0
- package/dist/agent/persistence/currentTask.js.map +1 -0
- package/dist/agent/persistence/hydrate.js +42 -0
- package/dist/agent/persistence/hydrate.js.map +1 -0
- package/dist/agent/persistence/index.js +15 -0
- package/dist/agent/persistence/index.js.map +1 -0
- package/dist/agent/persistence/snapshots.js +97 -0
- package/dist/agent/persistence/snapshots.js.map +1 -0
- package/dist/agent/persistence/steps.js +95 -0
- package/dist/agent/persistence/steps.js.map +1 -0
- package/dist/agent/persistence/tasks.js +195 -0
- package/dist/agent/persistence/tasks.js.map +1 -0
- package/dist/agent/persistence/turns.js +92 -0
- package/dist/agent/persistence/turns.js.map +1 -0
- package/dist/agent/policy/ambiguityResolution.js +226 -0
- package/dist/agent/policy/ambiguityResolution.js.map +1 -0
- package/dist/agent/policy/contracts.js +2 -0
- package/dist/agent/policy/contracts.js.map +1 -0
- package/dist/agent/policy/coveragePolicy.js +309 -0
- package/dist/agent/policy/coveragePolicy.js.map +1 -0
- package/dist/agent/policy/endDecisionPolicy.js +31 -0
- package/dist/agent/policy/endDecisionPolicy.js.map +1 -0
- package/dist/agent/policy/index.js +344 -0
- package/dist/agent/policy/index.js.map +1 -0
- package/dist/agent/policy/loopReview.js +778 -0
- package/dist/agent/policy/loopReview.js.map +1 -0
- package/dist/agent/policy/readinessPolicy.js +108 -0
- package/dist/agent/policy/readinessPolicy.js.map +1 -0
- package/dist/agent/policy/resolutionPipeline.js +356 -0
- package/dist/agent/policy/resolutionPipeline.js.map +1 -0
- package/dist/agent/policy/targetClassification.js +33 -0
- package/dist/agent/policy/targetClassification.js.map +1 -0
- package/dist/agent/prompting/actionChoice.js +90 -0
- package/dist/agent/prompting/actionChoice.js.map +1 -0
- package/dist/agent/prompting/finalAnswer.js +38 -0
- package/dist/agent/prompting/finalAnswer.js.map +1 -0
- package/dist/agent/prompting/index.js +14 -0
- package/dist/agent/prompting/index.js.map +1 -0
- package/dist/agent/prompting/plan.js +59 -0
- package/dist/agent/prompting/plan.js.map +1 -0
- package/dist/agent/prompting/transform.js +175 -0
- package/dist/agent/prompting/transform.js.map +1 -0
- package/dist/agent/prompting/understand.js +70 -0
- package/dist/agent/prompting/understand.js.map +1 -0
- package/dist/agent/read/freshness.js +29 -0
- package/dist/agent/read/freshness.js.map +1 -0
- package/dist/agent/read/fullReadPrompt.js +43 -0
- package/dist/agent/read/fullReadPrompt.js.map +1 -0
- package/dist/agent/read/index.js +140 -0
- package/dist/agent/read/index.js.map +1 -0
- package/dist/agent/read/persistence.js +88 -0
- package/dist/agent/read/persistence.js.map +1 -0
- package/dist/agent/read/summarizeReadEvidence.js +733 -0
- package/dist/agent/read/summarizeReadEvidence.js.map +1 -0
- package/dist/agent/read/targetResolution.js +126 -0
- package/dist/agent/read/targetResolution.js.map +1 -0
- package/dist/agent/resume/checkpoint.js +41 -0
- package/dist/agent/resume/checkpoint.js.map +1 -0
- package/dist/agent/runtime/lifecycle.js +67 -0
- package/dist/agent/runtime/lifecycle.js.map +1 -0
- package/dist/agent/runtime/progress.js +178 -0
- package/dist/agent/runtime/progress.js.map +1 -0
- package/dist/agent/runtime/runAgentLoop.js +402 -0
- package/dist/agent/runtime/runAgentLoop.js.map +1 -0
- package/dist/agent/runtime/runAgentPlanOnly.js +127 -0
- package/dist/agent/runtime/runAgentPlanOnly.js.map +1 -0
- package/dist/agent/runtime/understand.js +336 -0
- package/dist/agent/runtime/understand.js.map +1 -0
- package/dist/agent/search/batchPlanner.js +274 -0
- package/dist/agent/search/batchPlanner.js.map +1 -0
- package/dist/agent/search/candidateRetentionPolicy.js +184 -0
- package/dist/agent/search/candidateRetentionPolicy.js.map +1 -0
- package/dist/agent/search/directory.js +51 -0
- package/dist/agent/search/directory.js.map +1 -0
- package/dist/agent/search/exactTarget.js +151 -0
- package/dist/agent/search/exactTarget.js.map +1 -0
- package/dist/agent/search/fragment.js +110 -0
- package/dist/agent/search/fragment.js.map +1 -0
- package/dist/agent/search/index.js +166 -0
- package/dist/agent/search/index.js.map +1 -0
- package/dist/agent/search/laneClassifier.js +119 -0
- package/dist/agent/search/laneClassifier.js.map +1 -0
- package/dist/agent/search/limits.js +10 -0
- package/dist/agent/search/limits.js.map +1 -0
- package/dist/agent/search/ranking.js +22 -0
- package/dist/agent/search/ranking.js.map +1 -0
- package/dist/agent/search/regex.js +83 -0
- package/dist/agent/search/regex.js.map +1 -0
- package/dist/agent/search/routePolicy.js +11 -0
- package/dist/agent/search/routePolicy.js.map +1 -0
- package/dist/agent/search/searchContext.js +128 -0
- package/dist/agent/search/searchContext.js.map +1 -0
- package/dist/agent/search/semantic.js +113 -0
- package/dist/agent/search/semantic.js.map +1 -0
- package/dist/agent/search/semanticIndexSearch.js +202 -0
- package/dist/agent/search/semanticIndexSearch.js.map +1 -0
- package/dist/agent/search/shared.js +283 -0
- package/dist/agent/search/shared.js.map +1 -0
- package/dist/agent/search/shell.js +202 -0
- package/dist/agent/search/shell.js.map +1 -0
- package/dist/agent/search/snippetEvidence.js +57 -0
- package/dist/agent/search/snippetEvidence.js.map +1 -0
- package/dist/agent/search/types.js +2 -0
- package/dist/agent/search/types.js.map +1 -0
- package/dist/agent/state/index.js +99 -0
- package/dist/agent/state/index.js.map +1 -0
- package/dist/agent/state/memory.js +56 -0
- package/dist/agent/state/memory.js.map +1 -0
- package/dist/agent/structuredOutput/index.js +28 -0
- package/dist/agent/structuredOutput/index.js.map +1 -0
- package/dist/agent/tools/index.js +199 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/transform/index.js +519 -0
- package/dist/agent/transform/index.js.map +1 -0
- package/dist/agent/transform/syntax.js +49 -0
- package/dist/agent/transform/syntax.js.map +1 -0
- package/dist/agent/types.js +20 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agents/actionRegistry.js +114 -0
- package/dist/agents/actionRegistry.js.map +1 -0
- package/dist/agents/agent.js +5 -0
- package/dist/agents/agent.js.map +1 -0
- package/dist/agents/agentActions.js +5 -0
- package/dist/agents/agentActions.js.map +1 -0
- package/dist/agents/agentEvidence.js +5 -0
- package/dist/agents/agentEvidence.js.map +1 -0
- package/dist/agents/agentFeedback.js +5 -0
- package/dist/agents/agentFeedback.js.map +1 -0
- package/dist/agents/agentLogging.js +5 -0
- package/dist/agents/agentLogging.js.map +1 -0
- package/dist/agents/agentLoop.js +5 -0
- package/dist/agents/agentLoop.js.map +1 -0
- package/dist/agents/agentMemory.js +5 -0
- package/dist/agents/agentMemory.js.map +1 -0
- package/dist/agents/agentPlanMode.js +5 -0
- package/dist/agents/agentPlanMode.js.map +1 -0
- package/dist/agents/agentPolicyState.js +5 -0
- package/dist/agents/agentPolicyState.js.map +1 -0
- package/dist/agents/agentProgress.js +93 -0
- package/dist/agents/agentProgress.js.map +1 -0
- package/dist/agents/agentSchemas.js +5 -0
- package/dist/agents/agentSchemas.js.map +1 -0
- package/dist/agents/agentSearchScoring.js +5 -0
- package/dist/agents/agentSearchScoring.js.map +1 -0
- package/dist/agents/agentStateMachine.js +5 -0
- package/dist/agents/agentStateMachine.js.map +1 -0
- package/dist/agents/agentTools.js +5 -0
- package/dist/agents/agentTools.js.map +1 -0
- package/dist/agents/agentTypes.js +5 -0
- package/dist/agents/agentTypes.js.map +1 -0
- package/dist/agents/agentUnderstand.js +5 -0
- package/dist/agents/agentUnderstand.js.map +1 -0
- package/dist/agents/analysisPlanGenStep.js +194 -17
- package/dist/agents/analysisPlanGenStep.js.map +1 -0
- package/dist/agents/answerOnlyCompletion.js +32 -0
- package/dist/agents/answerOnlyCompletion.js.map +1 -0
- package/dist/agents/collaboratorStep.js +1 -0
- package/dist/agents/collaboratorStep.js.map +1 -0
- package/dist/agents/decideNextAction.js +444 -0
- package/dist/agents/decideNextAction.js.map +1 -0
- package/dist/agents/deriveFocusFromSearchStep.js +83 -0
- package/dist/agents/deriveFocusFromSearchStep.js.map +1 -0
- package/dist/agents/evidenceVerifierStep.js +104 -13
- package/dist/agents/evidenceVerifierStep.js.map +1 -0
- package/dist/agents/fileCheckStep.js +381 -12
- package/dist/agents/fileCheckStep.js.map +1 -0
- package/dist/agents/giveUpEvaluatorStep.js +63 -0
- package/dist/agents/giveUpEvaluatorStep.js.map +1 -0
- package/dist/agents/guardPolicy.js +20 -0
- package/dist/agents/guardPolicy.js.map +1 -0
- package/dist/agents/guards/executionPolicyResolver.js +165 -0
- package/dist/agents/guards/executionPolicyResolver.js.map +1 -0
- package/dist/agents/guards/guardState.js +195 -0
- package/dist/agents/guards/guardState.js.map +1 -0
- package/dist/agents/guards/resolveProgressState.js +403 -0
- package/dist/agents/guards/resolveProgressState.js.map +1 -0
- package/dist/agents/infoPlanGenStep.js +66 -8
- package/dist/agents/infoPlanGenStep.js.map +1 -0
- package/dist/agents/integrateFeedbackStep.js +1 -0
- package/dist/agents/integrateFeedbackStep.js.map +1 -0
- package/dist/agents/iterationFileSelector.js +8 -7
- package/dist/agents/iterationFileSelector.js.map +1 -0
- package/dist/agents/mainAgentActivityLog.js +85 -0
- package/dist/agents/mainAgentActivityLog.js.map +1 -0
- package/dist/agents/mainAgentHeuristics.js +173 -0
- package/dist/agents/mainAgentHeuristics.js.map +1 -0
- package/dist/agents/mainAgentVerify.js +159 -0
- package/dist/agents/mainAgentVerify.js.map +1 -0
- package/dist/agents/objectiveEvaluatorStep.js +103 -0
- package/dist/agents/objectiveEvaluatorStep.js.map +1 -0
- package/dist/agents/outerLoopRecoveryEvaluator.js +108 -0
- package/dist/agents/outerLoopRecoveryEvaluator.js.map +1 -0
- package/dist/agents/readinessGateStep.js +95 -9
- package/dist/agents/readinessGateStep.js.map +1 -0
- package/dist/agents/reasonNextStep.js +9 -8
- package/dist/agents/reasonNextStep.js.map +1 -0
- package/dist/agents/reasonNextTaskStep.js +267 -144
- package/dist/agents/reasonNextTaskStep.js.map +1 -0
- package/dist/agents/researchPlanGenStep.js +61 -25
- package/dist/agents/researchPlanGenStep.js.map +1 -0
- package/dist/agents/resolveAgentTargetClassification.js +5 -0
- package/dist/agents/resolveAgentTargetClassification.js.map +1 -0
- package/dist/agents/resolveExecutionModeStep.js +1 -0
- package/dist/agents/resolveExecutionModeStep.js.map +1 -0
- package/dist/agents/resolveExplicitTargetsStep.js +74 -0
- package/dist/agents/resolveExplicitTargetsStep.js.map +1 -0
- package/dist/agents/routingDecisionStep.js +58 -11
- package/dist/agents/routingDecisionStep.js.map +1 -0
- package/dist/agents/scopeClassificationStep.js +66 -3
- package/dist/agents/scopeClassificationStep.js.map +1 -0
- package/dist/agents/selectRelevantSourcesStep.js +13 -5
- package/dist/agents/selectRelevantSourcesStep.js.map +1 -0
- package/dist/agents/structuralPreloadStep.js +3 -4
- package/dist/agents/structuralPreloadStep.js.map +1 -0
- package/dist/agents/transformPlanGenStep.js +105 -18
- package/dist/agents/transformPlanGenStep.js.map +1 -0
- package/dist/agents/understandIntentStep.js +237 -17
- package/dist/agents/understandIntentStep.js.map +1 -0
- package/dist/agents/validateChangesStep.js +16 -2
- package/dist/agents/validateChangesStep.js.map +1 -0
- package/dist/agents/writeFileStep.js +1 -0
- package/dist/agents/writeFileStep.js.map +1 -0
- package/dist/commands/AskCmd.js +139 -44
- package/dist/commands/AskCmd.js.map +1 -0
- package/dist/commands/BackupCmd.js +1 -0
- package/dist/commands/BackupCmd.js.map +1 -0
- package/dist/commands/ChangeLogUpdateCmd.js +1 -0
- package/dist/commands/ChangeLogUpdateCmd.js.map +1 -0
- package/dist/commands/CommitSuggesterCmd.js +55 -13
- package/dist/commands/CommitSuggesterCmd.js.map +1 -0
- package/dist/commands/DaemonCmd.js +52 -14
- package/dist/commands/DaemonCmd.js.map +1 -0
- package/dist/commands/DeleteIndex.js +1 -0
- package/dist/commands/DeleteIndex.js.map +1 -0
- package/dist/commands/EvalReportCmd.js +374 -0
- package/dist/commands/EvalReportCmd.js.map +1 -0
- package/dist/commands/FindCmd.js +1 -0
- package/dist/commands/FindCmd.js.map +1 -0
- package/dist/commands/GitCmd.js +1 -0
- package/dist/commands/GitCmd.js.map +1 -0
- package/dist/commands/IndexCmd.js +11 -79
- package/dist/commands/IndexCmd.js.map +1 -0
- package/dist/commands/InspectCmd.js +1 -0
- package/dist/commands/InspectCmd.js.map +1 -0
- package/dist/commands/ModelCmd.js +24 -0
- package/dist/commands/ModelCmd.js.map +1 -0
- package/dist/commands/ReadlineSingleton.js +1 -0
- package/dist/commands/ReadlineSingleton.js.map +1 -0
- package/dist/commands/ResetDbCmd.js +18 -1
- package/dist/commands/ResetDbCmd.js.map +1 -0
- package/dist/commands/ReviewCmd.js +1 -0
- package/dist/commands/ReviewCmd.js.map +1 -0
- package/dist/commands/StatusCmd.js +22 -0
- package/dist/commands/StatusCmd.js.map +1 -0
- package/dist/commands/StopDaemonCmd.js +1 -0
- package/dist/commands/StopDaemonCmd.js.map +1 -0
- package/dist/commands/SummaryCmd.js +1 -0
- package/dist/commands/SummaryCmd.js.map +1 -0
- package/dist/commands/SwitchCmd.js +9 -15
- package/dist/commands/SwitchCmd.js.map +1 -0
- package/dist/commands/TasksCmd.js +142 -57
- package/dist/commands/TasksCmd.js.map +1 -0
- package/dist/commands/TestCmd.js +66 -0
- package/dist/commands/TestCmd.js.map +1 -0
- package/dist/commands/WorkflowCmd.js +1 -0
- package/dist/commands/WorkflowCmd.js.map +1 -0
- package/dist/commands/commandVisibility.js +27 -0
- package/dist/commands/commandVisibility.js.map +1 -0
- package/dist/commands/evalCommands.js +1337 -0
- package/dist/commands/evalCommands.js.map +1 -0
- package/dist/commands/factory.js +206 -38
- package/dist/commands/factory.js.map +1 -0
- package/dist/config.js +62 -11
- package/dist/config.js.map +1 -0
- package/dist/constants.js +21 -3
- package/dist/constants.js.map +1 -0
- package/dist/context.js +33 -32
- package/dist/context.js.map +1 -0
- package/dist/daemon/daemonQueues.js +1 -20
- package/dist/daemon/daemonQueues.js.map +1 -0
- package/dist/daemon/daemonWorker.js +26 -37
- package/dist/daemon/daemonWorker.js.map +1 -0
- package/dist/daemon/generateSummaries.js +1 -0
- package/dist/daemon/generateSummaries.js.map +1 -0
- package/dist/daemon/runFolderCapsuleBatch.js +1 -0
- package/dist/daemon/runFolderCapsuleBatch.js.map +1 -0
- package/dist/daemon/runIndexingBatch.js +1 -0
- package/dist/daemon/runIndexingBatch.js.map +1 -0
- package/dist/daemon/runKgBatch.js +9 -1
- package/dist/daemon/runKgBatch.js.map +1 -0
- package/dist/db/backup.js +1 -0
- package/dist/db/backup.js.map +1 -0
- package/dist/db/client.js +18 -3
- package/dist/db/client.js.map +1 -0
- package/dist/db/fileIndex.js +110 -152
- package/dist/db/fileIndex.js.map +1 -0
- package/dist/db/functionExtractors/extractFromJava.js +1 -0
- package/dist/db/functionExtractors/extractFromJava.js.map +1 -0
- package/dist/db/functionExtractors/extractFromJs.js +1 -0
- package/dist/db/functionExtractors/extractFromJs.js.map +1 -0
- package/dist/db/functionExtractors/extractFromTs.js +1 -0
- package/dist/db/functionExtractors/extractFromTs.js.map +1 -0
- package/dist/db/functionExtractors/extractFromXML.js +1 -0
- package/dist/db/functionExtractors/extractFromXML.js.map +1 -0
- package/dist/db/functionExtractors/index.js +1 -0
- package/dist/db/functionExtractors/index.js.map +1 -0
- package/dist/db/functionIndex.js +9 -0
- package/dist/db/functionIndex.js.map +1 -0
- package/dist/db/schema.js +314 -99
- package/dist/db/schema.js.map +1 -0
- package/dist/db/sqlTemplates.js +1 -0
- package/dist/db/sqlTemplates.js.map +1 -0
- package/dist/fileRules/builtins.js +1 -0
- package/dist/fileRules/builtins.js.map +1 -0
- package/dist/fileRules/classifyFile.js +1 -0
- package/dist/fileRules/classifyFile.js.map +1 -0
- package/dist/fileRules/codeAllowedExtensions.js +1 -0
- package/dist/fileRules/codeAllowedExtensions.js.map +1 -0
- package/dist/fileRules/detectFileType.js +1 -0
- package/dist/fileRules/detectFileType.js.map +1 -0
- package/dist/fileRules/fileClassifier.js +1 -0
- package/dist/fileRules/fileClassifier.js.map +1 -0
- package/dist/fileRules/fileExceptions.js +1 -0
- package/dist/fileRules/fileExceptions.js.map +1 -0
- package/dist/fileRules/ignoredExtensions.js +1 -0
- package/dist/fileRules/ignoredExtensions.js.map +1 -0
- package/dist/fileRules/ignoredPaths.js +48 -5
- package/dist/fileRules/ignoredPaths.js.map +1 -0
- package/dist/fileRules/queryTokenRules.js +176 -0
- package/dist/fileRules/queryTokenRules.js.map +1 -0
- package/dist/fileRules/searchPathClassification.js +58 -0
- package/dist/fileRules/searchPathClassification.js.map +1 -0
- package/dist/fileRules/shouldIgnoreFiles.js +1 -0
- package/dist/fileRules/shouldIgnoreFiles.js.map +1 -0
- package/dist/fileRules/stopWords.js +9 -0
- package/dist/fileRules/stopWords.js.map +1 -0
- package/dist/fileRules/wellKnownRepoFiles.js +1 -0
- package/dist/fileRules/wellKnownRepoFiles.js.map +1 -0
- package/dist/git/commitSummary.js +227 -0
- package/dist/git/commitSummary.js.map +1 -0
- package/dist/github/api.js +1 -0
- package/dist/github/api.js.map +1 -0
- package/dist/github/auth.js +1 -0
- package/dist/github/auth.js.map +1 -0
- package/dist/github/github.js +1 -0
- package/dist/github/github.js.map +1 -0
- package/dist/github/githubAuthCheck.js +1 -0
- package/dist/github/githubAuthCheck.js.map +1 -0
- package/dist/github/postComments.js +1 -0
- package/dist/github/postComments.js.map +1 -0
- package/dist/github/repo.js +15 -24
- package/dist/github/repo.js.map +1 -0
- package/dist/github/token.js +1 -0
- package/dist/github/token.js.map +1 -0
- package/dist/github/types.js +1 -0
- package/dist/github/types.js.map +1 -0
- package/dist/index.js +318 -37
- package/dist/index.js.map +1 -0
- package/dist/lib/generate.js +264 -20
- package/dist/lib/generate.js.map +1 -0
- package/dist/lib/generateFolderCapsules.js +1 -0
- package/dist/lib/generateFolderCapsules.js.map +1 -0
- package/dist/lib/ollamaModelPolicy.js +59 -0
- package/dist/lib/ollamaModelPolicy.js.map +1 -0
- package/dist/lib/spinner.js +29 -9
- package/dist/lib/spinner.js.map +1 -0
- package/dist/modelSetup.js +25 -78
- package/dist/modelSetup.js.map +1 -0
- package/dist/pipeline/modules/changeLogModule.js +10 -1
- package/dist/pipeline/modules/changeLogModule.js.map +1 -0
- package/dist/pipeline/modules/cleanupModule.js +1 -0
- package/dist/pipeline/modules/cleanupModule.js.map +1 -0
- package/dist/pipeline/modules/codeTransformModule.js +10 -16
- package/dist/pipeline/modules/codeTransformModule.js.map +1 -0
- package/dist/pipeline/modules/commentModule.js +12 -0
- package/dist/pipeline/modules/commentModule.js.map +1 -0
- package/dist/pipeline/modules/commitSuggesterModule.js +82 -12
- package/dist/pipeline/modules/commitSuggesterModule.js.map +1 -0
- package/dist/pipeline/modules/contextReviewModule.js +12 -1
- package/dist/pipeline/modules/contextReviewModule.js.map +1 -0
- package/dist/pipeline/modules/dialogAnswerModule.js +58 -0
- package/dist/pipeline/modules/dialogAnswerModule.js.map +1 -0
- package/dist/pipeline/modules/fileSearchModule.js +5 -143
- package/dist/pipeline/modules/fileSearchModule.js.map +1 -0
- package/dist/pipeline/modules/finalAnswerModule.js +1176 -151
- package/dist/pipeline/modules/finalAnswerModule.js.map +1 -0
- package/dist/pipeline/modules/kgModule.js +18 -1
- package/dist/pipeline/modules/kgModule.js.map +1 -0
- package/dist/pipeline/modules/planAnswerModule.js +99 -0
- package/dist/pipeline/modules/planAnswerModule.js.map +1 -0
- package/dist/pipeline/modules/readFileModule.js +300 -0
- package/dist/pipeline/modules/readFileModule.js.map +1 -0
- package/dist/pipeline/modules/reviewModule.js +10 -1
- package/dist/pipeline/modules/reviewModule.js.map +1 -0
- package/dist/pipeline/modules/searchDbModule.js +159 -0
- package/dist/pipeline/modules/searchDbModule.js.map +1 -0
- package/dist/pipeline/modules/searchListDirectoryModule.js +62 -0
- package/dist/pipeline/modules/searchListDirectoryModule.js.map +1 -0
- package/dist/pipeline/modules/searchModuleShared.js +71 -0
- package/dist/pipeline/modules/searchModuleShared.js.map +1 -0
- package/dist/pipeline/modules/searchRegexModule.js +59 -0
- package/dist/pipeline/modules/searchRegexModule.js.map +1 -0
- package/dist/pipeline/modules/semanticAnalysisModule.js +185 -28
- package/dist/pipeline/modules/semanticAnalysisModule.js.map +1 -0
- package/dist/pipeline/modules/summaryModule.js +11 -1
- package/dist/pipeline/modules/summaryModule.js.map +1 -0
- package/dist/pipeline/registry/moduleRegistry.js +9 -0
- package/dist/pipeline/registry/moduleRegistry.js.map +1 -0
- package/dist/pipeline/runModulePipeline.js +1 -0
- package/dist/pipeline/runModulePipeline.js.map +1 -0
- package/dist/scripts/dbScriptSupport.js +172 -0
- package/dist/scripts/dbScriptSupport.js.map +1 -0
- package/dist/scripts/dbcheck.js +173 -267
- package/dist/scripts/dbcheck.js.map +1 -0
- package/dist/scripts/dboverview.js +161 -0
- package/dist/scripts/dboverview.js.map +1 -0
- package/dist/scripts/migrateDb.js +1 -0
- package/dist/scripts/migrateDb.js.map +1 -0
- package/dist/search/SearchOrchestrator.js +928 -0
- package/dist/search/SearchOrchestrator.js.map +1 -0
- package/dist/search/sharedRankingPolicy.js +283 -0
- package/dist/search/sharedRankingPolicy.js.map +1 -0
- package/dist/setup/reindexOwner.js +97 -0
- package/dist/setup/reindexOwner.js.map +1 -0
- package/dist/setup/setupOwner.js +100 -0
- package/dist/setup/setupOwner.js.map +1 -0
- package/dist/shell/dialogUi.js +81 -0
- package/dist/shell/dialogUi.js.map +1 -0
- package/dist/shellSession.js +126 -0
- package/dist/shellSession.js.map +1 -0
- package/dist/status/statusOwner.js +239 -0
- package/dist/status/statusOwner.js.map +1 -0
- package/dist/testing/contextEval.js +514 -0
- package/dist/testing/contextEval.js.map +1 -0
- package/dist/testing/fixtures/transform/small-file.input.js +5 -0
- package/dist/testing/fixtures/transform/small-file.input.js.map +1 -0
- package/dist/testing/harnessArtifacts.js +112 -0
- package/dist/testing/harnessArtifacts.js.map +1 -0
- package/dist/testing/llmTraceSession.js +67 -0
- package/dist/testing/llmTraceSession.js.map +1 -0
- package/dist/testing/registerDevCliCommands.js +43 -0
- package/dist/testing/registerDevCliCommands.js.map +1 -0
- package/dist/testing/runDiagnosis.js +248 -0
- package/dist/testing/runDiagnosis.js.map +1 -0
- package/dist/testing/runtimeLogReader.js +144 -0
- package/dist/testing/runtimeLogReader.js.map +1 -0
- package/dist/testing/testCommands.js +35 -303
- package/dist/testing/testCommands.js.map +1 -0
- package/dist/testing/testRegistry.js +233 -0
- package/dist/testing/testRegistry.js.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/buildContextualPrompt.js +26 -75
- package/dist/utils/buildContextualPrompt.js.map +1 -0
- package/dist/utils/changeLogPrompt.js +1 -0
- package/dist/utils/changeLogPrompt.js.map +1 -0
- package/dist/utils/checkModel.js +17 -92
- package/dist/utils/checkModel.js.map +1 -0
- package/dist/utils/commentMap.js +1 -0
- package/dist/utils/commentMap.js.map +1 -0
- package/dist/utils/compileSearchQuery.js +23 -9
- package/dist/utils/compileSearchQuery.js.map +1 -0
- package/dist/utils/consolePresentation.js +208 -0
- package/dist/utils/consolePresentation.js.map +1 -0
- package/dist/utils/contentUtils.js +17 -2
- package/dist/utils/contentUtils.js.map +1 -0
- package/dist/utils/debugContext.js +1 -0
- package/dist/utils/debugContext.js.map +1 -0
- package/dist/utils/dialogState.js +201 -0
- package/dist/utils/dialogState.js.map +1 -0
- package/dist/utils/editor.js +1 -0
- package/dist/utils/editor.js.map +1 -0
- package/dist/utils/executionEvidence.js +50 -0
- package/dist/utils/executionEvidence.js.map +1 -0
- package/dist/utils/extractFileReferences.js +140 -6
- package/dist/utils/extractFileReferences.js.map +1 -0
- package/dist/utils/fileEvidenceCache.js +50 -0
- package/dist/utils/fileEvidenceCache.js.map +1 -0
- package/dist/utils/fileTree.js +1 -0
- package/dist/utils/fileTree.js.map +1 -0
- package/dist/utils/loadRelevantFolderCapsules.js +35 -5
- package/dist/utils/loadRelevantFolderCapsules.js.map +1 -0
- package/dist/utils/log.js +10 -1
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/normalizeData.js +1 -0
- package/dist/utils/normalizeData.js.map +1 -0
- package/dist/utils/ollamaModelStatus.js +28 -0
- package/dist/utils/ollamaModelStatus.js.map +1 -0
- package/dist/utils/ollamaService.js +294 -0
- package/dist/utils/ollamaService.js.map +1 -0
- package/dist/utils/outputFormatter.js +1 -0
- package/dist/utils/outputFormatter.js.map +1 -0
- package/dist/utils/parseTaggedContent.js +1 -0
- package/dist/utils/parseTaggedContent.js.map +1 -0
- package/dist/utils/planActions.js +27 -46
- package/dist/utils/planActions.js.map +1 -0
- package/dist/utils/promptBuilderHelper.js +1 -0
- package/dist/utils/promptBuilderHelper.js.map +1 -0
- package/dist/utils/promptLogHelper.js +29 -13
- package/dist/utils/promptLogHelper.js.map +1 -0
- package/dist/utils/queryAnchors.js +71 -0
- package/dist/utils/queryAnchors.js.map +1 -0
- package/dist/utils/repoIdentity.js +82 -0
- package/dist/utils/repoIdentity.js.map +1 -0
- package/dist/utils/repoKey.js +1 -0
- package/dist/utils/repoKey.js.map +1 -0
- package/dist/utils/resolveTargetsToFiles.js +1 -0
- package/dist/utils/resolveTargetsToFiles.js.map +1 -0
- package/dist/utils/resumeContext.js +219 -0
- package/dist/utils/resumeContext.js.map +1 -0
- package/dist/utils/resumeState.js +310 -0
- package/dist/utils/resumeState.js.map +1 -0
- package/dist/utils/rollingPlan.js +118 -0
- package/dist/utils/rollingPlan.js.map +1 -0
- package/dist/utils/runQueryWithDaemonControl.js +11 -3
- package/dist/utils/runQueryWithDaemonControl.js.map +1 -0
- package/dist/utils/runtimeLogger.js +252 -0
- package/dist/utils/runtimeLogger.js.map +1 -0
- package/dist/utils/sanitizeQuery.js +1 -0
- package/dist/utils/sanitizeQuery.js.map +1 -0
- package/dist/utils/sharedUtils.js +1 -0
- package/dist/utils/sharedUtils.js.map +1 -0
- package/dist/utils/sleep.js +1 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/splitCodeIntoChunk.js +1 -0
- package/dist/utils/splitCodeIntoChunk.js.map +1 -0
- package/dist/utils/time.js +66 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/verifyFocusPreference.js +107 -0
- package/dist/utils/verifyFocusPreference.js.map +1 -0
- package/dist/utils/vscode.js +1 -0
- package/dist/utils/vscode.js.map +1 -0
- package/dist/workflow/workflowResolver.js +1 -0
- package/dist/workflow/workflowResolver.js.map +1 -0
- package/dist/workflow/workflowRunner.js +1 -0
- package/dist/workflow/workflowRunner.js.map +1 -0
- package/package.json +3 -3
- package/dist/agents/MainAgent.js +0 -1886
- package/dist/agents/contextReviewStep.js +0 -101
- package/dist/agents/finalPlanGenStep.js +0 -107
- package/dist/agents/structuralAnalysisStep.js +0 -46
- package/dist/agents/validationAnalysisStep.js +0 -87
- package/dist/pipeline/modules/chunkManagerModule.js +0 -24
- package/dist/pipeline/modules/cleanGeneratedTestsModule.js +0 -33
- package/dist/pipeline/modules/fileReaderModule.js +0 -72
- package/dist/pipeline/modules/gatherInfoModule.js +0 -181
- package/dist/pipeline/modules/generateTestsModule.js +0 -68
- package/dist/pipeline/modules/preserveCodeModule.js +0 -195
- package/dist/pipeline/modules/refactorModule.js +0 -40
- package/dist/pipeline/modules/repairTestsModule.js +0 -48
- package/dist/pipeline/modules/runTestsModule.js +0 -37
|
@@ -1,10 +1,67 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Phase Boundary: Final Answer Module
|
|
3
|
+
*
|
|
4
|
+
* This module is responsible for producing the final grounded answer to the user's query.
|
|
5
|
+
* It focuses on synthesizing evidence from analyzed files, execution artifacts, and research
|
|
6
|
+
* to generate a concise, well-grounded response.
|
|
7
|
+
*
|
|
8
|
+
* Key responsibilities:
|
|
9
|
+
* - Collects and structures evidence from file analyses, execution outcomes, and research
|
|
10
|
+
* - Builds a compact answer packet for grounding the final AI response
|
|
11
|
+
* - Generates the final answer using a structured prompt that includes all relevant context
|
|
12
|
+
* - Ensures the final answer is focused, grounded, and prioritizes intent and rationale
|
|
13
|
+
*
|
|
14
|
+
* Boundary conditions:
|
|
15
|
+
* - Keeps final answer synthesis focused on grounded file and research evidence
|
|
16
|
+
* - Does not depend on removed legacy continuity-answer shortcuts
|
|
17
|
+
* - Focuses only on grounded answers derived from analyzed files and execution artifacts
|
|
18
|
+
*
|
|
19
|
+
* Final grounding rule:
|
|
20
|
+
* - Constructs one compact answer packet from the strongest available evidence
|
|
21
|
+
* - Prefers research synthesis for repo-wide answers over dumping planning/code sections
|
|
22
|
+
* - Only includes exact identifiers or short excerpts when they materially help keep the answer grounded
|
|
23
|
+
*/
|
|
2
24
|
import { logInputOutput } from "../../utils/promptLogHelper.js";
|
|
3
25
|
import { generate } from "../../lib/generate.js";
|
|
4
26
|
import { cleanupModule } from "./cleanupModule.js";
|
|
5
27
|
import chalk from "chalk";
|
|
28
|
+
import path from "path";
|
|
29
|
+
import { isExecutionGroundedStep } from "../../utils/executionEvidence.js";
|
|
30
|
+
import { getVerifyFocusFilePreference, isVerifyFocusQueryText } from "../../utils/verifyFocusPreference.js";
|
|
31
|
+
import { buildResumePacket, renderResumePacket } from "../../utils/resumeState.js";
|
|
6
32
|
const MAX_CODE_CHARS = 3000;
|
|
7
33
|
const MAX_FILES = 4;
|
|
34
|
+
const MIN_EXACT_VERIFY_CONFIDENCE = 0.85;
|
|
35
|
+
const MAX_EXCERPTS_PER_FILE = 1;
|
|
36
|
+
const MAX_RATIONALE_LINES = 3;
|
|
37
|
+
const MAX_PROMPT_EXCERPT_CHARS = 600;
|
|
38
|
+
const GENERIC_VERIFY_IDENTIFIERS = new Set([
|
|
39
|
+
"module",
|
|
40
|
+
"repo",
|
|
41
|
+
"pattern",
|
|
42
|
+
"src",
|
|
43
|
+
"cli",
|
|
44
|
+
"points",
|
|
45
|
+
"explain",
|
|
46
|
+
"summarize",
|
|
47
|
+
"weak",
|
|
48
|
+
]);
|
|
49
|
+
function extractMentionedFilePaths(text) {
|
|
50
|
+
if (!text.trim())
|
|
51
|
+
return [];
|
|
52
|
+
const matches = text.match(/(?:\/[^\s`'"]+\.(?:ts|js|tsx|jsx|py|java|go|rs|cpp|c|cs|md|json)|\b[\w./-]+\.(?:ts|js|tsx|jsx|py|java|go|rs|cpp|c|cs|md|json)\b)/g) ?? [];
|
|
53
|
+
return Array.from(new Set(matches.map(item => item.trim())));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Builds the persisted final answer text from the rendered response and analyzed files.
|
|
57
|
+
*
|
|
58
|
+
* Why this exists:
|
|
59
|
+
* - the module needs one stable place to shape the final output header
|
|
60
|
+
* - explanation queries often need the module-level concept name to stay visible
|
|
61
|
+
*/
|
|
62
|
+
function buildFinalAnswer(renderedResponse, analyzedFiles) {
|
|
63
|
+
return `Analyzed files:\n${analyzedFiles.join("\n")}\n\n${renderedResponse}`;
|
|
64
|
+
}
|
|
8
65
|
export const finalAnswerModule = {
|
|
9
66
|
name: "finalAnswer",
|
|
10
67
|
description: "Produces the final grounded answer, prioritizing intent, targets, and rationale over file listings.",
|
|
@@ -16,11 +73,33 @@ export const finalAnswerModule = {
|
|
|
16
73
|
throw new Error("[finalAnswerModule] No context provided");
|
|
17
74
|
}
|
|
18
75
|
const analysis = context.analysis ?? {};
|
|
76
|
+
const directoryListingAnswer = analysis.directoryListingAnswer;
|
|
77
|
+
if (directoryListingAnswer) {
|
|
78
|
+
const finalText = directoryListingAnswer.summary;
|
|
79
|
+
analysis.finalAnswer = finalText;
|
|
80
|
+
logInputOutput("finalAnswerModule", "output", {
|
|
81
|
+
directoryListing: true,
|
|
82
|
+
folderPath: directoryListingAnswer.folderPath ?? null,
|
|
83
|
+
entryCount: directoryListingAnswer.entries.length,
|
|
84
|
+
});
|
|
85
|
+
console.log(chalk.yellow(`\n\n[FINAL ANSWER]\n${finalText}\n`));
|
|
86
|
+
return {
|
|
87
|
+
query,
|
|
88
|
+
content: finalText,
|
|
89
|
+
data: {
|
|
90
|
+
response: finalText,
|
|
91
|
+
analyzedFiles: [],
|
|
92
|
+
},
|
|
93
|
+
context,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
19
96
|
const fileAnalysis = analysis.fileAnalysis ?? {};
|
|
20
|
-
const combinedAnalysis = await ensureCombinedAnalysis(context, query);
|
|
21
97
|
const focus = analysis.focus;
|
|
22
98
|
const rationale = focus?.rationale ?? "No explicit rationale provided.";
|
|
23
|
-
const
|
|
99
|
+
const resumedContextCapsule = renderResumePacket(buildResumePacket(context.analysis?.resumeState)) ||
|
|
100
|
+
context.initContext?.resumedContextCapsule?.trim();
|
|
101
|
+
const combinedAnalysis = await ensureCombinedAnalysis(context, query);
|
|
102
|
+
const answerMode = resolveAnswerMode(context, query);
|
|
24
103
|
// --------------------------------------------------
|
|
25
104
|
// 1️⃣ Collect semantic files and excerpts
|
|
26
105
|
// --------------------------------------------------
|
|
@@ -29,120 +108,58 @@ export const finalAnswerModule = {
|
|
|
29
108
|
(!focus?.relevantFiles || focus.relevantFiles.includes(path)))
|
|
30
109
|
.map(([path, fa]) => ({ path, analysis: fa }))
|
|
31
110
|
.slice(0, MAX_FILES);
|
|
111
|
+
const verifyMeaningfulFiles = resolveVerifyMeaningfulFiles(context, meaningfulFiles.map(file => file.path));
|
|
112
|
+
const answerFiles = resolveMeaningfulAnswerFiles(context, meaningfulFiles, verifyMeaningfulFiles);
|
|
32
113
|
// Collect analyzed files for output (prefer executed task steps from this run).
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// --------------------------------------------------
|
|
52
|
-
const semanticSection = meaningfulFiles.length > 0
|
|
53
|
-
? meaningfulFiles.map(({ path, analysis }) => {
|
|
54
|
-
const excerptsSection = analysis.excerpts?.map(ex => `
|
|
55
|
-
Excerpt:
|
|
56
|
-
Description: ${ex.description}
|
|
57
|
-
${ex.startLine !== undefined ? `StartLine: ${ex.startLine}` : ""}
|
|
58
|
-
${ex.endLine !== undefined ? `EndLine: ${ex.endLine}` : ""}
|
|
59
|
-
Symbols: ${ex.symbols?.join(", ") ?? "[none]"}
|
|
60
|
-
Code:
|
|
61
|
-
${sliceCode(ex.code)}
|
|
62
|
-
`.trim()).join("\n\n") ?? "[No excerpts available]";
|
|
63
|
-
return `
|
|
64
|
-
${formatProposedChanges(path, analysis)}
|
|
65
|
-
|
|
66
|
-
Supporting Excerpts:
|
|
67
|
-
${excerptsSection}
|
|
68
|
-
`.trim();
|
|
69
|
-
}).join("\n\n")
|
|
70
|
-
: "[No meaningful semantic changes identified]";
|
|
71
|
-
const combinedSection = combinedAnalysis
|
|
72
|
-
? `
|
|
73
|
-
Shared patterns:
|
|
74
|
-
${combinedAnalysis.sharedPatterns?.map(p => `- ${p}`).join("\n") || "[none]"}
|
|
75
|
-
|
|
76
|
-
Architecture summary:
|
|
77
|
-
${combinedAnalysis.architectureSummary ?? "[none]"}
|
|
78
|
-
|
|
79
|
-
Hotspots:
|
|
80
|
-
${combinedAnalysis.hotspots?.map(h => `- ${h}`).join("\n") || "[none]"}
|
|
81
|
-
`.trim()
|
|
82
|
-
: "[No combined analysis available]";
|
|
83
|
-
const codeSection = transformedFiles.length > 0
|
|
84
|
-
? transformedFiles.map(f => `
|
|
85
|
-
FILE: ${f.filePath}
|
|
86
|
-
----------------------------------------
|
|
87
|
-
${sliceCode(f.content)}
|
|
88
|
-
`).join("\n\n")
|
|
89
|
-
: codeSnippets.length > 0
|
|
90
|
-
? codeSnippets.map(f => `
|
|
91
|
-
FILE: ${f.path}
|
|
92
|
-
----------------------------------------
|
|
93
|
-
${f.code}
|
|
94
|
-
`).join("\n\n")
|
|
95
|
-
: "[No source code required for explanation]";
|
|
96
|
-
// --------------------------------------------------
|
|
97
|
-
// 4️⃣ Prompt
|
|
98
|
-
// --------------------------------------------------
|
|
99
|
-
const prompt = `
|
|
100
|
-
You are an AI assistant providing the final answer to the user.
|
|
101
|
-
|
|
102
|
-
This is the final stage of a structured analysis pipeline.
|
|
103
|
-
|
|
104
|
-
User query:
|
|
105
|
-
${query}
|
|
106
|
-
|
|
107
|
-
Rationale for focus:
|
|
108
|
-
${rationale}
|
|
109
|
-
|
|
110
|
-
Analyzed files:
|
|
111
|
-
${analyzedFiles.join("\n")}
|
|
112
|
-
|
|
113
|
-
==================== PROPOSED CHANGES ====================
|
|
114
|
-
|
|
115
|
-
${semanticSection}
|
|
116
|
-
|
|
117
|
-
==================== COMBINED ANALYSIS ====================
|
|
118
|
-
|
|
119
|
-
${combinedSection}
|
|
120
|
-
|
|
121
|
-
==================== SOURCE CODE (LIMITED) ====================
|
|
122
|
-
|
|
123
|
-
${codeSection}
|
|
124
|
-
|
|
125
|
-
==================== INSTRUCTIONS ====================
|
|
126
|
-
|
|
127
|
-
- Answer the user's question directly.
|
|
128
|
-
- Prioritize intent, targets, and rationale over file listings.
|
|
129
|
-
- Refer to files only when they materially support the explanation.
|
|
130
|
-
- If no changes or conclusions can be drawn, say so explicitly.
|
|
131
|
-
- Do NOT speculate.
|
|
132
|
-
- Do NOT suggest further changes.
|
|
133
|
-
- Be concise, grounded, and outcome-focused.
|
|
134
|
-
`.trim();
|
|
114
|
+
const preliminaryAnalyzedFiles = resolveAnalyzedFilesForOutput(context, answerFiles.map(f => f.path));
|
|
115
|
+
const answerEvidence = collectAnswerEvidence({
|
|
116
|
+
context,
|
|
117
|
+
rationale,
|
|
118
|
+
mode: answerMode,
|
|
119
|
+
answerFiles,
|
|
120
|
+
analyzedFiles: preliminaryAnalyzedFiles,
|
|
121
|
+
combinedAnalysis,
|
|
122
|
+
resumedContextCapsule,
|
|
123
|
+
});
|
|
124
|
+
const analyzedFiles = resolveAnalyzedFilesForOutput(context, answerEvidence.answerFiles);
|
|
125
|
+
const answerPacket = buildAnswerPacket({
|
|
126
|
+
mode: answerMode,
|
|
127
|
+
rationale,
|
|
128
|
+
orderedQuestions: context.analysis?.intent?.questions ?? [],
|
|
129
|
+
evidence: answerEvidence,
|
|
130
|
+
});
|
|
131
|
+
const prompt = renderFinalAnswerPrompt(query, answerPacket);
|
|
135
132
|
// --------------------------------------------------
|
|
136
133
|
// 5️⃣ Generate final answer
|
|
137
134
|
// --------------------------------------------------
|
|
138
|
-
const aiResponse = await generate({ query, content: prompt }
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
135
|
+
const aiResponse = await generate({ query, content: prompt }, {
|
|
136
|
+
caller: "finalAnswerModule",
|
|
137
|
+
inputContext: {
|
|
138
|
+
query,
|
|
139
|
+
mode: answerPacket.mode,
|
|
140
|
+
rationale: answerPacket.rationale,
|
|
141
|
+
analyzedFiles,
|
|
142
|
+
executedAnswerFiles: answerPacket.executedAnswerFiles,
|
|
143
|
+
candidateAnswerFiles: answerPacket.candidateAnswerFiles,
|
|
144
|
+
answerEvidenceLevel: answerPacket.answerEvidenceLevel,
|
|
145
|
+
answerFiles: answerPacket.answerFiles,
|
|
146
|
+
answerSummaryByFile: answerPacket.answerSummaryByFile,
|
|
147
|
+
crossFileSummary: answerPacket.crossFileSummary ?? "",
|
|
148
|
+
researchSummary: answerPacket.researchSummary ?? "",
|
|
149
|
+
exactIdentifiers: answerPacket.exactIdentifiers,
|
|
150
|
+
supportingExcerpts: answerPacket.supportingExcerpts,
|
|
151
|
+
resumedContextCapsule: answerPacket.resumedContextCapsule ?? "",
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
const renderedResponse = renderAnswerResponse({
|
|
155
|
+
rawResponse: aiResponse.data,
|
|
156
|
+
packet: answerPacket,
|
|
157
|
+
});
|
|
158
|
+
const finalText = buildFinalAnswer(renderedResponse, analyzedFiles);
|
|
143
159
|
context.analysis || (context.analysis = {});
|
|
144
160
|
context.analysis.finalAnswer = finalText;
|
|
145
161
|
logInputOutput("finalAnswerModule", "output", {
|
|
162
|
+
callId: aiResponse.trace?.callId,
|
|
146
163
|
data: aiResponse.data,
|
|
147
164
|
analyzedFiles,
|
|
148
165
|
});
|
|
@@ -151,7 +168,7 @@ ${codeSection}
|
|
|
151
168
|
query,
|
|
152
169
|
content: finalText,
|
|
153
170
|
data: {
|
|
154
|
-
response:
|
|
171
|
+
response: renderedResponse,
|
|
155
172
|
analyzedFiles,
|
|
156
173
|
},
|
|
157
174
|
context,
|
|
@@ -161,21 +178,13 @@ ${codeSection}
|
|
|
161
178
|
/* -------------------------
|
|
162
179
|
Helpers
|
|
163
180
|
---------------------------- */
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Targets:
|
|
173
|
-
${proposed?.targets?.map(t => `- ${t}`).join("\n") || "[none]"}
|
|
174
|
-
|
|
175
|
-
Rationale:
|
|
176
|
-
${proposed?.rationale ?? "[no rationale]"}
|
|
177
|
-
`.trim();
|
|
178
|
-
}
|
|
181
|
+
/**
|
|
182
|
+
* Trims large code blocks without losing both the setup and the ending.
|
|
183
|
+
*
|
|
184
|
+
* Examples:
|
|
185
|
+
* - short file under the limit -> return unchanged
|
|
186
|
+
* - long file -> keep the first 60%, insert the truncation marker line, then keep the last 40%
|
|
187
|
+
*/
|
|
179
188
|
function sliceCode(code) {
|
|
180
189
|
if (code.length <= MAX_CODE_CHARS)
|
|
181
190
|
return code;
|
|
@@ -183,6 +192,14 @@ function sliceCode(code) {
|
|
|
183
192
|
"\n/* … truncated … */\n" +
|
|
184
193
|
code.slice(-Math.floor(MAX_CODE_CHARS * 0.4)));
|
|
185
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Reuses cached cross-file analysis when available and only computes it when at least two
|
|
197
|
+
* semantically analyzed files exist.
|
|
198
|
+
*
|
|
199
|
+
* Examples:
|
|
200
|
+
* - 0 or 1 semantically analyzed files -> return `undefined` because there is nothing to compare
|
|
201
|
+
* - 3 analyzed files -> run combined analysis once, then cache it on `context.analysis.combinedAnalysis`
|
|
202
|
+
*/
|
|
186
203
|
async function ensureCombinedAnalysis(context, query) {
|
|
187
204
|
context.analysis || (context.analysis = {});
|
|
188
205
|
if (context.analysis.combinedAnalysis) {
|
|
@@ -202,6 +219,13 @@ async function ensureCombinedAnalysis(context, query) {
|
|
|
202
219
|
context.analysis.combinedAnalysis = combined;
|
|
203
220
|
return combined;
|
|
204
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Builds the cross-file prompt and parses the model result into a stable shape.
|
|
224
|
+
*
|
|
225
|
+
* Examples:
|
|
226
|
+
* - shared auth logic across two files -> `sharedPatterns` may include `"both files validate session state first"`
|
|
227
|
+
* - parse failure or malformed JSON -> fall back to empty arrays and `"[unparsed]"`
|
|
228
|
+
*/
|
|
205
229
|
async function analyzeCombined(fileAnalysis, query) {
|
|
206
230
|
const prompt = `
|
|
207
231
|
You are given semantic analysis for multiple files that are all relevant
|
|
@@ -226,7 +250,13 @@ Return STRICT JSON:
|
|
|
226
250
|
}
|
|
227
251
|
`.trim();
|
|
228
252
|
try {
|
|
229
|
-
const ai = await generate({ query: "cross-file analysis", content: prompt }
|
|
253
|
+
const ai = await generate({ query: "cross-file analysis", content: prompt }, {
|
|
254
|
+
caller: "finalAnswerModule.analyzeCombined",
|
|
255
|
+
inputContext: {
|
|
256
|
+
query,
|
|
257
|
+
fileAnalysis,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
230
260
|
const cleaned = await cleanupModule.run({ query, content: ai.data });
|
|
231
261
|
const data = typeof cleaned.data === "object" && cleaned.data
|
|
232
262
|
? cleaned.data
|
|
@@ -246,36 +276,1031 @@ Return STRICT JSON:
|
|
|
246
276
|
};
|
|
247
277
|
}
|
|
248
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Resolves the grounding mode so prompt inputs match the answer shape.
|
|
281
|
+
*
|
|
282
|
+
* Examples:
|
|
283
|
+
* - explicit target explanation -> `single-file`
|
|
284
|
+
* - repo-wide architecture question with synthesis research -> `repo-wide`
|
|
285
|
+
* - "what should we do next?" on a resumed task -> `continuity`
|
|
286
|
+
*/
|
|
287
|
+
function resolveAnswerMode(context, query) {
|
|
288
|
+
const normalized = query.trim().toLowerCase();
|
|
289
|
+
const isContinuityQuery = normalized === "where were we?" ||
|
|
290
|
+
normalized === "what should we do next?" ||
|
|
291
|
+
context.analysis?.resumeIntent?.kind === "continuity-summary" ||
|
|
292
|
+
context.analysis?.resumeIntent?.kind === "continuity-next-step";
|
|
293
|
+
if (isContinuityQuery) {
|
|
294
|
+
return "continuity";
|
|
295
|
+
}
|
|
296
|
+
const hasArchitectureSynthesis = Boolean(context.analysis?.researchArtifacts?.latestByAction?.["research-architecture-synthesis"]);
|
|
297
|
+
if (context.analysis?.scopeType === "repo-wide" || hasArchitectureSynthesis) {
|
|
298
|
+
return "repo-wide";
|
|
299
|
+
}
|
|
300
|
+
const selectedCount = context.analysis?.focus?.selectedFiles?.length ?? 0;
|
|
301
|
+
return selectedCount <= 1 ? "single-file" : "multi-file";
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Builds the compact answer packet used to ground the final answer model call.
|
|
305
|
+
*
|
|
306
|
+
* Examples:
|
|
307
|
+
* - repo-wide answer -> prefer research synthesis plus a few representative files
|
|
308
|
+
* - single-file explanation -> keep one file summary, exact identifiers, and one short excerpt
|
|
309
|
+
*/
|
|
310
|
+
function buildAnswerPacket(args) {
|
|
311
|
+
return {
|
|
312
|
+
mode: args.mode,
|
|
313
|
+
rationale: reduceAnswerRationale(args.rationale),
|
|
314
|
+
orderedQuestions: args.orderedQuestions,
|
|
315
|
+
answerFiles: args.evidence.answerFiles,
|
|
316
|
+
executedAnswerFiles: args.evidence.executedAnswerFiles,
|
|
317
|
+
candidateAnswerFiles: args.evidence.candidateAnswerFiles,
|
|
318
|
+
answerEvidenceLevel: args.evidence.answerEvidenceLevel,
|
|
319
|
+
answerSummaryByFile: args.evidence.answerSummaryByFile,
|
|
320
|
+
crossFileSummary: args.evidence.crossFileSummary,
|
|
321
|
+
researchSummary: args.evidence.researchSummary,
|
|
322
|
+
exactIdentifiers: args.evidence.exactIdentifiers,
|
|
323
|
+
supportingExcerpts: args.evidence.supportingExcerpts,
|
|
324
|
+
resumedContextCapsule: args.evidence.resumedContextCapsule,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function parseStructuredQuestionAnswers(value) {
|
|
328
|
+
const raw = typeof value === "string" ? value : JSON.stringify(value ?? "");
|
|
329
|
+
try {
|
|
330
|
+
const parsed = JSON.parse(raw);
|
|
331
|
+
if (!Array.isArray(parsed.answers)) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
const normalized = parsed.answers
|
|
335
|
+
.filter((item) => !!item && typeof item.questionId === "string" && typeof item.answer === "string")
|
|
336
|
+
.map((item) => ({
|
|
337
|
+
questionId: item.questionId,
|
|
338
|
+
answer: item.answer.trim(),
|
|
339
|
+
evidenceStatus: item.evidenceStatus,
|
|
340
|
+
}))
|
|
341
|
+
.filter((item) => item.answer.length > 0);
|
|
342
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function renderStructuredQuestionAnswers(questions, answers) {
|
|
349
|
+
return questions
|
|
350
|
+
.sort((left, right) => left.order - right.order)
|
|
351
|
+
.map((question, index) => {
|
|
352
|
+
const matched = answers.find((answer) => answer.questionId === question.id);
|
|
353
|
+
const body = matched?.answer?.trim() || "Not enough grounded evidence to answer this question separately yet.";
|
|
354
|
+
return `${index + 1}. ${question.text}\n${body}`;
|
|
355
|
+
})
|
|
356
|
+
.join("\n\n");
|
|
357
|
+
}
|
|
358
|
+
function renderAnswerResponse(args) {
|
|
359
|
+
if (args.packet.orderedQuestions.length <= 1) {
|
|
360
|
+
return typeof args.rawResponse === "string"
|
|
361
|
+
? args.rawResponse
|
|
362
|
+
: JSON.stringify(args.rawResponse, null, 2);
|
|
363
|
+
}
|
|
364
|
+
const structuredAnswers = parseStructuredQuestionAnswers(args.rawResponse);
|
|
365
|
+
if (structuredAnswers) {
|
|
366
|
+
return renderStructuredQuestionAnswers(args.packet.orderedQuestions, structuredAnswers);
|
|
367
|
+
}
|
|
368
|
+
const rawText = typeof args.rawResponse === "string"
|
|
369
|
+
? args.rawResponse.trim()
|
|
370
|
+
: JSON.stringify(args.rawResponse, null, 2);
|
|
371
|
+
return rawText || renderStructuredQuestionAnswers(args.packet.orderedQuestions, []);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Collects the grounded evidence that will be packed into the final answer prompt.
|
|
375
|
+
*
|
|
376
|
+
* This keeps file choice, grounded notes, and continuity reduction separate from packet assembly.
|
|
377
|
+
*/
|
|
378
|
+
function collectAnswerEvidence(args) {
|
|
379
|
+
const { context, rationale, mode, answerFiles, analyzedFiles, combinedAnalysis, resumedContextCapsule } = args;
|
|
380
|
+
const architectureInputFiles = resolveArchitectureInputFiles(context);
|
|
381
|
+
const preferredFiles = normalizeAnswerFilePaths([
|
|
382
|
+
...extractMentionedFilePaths(rationale),
|
|
383
|
+
...(context.analysis?.focus?.selectedFiles ?? []),
|
|
384
|
+
...(context.analysis?.focus?.candidateFiles ?? []),
|
|
385
|
+
]);
|
|
386
|
+
const preferredGroundedFiles = resolvePreferredGroundedFiles(context, preferredFiles);
|
|
387
|
+
const researchSummary = buildResearchSummary(context, mode);
|
|
388
|
+
const packetFiles = resolveAnswerPacketFiles({
|
|
389
|
+
mode,
|
|
390
|
+
answerFiles,
|
|
391
|
+
analyzedFiles,
|
|
392
|
+
researchSummary,
|
|
393
|
+
preferredFiles,
|
|
394
|
+
preferredGroundedFiles,
|
|
395
|
+
architectureInputFiles,
|
|
396
|
+
});
|
|
397
|
+
const executedAnswerFiles = resolveExecutedAnswerFiles(context, packetFiles);
|
|
398
|
+
const candidateAnswerFiles = packetFiles.filter(filePath => !executedAnswerFiles.includes(filePath));
|
|
399
|
+
const answerSummaryByFile = packetFiles.map(filePath => ({
|
|
400
|
+
filePath,
|
|
401
|
+
summary: buildAnswerSummaryForFile(context, filePath, mode, args.context.initContext?.userQuery ?? ""),
|
|
402
|
+
}));
|
|
403
|
+
const exactIdentifiers = packetFiles
|
|
404
|
+
.map(filePath => ({
|
|
405
|
+
filePath,
|
|
406
|
+
identifiers: collectAnswerExactIdentifiers(context, filePath, args.context.initContext?.userQuery ?? ""),
|
|
407
|
+
}))
|
|
408
|
+
.filter(entry => entry.identifiers.length > 0);
|
|
409
|
+
const supportingExcerpts = collectSupportingExcerpts(context, packetFiles, mode, args.context.initContext?.userQuery ?? "");
|
|
410
|
+
return {
|
|
411
|
+
answerFiles: packetFiles,
|
|
412
|
+
executedAnswerFiles,
|
|
413
|
+
candidateAnswerFiles,
|
|
414
|
+
answerEvidenceLevel: resolveAnswerEvidenceLevel(context, executedAnswerFiles, candidateAnswerFiles),
|
|
415
|
+
answerSummaryByFile,
|
|
416
|
+
crossFileSummary: buildCrossFileSummary(mode, combinedAnalysis),
|
|
417
|
+
researchSummary,
|
|
418
|
+
exactIdentifiers,
|
|
419
|
+
supportingExcerpts,
|
|
420
|
+
resumedContextCapsule: mode === "continuity"
|
|
421
|
+
? reduceResumedContextCapsule(resumedContextCapsule)
|
|
422
|
+
: undefined,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function collectSupportingExcerpts(context, packetFiles, mode, query) {
|
|
426
|
+
if (mode === "repo-wide" || mode === "continuity") {
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
return packetFiles
|
|
430
|
+
.map(filePath => ({
|
|
431
|
+
filePath,
|
|
432
|
+
excerpts: buildSupportingExcerpts(context, filePath, query),
|
|
433
|
+
}))
|
|
434
|
+
.filter(entry => entry.excerpts.length > 0);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Chooses the smallest stable file list that still supports the answer.
|
|
438
|
+
*
|
|
439
|
+
* Examples:
|
|
440
|
+
* - repo-wide synthesis with architecture inputs -> use those first
|
|
441
|
+
* - single-file explanation -> keep only the first grounded file
|
|
442
|
+
*/
|
|
443
|
+
function resolveAnswerPacketFiles(args) {
|
|
444
|
+
const { mode, answerFiles, analyzedFiles, researchSummary, preferredFiles, preferredGroundedFiles, architectureInputFiles } = args;
|
|
445
|
+
const base = normalizeAnswerFilePaths(answerFiles.map(file => file.path));
|
|
446
|
+
const normalizedAnalyzed = normalizeAnswerFilePaths(analyzedFiles);
|
|
447
|
+
const groundedSupport = normalizeAnswerFilePaths([
|
|
448
|
+
...preferredGroundedFiles,
|
|
449
|
+
...architectureInputFiles,
|
|
450
|
+
]);
|
|
451
|
+
const fallbackFiles = base.length > 0
|
|
452
|
+
? base
|
|
453
|
+
: normalizedAnalyzed.length > 0
|
|
454
|
+
? normalizedAnalyzed
|
|
455
|
+
: groundedSupport.length > 0
|
|
456
|
+
? groundedSupport
|
|
457
|
+
: preferredFiles;
|
|
458
|
+
const preferredInScope = preferredFiles.filter(filePath => base.includes(filePath) || normalizedAnalyzed.includes(filePath) || groundedSupport.includes(filePath));
|
|
459
|
+
if (mode === "single-file") {
|
|
460
|
+
const primary = preferredInScope[0] ?? groundedSupport[0] ?? fallbackFiles[0];
|
|
461
|
+
return primary ? [primary] : [];
|
|
462
|
+
}
|
|
463
|
+
if (mode === "continuity") {
|
|
464
|
+
return (preferredInScope.length > 0 ? preferredInScope : groundedSupport.length > 0 ? groundedSupport : fallbackFiles).slice(0, 2);
|
|
465
|
+
}
|
|
466
|
+
if (mode === "repo-wide" && researchSummary) {
|
|
467
|
+
const merged = Array.from(new Set([...preferredInScope, ...groundedSupport, ...fallbackFiles]));
|
|
468
|
+
return merged.slice(0, MAX_FILES);
|
|
469
|
+
}
|
|
470
|
+
const merged = Array.from(new Set([
|
|
471
|
+
...preferredInScope,
|
|
472
|
+
...groundedSupport,
|
|
473
|
+
...fallbackFiles,
|
|
474
|
+
]));
|
|
475
|
+
return merged.slice(0, MAX_FILES);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Distills one file into a short answer note instead of dumping planning prose.
|
|
479
|
+
*
|
|
480
|
+
* Examples:
|
|
481
|
+
* - semantic summary exists -> keep that concise summary
|
|
482
|
+
* - exact identifiers exist -> surface them as the key grounded fact
|
|
483
|
+
*/
|
|
484
|
+
function buildAnswerSummaryForFile(context, filePath, mode, query) {
|
|
485
|
+
const readResult = context.analysis?.readResults?.byFile?.[filePath];
|
|
486
|
+
const analysis = context.analysis?.fileAnalysis?.[filePath];
|
|
487
|
+
const verify = context.analysis?.verify?.byFile?.[filePath];
|
|
488
|
+
const exact = collectAnswerExactIdentifiers(context, filePath, query);
|
|
489
|
+
const fileLabel = buildQueryAlignedFileLabel(filePath, query);
|
|
490
|
+
if (exact.length > 0) {
|
|
491
|
+
return fileLabel
|
|
492
|
+
? `${fileLabel}. Key identifiers: ${exact.join(", ")}.`
|
|
493
|
+
: `Key identifiers: ${exact.join(", ")}.`;
|
|
494
|
+
}
|
|
495
|
+
const readFacts = readResult?.facts?.filter(fact => fact.found).map(fact => fact.key) ?? [];
|
|
496
|
+
if (readFacts.length > 0) {
|
|
497
|
+
return `Quick-read facts: ${readFacts.slice(0, 3).join(", ")}.`;
|
|
498
|
+
}
|
|
499
|
+
const summary = analysis?.proposedChanges?.summary?.trim();
|
|
500
|
+
if (summary) {
|
|
501
|
+
return summarizeSentence(summary);
|
|
502
|
+
}
|
|
503
|
+
const verifyRationale = verify?.rationale?.trim();
|
|
504
|
+
if (verifyRationale && !/generic token|insufficient verify evidence/i.test(verifyRationale)) {
|
|
505
|
+
return summarizeSentence(verifyRationale);
|
|
506
|
+
}
|
|
507
|
+
if (mode === "repo-wide") {
|
|
508
|
+
return "Representative source used for repo-wide architecture grounding.";
|
|
509
|
+
}
|
|
510
|
+
return "Grounded source selected for the final answer.";
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Prefers cross-file synthesis only when the answer truly needs it.
|
|
514
|
+
*
|
|
515
|
+
* Examples:
|
|
516
|
+
* - single-file answer -> omit cross-file summary
|
|
517
|
+
* - repo-wide answer without synthesis -> fall back to combined analysis only if it exists
|
|
518
|
+
*/
|
|
519
|
+
function buildCrossFileSummary(mode, combinedAnalysis) {
|
|
520
|
+
if (mode === "single-file" || mode === "continuity" || !combinedAnalysis) {
|
|
521
|
+
return undefined;
|
|
522
|
+
}
|
|
523
|
+
const parts = [
|
|
524
|
+
combinedAnalysis.architectureSummary?.trim(),
|
|
525
|
+
combinedAnalysis.sharedPatterns?.length
|
|
526
|
+
? `Shared patterns: ${combinedAnalysis.sharedPatterns.join("; ")}`
|
|
527
|
+
: "",
|
|
528
|
+
combinedAnalysis.hotspots?.length
|
|
529
|
+
? `Hotspots: ${combinedAnalysis.hotspots.join("; ")}`
|
|
530
|
+
: "",
|
|
531
|
+
].filter(Boolean);
|
|
532
|
+
return parts.length > 0 ? parts.join("\n") : undefined;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Pulls repo-wide synthesis from stored research artifacts when available.
|
|
536
|
+
*
|
|
537
|
+
* Examples:
|
|
538
|
+
* - completed architecture synthesis -> use its summary, prior research, and coupling points
|
|
539
|
+
* - no stored synthesis -> return `undefined`
|
|
540
|
+
*/
|
|
541
|
+
function buildResearchSummary(context, mode) {
|
|
542
|
+
if (mode !== "repo-wide")
|
|
543
|
+
return undefined;
|
|
544
|
+
const entry = context.analysis?.researchArtifacts?.latestByAction?.["research-architecture-synthesis"];
|
|
545
|
+
if (!entry)
|
|
546
|
+
return undefined;
|
|
547
|
+
const data = entry.collectedData ?? {};
|
|
548
|
+
const priorResearch = Array.isArray(data.priorResearchSummaries)
|
|
549
|
+
? data.priorResearchSummaries
|
|
550
|
+
.map(item => `${String(item.action ?? "research")}: ${String(item.summary ?? "[none]")}`)
|
|
551
|
+
: [];
|
|
552
|
+
const sharedPatterns = Array.isArray(data.sharedPatterns)
|
|
553
|
+
? data.sharedPatterns.map(item => String(item))
|
|
554
|
+
: [];
|
|
555
|
+
const hotspots = Array.isArray(data.hotspots)
|
|
556
|
+
? data.hotspots.map(item => String(item))
|
|
557
|
+
: [];
|
|
558
|
+
const couplingPoints = Array.isArray(data.couplingPoints)
|
|
559
|
+
? data.couplingPoints.map(item => String(item))
|
|
560
|
+
: [];
|
|
561
|
+
const architectureInputFiles = Array.isArray(data.architectureInputFiles)
|
|
562
|
+
? data.architectureInputFiles.map(item => String(item))
|
|
563
|
+
: [];
|
|
564
|
+
const sections = [
|
|
565
|
+
`Synthesis summary: ${entry.summary}`,
|
|
566
|
+
typeof data.problemStatement === "string" && data.problemStatement.trim().length > 0
|
|
567
|
+
? `Problem statement: ${data.problemStatement}`
|
|
568
|
+
: "",
|
|
569
|
+
priorResearch.length > 0 ? `Prior research: ${priorResearch.join(" | ")}` : "",
|
|
570
|
+
sharedPatterns.length > 0 ? `Shared patterns: ${sharedPatterns.join("; ")}` : "",
|
|
571
|
+
hotspots.length > 0 ? `Hotspots: ${hotspots.join("; ")}` : "",
|
|
572
|
+
couplingPoints.length > 0 ? `Coupling points: ${couplingPoints.join("; ")}` : "",
|
|
573
|
+
architectureInputFiles.length > 0 ? `Architecture input files: ${architectureInputFiles.join(", ")}` : "",
|
|
574
|
+
].filter(Boolean);
|
|
575
|
+
return sections.join("\n");
|
|
576
|
+
}
|
|
577
|
+
function resolveArchitectureInputFiles(context) {
|
|
578
|
+
const synthesis = context.analysis?.researchArtifacts?.latestByAction?.["research-architecture-synthesis"];
|
|
579
|
+
const inputFiles = Array.isArray(synthesis?.collectedData?.architectureInputFiles)
|
|
580
|
+
? (synthesis?.collectedData?.architectureInputFiles).map(item => String(item))
|
|
581
|
+
: [];
|
|
582
|
+
return normalizeAnswerFilePaths(inputFiles);
|
|
583
|
+
}
|
|
584
|
+
function resolvePreferredGroundedFiles(context, preferredFiles) {
|
|
585
|
+
const groundedCandidates = normalizeAnswerFilePaths([
|
|
586
|
+
...(context.workingFiles ?? []).map(file => file.path),
|
|
587
|
+
...Object.keys(context.analysis?.verify?.byFile ?? {}),
|
|
588
|
+
...Object.keys(context.analysis?.fileAnalysis ?? {}),
|
|
589
|
+
...resolveArchitectureInputFiles(context),
|
|
590
|
+
]);
|
|
591
|
+
return preferredFiles.filter(filePath => groundedCandidates.includes(filePath));
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Keeps continuity capsules short enough for final grounding.
|
|
595
|
+
*
|
|
596
|
+
* Examples:
|
|
597
|
+
* - long resume capsule -> retain task, status, selected files, and next hints only
|
|
598
|
+
* - empty capsule -> return `undefined`
|
|
599
|
+
*/
|
|
600
|
+
function reduceResumedContextCapsule(value) {
|
|
601
|
+
if (!value?.trim())
|
|
602
|
+
return undefined;
|
|
603
|
+
const lines = value.split("\n").map(line => line.trim()).filter(Boolean);
|
|
604
|
+
const keepPrefixes = [
|
|
605
|
+
"Original task:",
|
|
606
|
+
"Current task status:",
|
|
607
|
+
"Current intent state:",
|
|
608
|
+
"- Normalized intent:",
|
|
609
|
+
"Current routing/focus state:",
|
|
610
|
+
"- Selected files:",
|
|
611
|
+
"Next plan hints:",
|
|
612
|
+
"New user query:",
|
|
613
|
+
];
|
|
614
|
+
const reduced = lines.filter(line => keepPrefixes.some(prefix => line.startsWith(prefix)));
|
|
615
|
+
const sanitized = (reduced.length > 0 ? reduced : lines.slice(0, 6))
|
|
616
|
+
.filter(line => line !== "Original task:" && line !== "Next plan hints:");
|
|
617
|
+
return sanitized.join("\n");
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Uses semantic excerpts first and falls back to a short source slice only when needed.
|
|
621
|
+
*
|
|
622
|
+
* Examples:
|
|
623
|
+
* - semantic excerpt available -> keep one short excerpt
|
|
624
|
+
* - no semantic excerpt but code exists -> use one bounded code slice
|
|
625
|
+
*/
|
|
626
|
+
function buildSupportingExcerpts(context, filePath, query) {
|
|
627
|
+
const analysis = context.analysis?.fileAnalysis?.[filePath];
|
|
628
|
+
const exactIdentifiers = collectAnswerExactIdentifiers(context, filePath, query);
|
|
629
|
+
const excerpts = (analysis?.excerpts ?? [])
|
|
630
|
+
.map(excerpt => excerpt.code?.trim())
|
|
631
|
+
.filter((excerpt) => Boolean(excerpt))
|
|
632
|
+
.slice(0, MAX_EXCERPTS_PER_FILE)
|
|
633
|
+
.map(excerpt => compactSupportingExcerpt(excerpt, exactIdentifiers));
|
|
634
|
+
if (excerpts.length > 0) {
|
|
635
|
+
return excerpts;
|
|
636
|
+
}
|
|
637
|
+
const code = context.workingFiles?.find(file => file.path === filePath)?.code;
|
|
638
|
+
if (!code?.trim())
|
|
639
|
+
return [];
|
|
640
|
+
return [compactSupportingExcerpt(code, exactIdentifiers)];
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Keeps rationale short so final grounding sees the decision, not the full planner history.
|
|
644
|
+
*
|
|
645
|
+
* Examples:
|
|
646
|
+
* - verbose pre/post-check rationale -> keep the first few specific lines only
|
|
647
|
+
* - empty rationale -> return a stable fallback
|
|
648
|
+
*/
|
|
649
|
+
function reduceAnswerRationale(value) {
|
|
650
|
+
const lines = value
|
|
651
|
+
.split("\n")
|
|
652
|
+
.map(line => line.trim())
|
|
653
|
+
.filter(Boolean)
|
|
654
|
+
.filter(line => !/^\[selectRelevantSources\]/i.test(line));
|
|
655
|
+
if (lines.length === 0) {
|
|
656
|
+
return "No explicit rationale provided.";
|
|
657
|
+
}
|
|
658
|
+
const ranked = lines.filter(line => /^Pre-check:/i.test(line) ||
|
|
659
|
+
/^\[Post-check\]/i.test(line) ||
|
|
660
|
+
/selected|candidate|relevant|aligned|confidence/i.test(line));
|
|
661
|
+
const compact = (ranked.length > 0 ? ranked : lines)
|
|
662
|
+
.slice(0, MAX_RATIONALE_LINES)
|
|
663
|
+
.map(line => summarizeSentence(line));
|
|
664
|
+
return compact.join("\n");
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Keeps one answer summary sentence compact enough for prompt grounding.
|
|
668
|
+
* Example: a long semantic summary paragraph -> keep the first sentence under the cap.
|
|
669
|
+
*/
|
|
670
|
+
function summarizeSentence(value) {
|
|
671
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
672
|
+
if (!normalized) {
|
|
673
|
+
return "Grounded source selected for the final answer.";
|
|
674
|
+
}
|
|
675
|
+
const firstSentence = normalized.match(/^.*?[.!?](?:\s|$)/)?.[0]?.trim() ?? normalized;
|
|
676
|
+
const bounded = firstSentence.length <= 180 ? firstSentence : `${firstSentence.slice(0, 177).trimEnd()}...`;
|
|
677
|
+
return bounded;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Narrows a raw code block to the smallest useful excerpt for final grounding.
|
|
681
|
+
*
|
|
682
|
+
* Examples:
|
|
683
|
+
* - long file header plus one relevant function -> keep the function-sized slice
|
|
684
|
+
* - exact identifier present -> center the excerpt around the first identifier match
|
|
685
|
+
*/
|
|
686
|
+
function compactSupportingExcerpt(code, exactIdentifiers) {
|
|
687
|
+
const trimmed = code.trim();
|
|
688
|
+
if (!trimmed) {
|
|
689
|
+
return "";
|
|
690
|
+
}
|
|
691
|
+
const byIdentifier = exactIdentifiers
|
|
692
|
+
.map(identifier => extractExcerptAroundIdentifier(trimmed, identifier))
|
|
693
|
+
.find(Boolean);
|
|
694
|
+
if (byIdentifier) {
|
|
695
|
+
return byIdentifier;
|
|
696
|
+
}
|
|
697
|
+
const semanticBlock = extractLikelySemanticBlock(trimmed);
|
|
698
|
+
return limitExcerptLength(semanticBlock);
|
|
699
|
+
}
|
|
700
|
+
function extractExcerptAroundIdentifier(code, identifier) {
|
|
701
|
+
const escaped = identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
702
|
+
const match = code.match(new RegExp(`(^|\\n)([^\\n]{0,120}\\b${escaped}\\b[^\\n]{0,220})`, "i"));
|
|
703
|
+
const line = match?.[2]?.trim();
|
|
704
|
+
if (!line) {
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
return limitExcerptLength(line);
|
|
708
|
+
}
|
|
709
|
+
function extractLikelySemanticBlock(code) {
|
|
710
|
+
const lines = code.split("\n").map(line => line.trimEnd());
|
|
711
|
+
const filtered = lines.filter(line => {
|
|
712
|
+
const trimmed = line.trim();
|
|
713
|
+
if (!trimmed)
|
|
714
|
+
return false;
|
|
715
|
+
if (trimmed.startsWith("import "))
|
|
716
|
+
return false;
|
|
717
|
+
if (trimmed.startsWith("export type ") || trimmed.startsWith("type "))
|
|
718
|
+
return false;
|
|
719
|
+
if (trimmed === "/**" || trimmed === "*/" || trimmed.startsWith("* "))
|
|
720
|
+
return false;
|
|
721
|
+
return true;
|
|
722
|
+
});
|
|
723
|
+
const chosen = filtered.slice(0, 8).join("\n").trim();
|
|
724
|
+
return chosen || lines.slice(0, 6).join("\n").trim();
|
|
725
|
+
}
|
|
726
|
+
function limitExcerptLength(value) {
|
|
727
|
+
const normalized = sliceCode(value).replace(/\n{3,}/g, "\n\n").trim();
|
|
728
|
+
if (normalized.length <= MAX_PROMPT_EXCERPT_CHARS) {
|
|
729
|
+
return normalized;
|
|
730
|
+
}
|
|
731
|
+
return `${normalized.slice(0, MAX_PROMPT_EXCERPT_CHARS - 19).trimEnd()}\n/* … trimmed … */`;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Renders the compact answer packet into the final prompt.
|
|
735
|
+
*
|
|
736
|
+
* Examples:
|
|
737
|
+
* - repo-wide packet -> includes research synthesis and skips broad code dumps
|
|
738
|
+
* - single-file packet -> includes one file summary plus minimal excerpts
|
|
739
|
+
*/
|
|
740
|
+
function renderFinalAnswerPrompt(query, packet) {
|
|
741
|
+
const orderedQuestionSection = packet.orderedQuestions.length > 1
|
|
742
|
+
? packet.orderedQuestions
|
|
743
|
+
.sort((left, right) => left.order - right.order)
|
|
744
|
+
.map((question, index) => `${index + 1}. ${question.text}`)
|
|
745
|
+
.join("\n")
|
|
746
|
+
: "";
|
|
747
|
+
const answerFilesSection = packet.answerSummaryByFile.length > 0
|
|
748
|
+
? packet.answerSummaryByFile
|
|
749
|
+
.map(entry => `- ${entry.filePath}: ${entry.summary}`)
|
|
750
|
+
.join("\n")
|
|
751
|
+
: "[none]";
|
|
752
|
+
const executedAnswerFilesSection = packet.executedAnswerFiles.length > 0
|
|
753
|
+
? packet.executedAnswerFiles.map(filePath => `- ${filePath}`).join("\n")
|
|
754
|
+
: "[none]";
|
|
755
|
+
const candidateAnswerFilesSection = packet.candidateAnswerFiles.length > 0
|
|
756
|
+
? packet.candidateAnswerFiles.map(filePath => `- ${filePath}`).join("\n")
|
|
757
|
+
: "[none]";
|
|
758
|
+
const exactIdentifierSection = packet.exactIdentifiers.length > 0
|
|
759
|
+
? packet.exactIdentifiers
|
|
760
|
+
.map(entry => `FILE: ${entry.filePath}\n- ${entry.identifiers.join("\n- ")}`)
|
|
761
|
+
.join("\n\n")
|
|
762
|
+
: "[No exact identifiers needed]";
|
|
763
|
+
const supportingExcerptSection = packet.supportingExcerpts.length > 0
|
|
764
|
+
? packet.supportingExcerpts
|
|
765
|
+
.map(entry => `FILE: ${entry.filePath}\n${entry.excerpts.map(excerpt => `---\n${excerpt}`).join("\n")}`)
|
|
766
|
+
.join("\n\n")
|
|
767
|
+
: "[No supporting excerpts needed]";
|
|
768
|
+
return `
|
|
769
|
+
You are an AI assistant providing the final answer to the user.
|
|
770
|
+
|
|
771
|
+
This is the final stage of a structured analysis pipeline.
|
|
772
|
+
|
|
773
|
+
User query:
|
|
774
|
+
${query}
|
|
775
|
+
|
|
776
|
+
Answer mode:
|
|
777
|
+
${packet.mode}
|
|
778
|
+
|
|
779
|
+
${packet.resumedContextCapsule ? `Reduced resumed context:\n${packet.resumedContextCapsule}\n` : ""}
|
|
780
|
+
|
|
781
|
+
${orderedQuestionSection ? `Ordered question parts:\n${orderedQuestionSection}\n` : ""}
|
|
782
|
+
|
|
783
|
+
Rationale for focus:
|
|
784
|
+
${packet.rationale}
|
|
785
|
+
|
|
786
|
+
Answer evidence level:
|
|
787
|
+
${packet.answerEvidenceLevel}
|
|
788
|
+
|
|
789
|
+
Executed answer files:
|
|
790
|
+
${executedAnswerFilesSection}
|
|
791
|
+
|
|
792
|
+
Candidate answer files:
|
|
793
|
+
${candidateAnswerFilesSection}
|
|
794
|
+
|
|
795
|
+
Grounded answer files:
|
|
796
|
+
${answerFilesSection}
|
|
797
|
+
|
|
798
|
+
${packet.crossFileSummary ? `Cross-file summary:\n${packet.crossFileSummary}\n` : ""}
|
|
799
|
+
|
|
800
|
+
${packet.researchSummary ? `Research synthesis:\n${packet.researchSummary}\n` : ""}
|
|
801
|
+
|
|
802
|
+
Exact identifiers:
|
|
803
|
+
${exactIdentifierSection}
|
|
804
|
+
|
|
805
|
+
Supporting excerpts:
|
|
806
|
+
${supportingExcerptSection}
|
|
807
|
+
|
|
808
|
+
${packet.orderedQuestions.length > 1 ? `
|
|
809
|
+
Return STRICT JSON in this format:
|
|
810
|
+
{
|
|
811
|
+
"answers": [
|
|
812
|
+
{ "questionId": "q1", "answer": "grounded answer text", "evidenceStatus": "grounded" | "partial" | "missing" }
|
|
813
|
+
]
|
|
814
|
+
}
|
|
815
|
+
The answers array must cover the ordered question parts in user order.
|
|
816
|
+
` : ""}
|
|
817
|
+
|
|
818
|
+
Instructions:
|
|
819
|
+
- Answer the user's question directly.
|
|
820
|
+
- If ordered question parts are present, answer in that exact order with numbered sections.
|
|
821
|
+
- If one question lacks evidence, say so for that question only and continue with the rest.
|
|
822
|
+
- Prefer the grounded answer files and research synthesis over broad code dumps.
|
|
823
|
+
- Use exact identifiers only when they materially support the answer.
|
|
824
|
+
- Refer to files only when they help explain the answer.
|
|
825
|
+
- If the reduced resumed context already answers the question, prefer it over broader repo context.
|
|
826
|
+
- Do NOT speculate.
|
|
827
|
+
- Do NOT suggest further changes.
|
|
828
|
+
- Be concise, grounded, and outcome-focused.
|
|
829
|
+
`.trim();
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Chooses which file list to expose in the final answer header.
|
|
833
|
+
*
|
|
834
|
+
* Priority examples:
|
|
835
|
+
* - transformed file exists -> prefer that over focus-selected files because it reflects actual work
|
|
836
|
+
* - no artifacts, but answer files exist -> use those fallback meaningful files
|
|
837
|
+
* - nothing qualified -> return `["[none]"]`
|
|
838
|
+
*/
|
|
249
839
|
function resolveAnalyzedFilesForOutput(context, fallbackMeaningfulFiles) {
|
|
840
|
+
const artifactPriority = resolveExecutedAnswerFiles(context, fallbackMeaningfulFiles);
|
|
841
|
+
if (artifactPriority.length > 0) {
|
|
842
|
+
return artifactPriority.slice(0, MAX_FILES);
|
|
843
|
+
}
|
|
844
|
+
const rationaleMentionedFiles = normalizeAnswerFilePaths(extractMentionedFilePaths(context.analysis?.focus?.rationale ?? ""));
|
|
845
|
+
const preferredGroundedFiles = resolvePreferredGroundedFiles(context, rationaleMentionedFiles);
|
|
846
|
+
const synthesisInputFiles = resolveArchitectureInputFiles(context);
|
|
847
|
+
const loadedGroundedFiles = normalizeAnswerFilePaths(fallbackMeaningfulFiles).filter(filePath => {
|
|
848
|
+
const isLoadedWorkingFile = (context.workingFiles ?? []).some(file => file.path === filePath && !!file.code?.trim());
|
|
849
|
+
const isPreferredGrounded = preferredGroundedFiles.includes(filePath);
|
|
850
|
+
const isSynthesisInput = synthesisInputFiles.includes(filePath);
|
|
851
|
+
return isSynthesisInput || (isLoadedWorkingFile && isPreferredGrounded);
|
|
852
|
+
});
|
|
853
|
+
if (loadedGroundedFiles.length > 0) {
|
|
854
|
+
return loadedGroundedFiles.slice(0, MAX_FILES);
|
|
855
|
+
}
|
|
856
|
+
return ["[none]"];
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Keeps answer reporting tied to executed or verified evidence, not raw candidate metadata.
|
|
860
|
+
* Examples:
|
|
861
|
+
* - completed task step or valid verification on a selected file -> include
|
|
862
|
+
* - selected file with no execution or verify evidence -> exclude
|
|
863
|
+
*/
|
|
864
|
+
function resolveExecutedAnswerFiles(context, candidateAnswerFiles) {
|
|
250
865
|
const transformedFiles = (context.execution?.codeTransformArtifacts?.files ?? [])
|
|
251
|
-
.map(file => file.filePath)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
.
|
|
255
|
-
const
|
|
256
|
-
.filter(
|
|
866
|
+
.map(file => file.filePath);
|
|
867
|
+
const taskStepFiles = (context.task?.steps ?? [])
|
|
868
|
+
.filter(step => isExecutionGroundedStep(step))
|
|
869
|
+
.map(step => step.filePath);
|
|
870
|
+
const validatedFiles = (context.analysis?.executionOutcome?.validations ?? [])
|
|
871
|
+
.filter(validation => validation.status === "valid" && !!validation.filePath)
|
|
872
|
+
.map(validation => validation.filePath);
|
|
873
|
+
const verifyFiles = Object.entries(context.analysis?.verify?.byFile ?? {})
|
|
874
|
+
.filter(([filePath, verify]) => candidateAnswerFiles.includes(filePath) &&
|
|
875
|
+
(context.analysis?.focus?.selectedFiles ?? []).includes(filePath) &&
|
|
876
|
+
verify?.isRelevant === true &&
|
|
877
|
+
(verify.fileConfidence ?? 0) >= 0.45)
|
|
257
878
|
.map(([filePath]) => filePath);
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
.filter((filePath, index, all) => all.indexOf(filePath) === index);
|
|
264
|
-
const artifactPriority = Array.from(new Set([
|
|
879
|
+
const synthesis = context.analysis?.researchArtifacts?.latestByAction?.["research-architecture-synthesis"];
|
|
880
|
+
const synthesisFiles = Array.isArray(synthesis?.collectedData?.architectureInputFiles)
|
|
881
|
+
? (synthesis?.collectedData?.architectureInputFiles).map(item => String(item))
|
|
882
|
+
: [];
|
|
883
|
+
return normalizeAnswerFilePaths([
|
|
265
884
|
...transformedFiles,
|
|
266
|
-
...researchTouchedFiles,
|
|
267
|
-
...semanticAnalyzedFiles,
|
|
268
885
|
...taskStepFiles,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
886
|
+
...validatedFiles,
|
|
887
|
+
...verifyFiles,
|
|
888
|
+
...synthesisFiles,
|
|
889
|
+
]).filter(filePath => candidateAnswerFiles.includes(filePath));
|
|
890
|
+
}
|
|
891
|
+
function resolveAnswerEvidenceLevel(context, executedAnswerFiles, candidateAnswerFiles) {
|
|
892
|
+
if (context.analysis?.researchArtifacts?.latestByAction?.["research-architecture-synthesis"]) {
|
|
893
|
+
return "synthesized";
|
|
272
894
|
}
|
|
273
|
-
if (
|
|
274
|
-
return
|
|
895
|
+
if (executedAnswerFiles.some(filePath => hasExecutionGradeAnswerEvidence(context, filePath))) {
|
|
896
|
+
return "executed";
|
|
275
897
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return selected.slice(0, MAX_FILES);
|
|
898
|
+
if (executedAnswerFiles.length > 0) {
|
|
899
|
+
return "verify";
|
|
279
900
|
}
|
|
280
|
-
|
|
901
|
+
if (candidateAnswerFiles.length > 0) {
|
|
902
|
+
return "candidate-only";
|
|
903
|
+
}
|
|
904
|
+
return "none";
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Distinguishes true execution evidence from verify-only grounded files.
|
|
908
|
+
* Examples:
|
|
909
|
+
* - transformed file or valid execution validation -> true
|
|
910
|
+
* - verify-relevant selected file with no transform or validation -> false
|
|
911
|
+
*/
|
|
912
|
+
function hasExecutionGradeAnswerEvidence(context, filePath) {
|
|
913
|
+
return (context.task?.steps ?? []).some(step => isExecutionGroundedStep(step) && step.filePath === filePath) ||
|
|
914
|
+
(context.execution?.codeTransformArtifacts?.files ?? []).some(file => file.filePath === filePath) ||
|
|
915
|
+
(context.analysis?.executionOutcome?.validations ?? []).some(validation => validation.status === "valid" && validation.filePath === filePath);
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Keeps final grounding on canonical repo file paths only.
|
|
919
|
+
* Examples:
|
|
920
|
+
* - "/repo/cli/src/db/fileIndex.ts" -> keep
|
|
921
|
+
* - "db/fileIndex.ts" -> drop
|
|
922
|
+
* - "__research__/impact-map" -> drop
|
|
923
|
+
*/
|
|
924
|
+
function normalizeAnswerFilePaths(paths) {
|
|
925
|
+
const out = new Set();
|
|
926
|
+
for (const filePath of paths) {
|
|
927
|
+
const trimmed = String(filePath ?? "").trim();
|
|
928
|
+
if (!trimmed || trimmed.startsWith("__research__/") || !path.isAbsolute(trimmed)) {
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
out.add(path.normalize(trimmed));
|
|
932
|
+
}
|
|
933
|
+
return Array.from(out);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Selects verify-backed files when semantic analysis did not produce enough grounded answer files.
|
|
937
|
+
*
|
|
938
|
+
* Why this exists:
|
|
939
|
+
* - verify can surface the files that best answer the user even when semantic excerpts are sparse
|
|
940
|
+
* - we still keep the list narrow so the final answer stays grounded and readable
|
|
941
|
+
*
|
|
942
|
+
* Examples:
|
|
943
|
+
* - existingPaths `["cli/src/index.ts"]`, verify marks `"cli/src/index.ts"` relevant -> skip duplicate
|
|
944
|
+
* - verify marks `"README.md"` relevant, but it was not selected/focused/touched this run -> skip as out of scope
|
|
945
|
+
* - `"cli/src/runner.ts"` confidence `0.22` -> skip as too weak
|
|
946
|
+
* - `"cli/src/runner.ts"` confidence `0.48` and query strongly prefers that path -> sort above a generic `0.48` match
|
|
947
|
+
*/
|
|
948
|
+
function resolveVerifyMeaningfulFiles(context, existingPaths) {
|
|
949
|
+
const seen = new Set(existingPaths);
|
|
950
|
+
const verifyByFile = context.analysis?.verify?.byFile ?? {};
|
|
951
|
+
const fileAnalysis = context.analysis?.fileAnalysis ?? {};
|
|
952
|
+
// Only consider files the run already had reason to care about.
|
|
953
|
+
// Example: focused files, transformed files, or files opened in the working set.
|
|
954
|
+
const eligibleVerifyFiles = new Set([
|
|
955
|
+
...(context.analysis?.focus?.selectedFiles ?? []),
|
|
956
|
+
...(context.execution?.codeTransformArtifacts?.files ?? []).map(file => file.filePath),
|
|
957
|
+
...(context.workingFiles ?? []).map(file => file.path),
|
|
958
|
+
]);
|
|
959
|
+
return Object.entries(verifyByFile)
|
|
960
|
+
.filter(([filePath, verify]) => {
|
|
961
|
+
// Avoid repeating files that semantic selection already chose.
|
|
962
|
+
if (seen.has(filePath))
|
|
963
|
+
return false;
|
|
964
|
+
// Keep verify from pulling in unrelated repo files just because they scored well.
|
|
965
|
+
if (!eligibleVerifyFiles.has(filePath))
|
|
966
|
+
return false;
|
|
967
|
+
// Require verify to explicitly say the file matters.
|
|
968
|
+
if (!verify?.isRelevant)
|
|
969
|
+
return false;
|
|
970
|
+
// Example: confidence 0.29 -> reject, confidence 0.30 -> keep.
|
|
971
|
+
return (verify.fileConfidence ?? 0) >= 0.3;
|
|
972
|
+
})
|
|
973
|
+
.sort(([filePathA, verifyA], [filePathB, verifyB]) => {
|
|
974
|
+
const prefA = getVerifyFocusFilePreference({
|
|
975
|
+
query: context.initContext?.userQuery ?? "",
|
|
976
|
+
filePath: filePathA,
|
|
977
|
+
});
|
|
978
|
+
const prefB = getVerifyFocusFilePreference({
|
|
979
|
+
query: context.initContext?.userQuery ?? "",
|
|
980
|
+
filePath: filePathB,
|
|
981
|
+
});
|
|
982
|
+
const scoreA = (verifyA.fileConfidence ?? 0) + (prefA.applies ? prefA.score : 0);
|
|
983
|
+
const scoreB = (verifyB.fileConfidence ?? 0) + (prefB.applies ? prefB.score : 0);
|
|
984
|
+
// Example: two files both score 0.50 from verify; the one matching an explicit query path wins.
|
|
985
|
+
return scoreB - scoreA;
|
|
986
|
+
})
|
|
987
|
+
.slice(0, MAX_FILES)
|
|
988
|
+
.map(([filePath]) => ({
|
|
989
|
+
path: filePath,
|
|
990
|
+
analysis: fileAnalysis[filePath] ?? {
|
|
991
|
+
semanticAnalyzed: false,
|
|
992
|
+
intent: "relevant",
|
|
993
|
+
proposedChanges: {
|
|
994
|
+
summary: "Verify evidence selected this file as relevant to the final answer.",
|
|
995
|
+
targets: ["verify evidence"],
|
|
996
|
+
rationale: "Used as a grounded answer source because verify evidence was stronger than semantic analysis coverage.",
|
|
997
|
+
},
|
|
998
|
+
},
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Keeps verify-backed execution files in the final packet when a verify-focus answer also has broad semantic docs.
|
|
1003
|
+
* Example: a verify-wave trace can keep `mainAgentVerify.ts` and `fileCheckStep.ts`
|
|
1004
|
+
* ahead of `README.md` instead of treating verify files as fallback-only.
|
|
1005
|
+
*/
|
|
1006
|
+
function resolveMeaningfulAnswerFiles(context, meaningfulFiles, verifyMeaningfulFiles) {
|
|
1007
|
+
if (meaningfulFiles.length === 0) {
|
|
1008
|
+
return verifyMeaningfulFiles;
|
|
1009
|
+
}
|
|
1010
|
+
const query = context.initContext?.userQuery ?? "";
|
|
1011
|
+
if (!isVerifyFocusQueryText(query) || verifyMeaningfulFiles.length === 0) {
|
|
1012
|
+
const focusedWorkingFiles = resolveFocusedWorkingAnswerFiles(context, meaningfulFiles.map(file => file.path));
|
|
1013
|
+
if (focusedWorkingFiles.length === 0) {
|
|
1014
|
+
return meaningfulFiles;
|
|
1015
|
+
}
|
|
1016
|
+
const merged = new Map();
|
|
1017
|
+
for (const file of [...focusedWorkingFiles, ...meaningfulFiles]) {
|
|
1018
|
+
if (!merged.has(file.path)) {
|
|
1019
|
+
merged.set(file.path, file);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return Array.from(merged.values()).slice(0, MAX_FILES);
|
|
1023
|
+
}
|
|
1024
|
+
const merged = new Map();
|
|
1025
|
+
const focusedWorkingFiles = resolveFocusedWorkingAnswerFiles(context, [...meaningfulFiles, ...verifyMeaningfulFiles].map(file => file.path));
|
|
1026
|
+
for (const file of [...verifyMeaningfulFiles, ...focusedWorkingFiles, ...meaningfulFiles]) {
|
|
1027
|
+
if (!merged.has(file.path)) {
|
|
1028
|
+
merged.set(file.path, file);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return Array.from(merged.values()).slice(0, MAX_FILES);
|
|
1032
|
+
}
|
|
1033
|
+
function resolveFocusedWorkingAnswerFiles(context, existingPaths) {
|
|
1034
|
+
const query = context.initContext?.userQuery ?? "";
|
|
1035
|
+
if (!isVerifyFocusQueryText(query)) {
|
|
1036
|
+
return [];
|
|
1037
|
+
}
|
|
1038
|
+
const seen = new Set(existingPaths);
|
|
1039
|
+
const fileAnalysis = context.analysis?.fileAnalysis ?? {};
|
|
1040
|
+
const selected = new Set(context.analysis?.focus?.selectedFiles ?? []);
|
|
1041
|
+
return (context.workingFiles ?? [])
|
|
1042
|
+
.filter(file => selected.has(file.path))
|
|
1043
|
+
.filter(file => !seen.has(file.path))
|
|
1044
|
+
.filter(file => file.code?.trim())
|
|
1045
|
+
.filter(file => getVerifyFocusFilePreference({ query, filePath: file.path }).score > 0)
|
|
1046
|
+
.map(file => ({
|
|
1047
|
+
path: file.path,
|
|
1048
|
+
analysis: fileAnalysis[file.path] ?? {
|
|
1049
|
+
semanticAnalyzed: false,
|
|
1050
|
+
intent: "relevant",
|
|
1051
|
+
proposedChanges: {
|
|
1052
|
+
summary: "Focused working file selected during verify and kept for final grounding.",
|
|
1053
|
+
targets: ["verify focus"],
|
|
1054
|
+
rationale: "Used as a grounded answer source because the verify-focused query selected and executed this file.",
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
}))
|
|
1058
|
+
.slice(0, MAX_FILES);
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Collects exact symbol/structural excerpts for one file from verify evidence.
|
|
1062
|
+
*
|
|
1063
|
+
* Example:
|
|
1064
|
+
* - evidence excerpts ["getGroundingWaveBudget", "runVerifyWave"] -> preserve both in confidence order
|
|
1065
|
+
* - import excerpt `import { upsertFileTemplate } from '../db/sqlTemplates.js';` -> preserve `upsertFileTemplate`
|
|
1066
|
+
*/
|
|
1067
|
+
function collectVerifyExactSymbols(context, filePath) {
|
|
1068
|
+
const evidence = context.analysis?.verify?.byFile?.[filePath]?.evidence ?? [];
|
|
1069
|
+
const ranked = evidence
|
|
1070
|
+
.filter(item => (item.type === "symbol" || item.type === "structural") &&
|
|
1071
|
+
typeof item.excerpt === "string" &&
|
|
1072
|
+
item.excerpt.trim().length > 0 &&
|
|
1073
|
+
(item.confidence ?? 0) >= MIN_EXACT_VERIFY_CONFIDENCE)
|
|
1074
|
+
.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
|
|
1075
|
+
const out = [];
|
|
1076
|
+
const seen = new Set();
|
|
1077
|
+
for (const item of ranked) {
|
|
1078
|
+
const identifiers = extractUsefulExactIdentifiers(item.excerpt);
|
|
1079
|
+
if (identifiers.length === 0)
|
|
1080
|
+
continue;
|
|
1081
|
+
for (const identifier of identifiers) {
|
|
1082
|
+
const key = identifier.toLowerCase();
|
|
1083
|
+
if (seen.has(key))
|
|
1084
|
+
continue;
|
|
1085
|
+
seen.add(key);
|
|
1086
|
+
out.push(identifier);
|
|
1087
|
+
if (out.length >= 4)
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
if (out.length >= 4)
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
return out;
|
|
1094
|
+
}
|
|
1095
|
+
function collectAnswerExactIdentifiers(context, filePath, query) {
|
|
1096
|
+
const verifyIdentifiers = collectVerifyExactSymbols(context, filePath);
|
|
1097
|
+
if (verifyIdentifiers.length > 0) {
|
|
1098
|
+
return verifyIdentifiers;
|
|
1099
|
+
}
|
|
1100
|
+
if (!shouldUseQueryAlignedExactIdentifiers(context, query)) {
|
|
1101
|
+
return [];
|
|
1102
|
+
}
|
|
1103
|
+
return collectQueryAlignedIdentifiers(context, filePath, query);
|
|
1104
|
+
}
|
|
1105
|
+
function shouldUseQueryAlignedExactIdentifiers(context, query) {
|
|
1106
|
+
if ((context.analysis?.intent?.questions?.length ?? 0) > 1) {
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
if (context.analysis?.scopeType === "repo-wide") {
|
|
1110
|
+
return true;
|
|
1111
|
+
}
|
|
1112
|
+
const normalized = query.trim().toLowerCase();
|
|
1113
|
+
return /\b(explain|summarize|trace|how|where)\b/.test(normalized);
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Falls back to query-aligned code identifiers when verify evidence did not preserve the exact symbol.
|
|
1117
|
+
* Example: "How is verify triggered?" can surface `runVerify` from MainAgent.ts.
|
|
1118
|
+
*/
|
|
1119
|
+
function collectQueryAlignedIdentifiers(context, filePath, query) {
|
|
1120
|
+
const code = context.workingFiles?.find(file => file.path === filePath)?.code ?? "";
|
|
1121
|
+
if (!code.trim()) {
|
|
1122
|
+
return [];
|
|
1123
|
+
}
|
|
1124
|
+
const queryTerms = extractQueryFocusTerms(query);
|
|
1125
|
+
if (queryTerms.length === 0) {
|
|
1126
|
+
return [];
|
|
1127
|
+
}
|
|
1128
|
+
const declarationIdentifiers = extractDeclarationIdentifiers(code)
|
|
1129
|
+
.filter(identifier => {
|
|
1130
|
+
const identifierParts = splitIdentifierParts(identifier);
|
|
1131
|
+
const overlap = identifierParts.filter(part => queryTerms.includes(part));
|
|
1132
|
+
return overlap.length > 0;
|
|
1133
|
+
});
|
|
1134
|
+
const preferredDeclarations = keepUsefulIdentifiers(declarationIdentifiers);
|
|
1135
|
+
if (preferredDeclarations.length > 0) {
|
|
1136
|
+
return preferredDeclarations.slice(0, 4);
|
|
1137
|
+
}
|
|
1138
|
+
const identifiers = code.match(/[A-Za-z_$][A-Za-z0-9_$]{2,}/g) ?? [];
|
|
1139
|
+
const ranked = [];
|
|
1140
|
+
const seen = new Set();
|
|
1141
|
+
for (const identifier of identifiers) {
|
|
1142
|
+
if (seen.has(identifier))
|
|
1143
|
+
continue;
|
|
1144
|
+
const identifierParts = splitIdentifierParts(identifier);
|
|
1145
|
+
const overlap = identifierParts.filter(part => queryTerms.includes(part));
|
|
1146
|
+
if (overlap.length === 0)
|
|
1147
|
+
continue;
|
|
1148
|
+
if (!/^(run|route|verify|search|resolve|trigger)/i.test(identifier) && overlap.length < 2) {
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
seen.add(identifier);
|
|
1152
|
+
ranked.push(identifier);
|
|
1153
|
+
if (ranked.length >= 4)
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
return keepUsefulIdentifiers(ranked);
|
|
1157
|
+
}
|
|
1158
|
+
function extractDeclarationIdentifiers(code) {
|
|
1159
|
+
const matches = Array.from(code.matchAll(/\b(?:export\s+)?(?:async\s+)?(?:function|const|let|var|class)\s+([A-Za-z_$][\w$]*)\b/g));
|
|
1160
|
+
return matches.map(match => match[1]).filter(Boolean);
|
|
1161
|
+
}
|
|
1162
|
+
function buildQueryAlignedFileLabel(filePath, query) {
|
|
1163
|
+
const stem = path.basename(filePath, path.extname(filePath));
|
|
1164
|
+
const label = stem
|
|
1165
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
1166
|
+
.replace(/[_-]+/g, " ")
|
|
1167
|
+
.trim();
|
|
1168
|
+
if (!label) {
|
|
1169
|
+
return undefined;
|
|
1170
|
+
}
|
|
1171
|
+
const normalizedLabel = label.toLowerCase();
|
|
1172
|
+
const normalizedQuery = query.toLowerCase();
|
|
1173
|
+
if (normalizedQuery.includes(normalizedLabel)) {
|
|
1174
|
+
return label.replace(/\b\w/g, char => char.toUpperCase());
|
|
1175
|
+
}
|
|
1176
|
+
const labelTerms = normalizedLabel.split(/\s+/).filter(term => term.length >= 4);
|
|
1177
|
+
const queryTerms = extractQueryFocusTerms(query);
|
|
1178
|
+
const overlap = labelTerms.filter(term => queryTerms.includes(term));
|
|
1179
|
+
if (overlap.length >= Math.min(2, labelTerms.length)) {
|
|
1180
|
+
return label.replace(/\b\w/g, char => char.toUpperCase());
|
|
1181
|
+
}
|
|
1182
|
+
return undefined;
|
|
1183
|
+
}
|
|
1184
|
+
function extractQueryFocusTerms(query) {
|
|
1185
|
+
const ignored = new Set([
|
|
1186
|
+
"where", "what", "when", "why", "which", "who", "how", "is", "are", "do", "does",
|
|
1187
|
+
"the", "this", "that", "these", "those", "and", "for", "with", "from", "into", "across",
|
|
1188
|
+
]);
|
|
1189
|
+
return Array.from(new Set((query.match(/[A-Za-z][A-Za-z0-9]+/g) ?? [])
|
|
1190
|
+
.map(token => normalizeQueryFocusTerm(token))
|
|
1191
|
+
.filter(token => token.length >= 4 && !ignored.has(token))));
|
|
1192
|
+
}
|
|
1193
|
+
function normalizeQueryFocusTerm(token) {
|
|
1194
|
+
const lower = token.toLowerCase();
|
|
1195
|
+
if (lower.endsWith("ing") && lower.length > 5)
|
|
1196
|
+
return lower.slice(0, -3);
|
|
1197
|
+
if (lower.endsWith("ed") && lower.length > 4)
|
|
1198
|
+
return lower.slice(0, -2);
|
|
1199
|
+
if (lower.endsWith("es") && lower.length > 4)
|
|
1200
|
+
return lower.slice(0, -2);
|
|
1201
|
+
if (lower.endsWith("s") && lower.length > 4)
|
|
1202
|
+
return lower.slice(0, -1);
|
|
1203
|
+
return lower;
|
|
1204
|
+
}
|
|
1205
|
+
function splitIdentifierParts(identifier) {
|
|
1206
|
+
return identifier
|
|
1207
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
1208
|
+
.replace(/[_-]+/g, " ")
|
|
1209
|
+
.toLowerCase()
|
|
1210
|
+
.split(/\s+/)
|
|
1211
|
+
.map(part => normalizeQueryFocusTerm(part))
|
|
1212
|
+
.filter(part => part.length >= 4);
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Extracts declaration-like identifiers and drops weak generic tokens.
|
|
1216
|
+
*
|
|
1217
|
+
* Examples:
|
|
1218
|
+
* - `export function getGroundingWaveBudget()` -> `getGroundingWaveBudget`
|
|
1219
|
+
* - `import { upsertFileTemplate } from '../db/sqlTemplates.js';` -> `upsertFileTemplate`
|
|
1220
|
+
* - comment-only snippets -> `[]`
|
|
1221
|
+
*/
|
|
1222
|
+
function extractUsefulExactIdentifiers(excerpt) {
|
|
1223
|
+
const trimmed = excerpt.trim();
|
|
1224
|
+
if (!trimmed)
|
|
1225
|
+
return [];
|
|
1226
|
+
const importIdentifiers = extractImportIdentifiers(trimmed);
|
|
1227
|
+
if (importIdentifiers.length > 0) {
|
|
1228
|
+
return importIdentifiers;
|
|
1229
|
+
}
|
|
1230
|
+
const declarationPatterns = [
|
|
1231
|
+
/\b(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\b/,
|
|
1232
|
+
/\b(?:export\s+)?(?:const|let|var|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)\b/,
|
|
1233
|
+
/\b([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(/,
|
|
1234
|
+
];
|
|
1235
|
+
for (const pattern of declarationPatterns) {
|
|
1236
|
+
const match = trimmed.match(pattern);
|
|
1237
|
+
const identifier = match?.[1];
|
|
1238
|
+
if (identifier) {
|
|
1239
|
+
return keepUsefulIdentifiers([identifier]);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (/^[A-Za-z_$][\w$]*$/.test(trimmed)) {
|
|
1243
|
+
return keepUsefulIdentifiers([trimmed]);
|
|
1244
|
+
}
|
|
1245
|
+
return [];
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Extracts local names from import snippets so the final answer can cite exact imported symbols.
|
|
1249
|
+
*
|
|
1250
|
+
* Examples:
|
|
1251
|
+
* - `import foo, { bar as baz } from 'pkg'` -> [`foo`, `baz`]
|
|
1252
|
+
* - `import * as sqlTemplates from '../db/sqlTemplates.js'` -> [`sqlTemplates`]
|
|
1253
|
+
*/
|
|
1254
|
+
function extractImportIdentifiers(excerpt) {
|
|
1255
|
+
if (!/\bimport\b/.test(excerpt)) {
|
|
1256
|
+
return [];
|
|
1257
|
+
}
|
|
1258
|
+
const out = [];
|
|
1259
|
+
const importStatements = excerpt.match(/import[\s\S]*?from\s+['"][^'"]+['"];?/g) ?? [];
|
|
1260
|
+
for (const statement of importStatements) {
|
|
1261
|
+
const namespaceMatch = statement.match(/import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\b/);
|
|
1262
|
+
if (namespaceMatch?.[1]) {
|
|
1263
|
+
out.push(namespaceMatch[1]);
|
|
1264
|
+
}
|
|
1265
|
+
const defaultMatch = statement.match(/import\s+([A-Za-z_$][\w$]*)(?:\s*,|\s+from\b)/);
|
|
1266
|
+
if (defaultMatch?.[1] && defaultMatch[1] !== "type") {
|
|
1267
|
+
out.push(defaultMatch[1]);
|
|
1268
|
+
}
|
|
1269
|
+
const namedMatch = statement.match(/\{([^}]+)\}/);
|
|
1270
|
+
if (namedMatch?.[1]) {
|
|
1271
|
+
for (const part of namedMatch[1].split(",")) {
|
|
1272
|
+
const cleaned = part.trim().replace(/^type\s+/, "");
|
|
1273
|
+
if (!cleaned)
|
|
1274
|
+
continue;
|
|
1275
|
+
const aliasMatch = cleaned.match(/^(?:[A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)$/);
|
|
1276
|
+
if (aliasMatch?.[1]) {
|
|
1277
|
+
out.push(aliasMatch[1]);
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
const directMatch = cleaned.match(/^([A-Za-z_$][\w$]*)$/);
|
|
1281
|
+
if (directMatch?.[1]) {
|
|
1282
|
+
out.push(directMatch[1]);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return keepUsefulIdentifiers(out);
|
|
1288
|
+
}
|
|
1289
|
+
function keepUsefulIdentifiers(identifiers) {
|
|
1290
|
+
const out = [];
|
|
1291
|
+
const seen = new Set();
|
|
1292
|
+
for (const identifier of identifiers) {
|
|
1293
|
+
const trimmed = identifier.trim();
|
|
1294
|
+
if (!trimmed || GENERIC_VERIFY_IDENTIFIERS.has(trimmed.toLowerCase())) {
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
const key = trimmed.toLowerCase();
|
|
1298
|
+
if (seen.has(key)) {
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
seen.add(key);
|
|
1302
|
+
out.push(trimmed);
|
|
1303
|
+
}
|
|
1304
|
+
return out;
|
|
281
1305
|
}
|
|
1306
|
+
//# sourceMappingURL=finalAnswerModule.js.map
|