scai 0.1.178 → 1.0.1

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.
Files changed (770) hide show
  1. package/README.md +171 -260
  2. package/dist/__tests__/CommitSuggesterCmd.test.js +112 -0
  3. package/dist/__tests__/CommitSuggesterCmd.test.js.map +1 -0
  4. package/dist/__tests__/EvalReportCmd.test.js +645 -0
  5. package/dist/__tests__/EvalReportCmd.test.js.map +1 -0
  6. package/dist/__tests__/ModelCmd.test.js +64 -0
  7. package/dist/__tests__/ModelCmd.test.js.map +1 -0
  8. package/dist/__tests__/agents/agentActions.test.js +345 -0
  9. package/dist/__tests__/agents/agentActions.test.js.map +1 -0
  10. package/dist/__tests__/agents/agentFeedback.test.js +118 -0
  11. package/dist/__tests__/agents/agentFeedback.test.js.map +1 -0
  12. package/dist/__tests__/agents/agentGeneralScope.test.js +74 -0
  13. package/dist/__tests__/agents/agentGeneralScope.test.js.map +1 -0
  14. package/dist/__tests__/agents/agentLoop.test.js +1723 -0
  15. package/dist/__tests__/agents/agentLoop.test.js.map +1 -0
  16. package/dist/__tests__/agents/agentPolicyState.test.js +948 -0
  17. package/dist/__tests__/agents/agentPolicyState.test.js.map +1 -0
  18. package/dist/__tests__/agents/agentReadEvidence.test.js +170 -0
  19. package/dist/__tests__/agents/agentReadEvidence.test.js.map +1 -0
  20. package/dist/__tests__/agents/agentReadPersistence.test.js +129 -0
  21. package/dist/__tests__/agents/agentReadPersistence.test.js.map +1 -0
  22. package/dist/__tests__/agents/agentResumeCheckpoint.test.js +90 -0
  23. package/dist/__tests__/agents/agentResumeCheckpoint.test.js.map +1 -0
  24. package/dist/__tests__/agents/agentSearchBatchPlanner.test.js +289 -0
  25. package/dist/__tests__/agents/agentSearchBatchPlanner.test.js.map +1 -0
  26. package/dist/__tests__/agents/agentSearchOwnership.test.js +166 -0
  27. package/dist/__tests__/agents/agentSearchOwnership.test.js.map +1 -0
  28. package/dist/__tests__/agents/agentSearchRanking.test.js +139 -0
  29. package/dist/__tests__/agents/agentSearchRanking.test.js.map +1 -0
  30. package/dist/__tests__/agents/agentSearchRouting.test.js +584 -0
  31. package/dist/__tests__/agents/agentSearchRouting.test.js.map +1 -0
  32. package/dist/__tests__/agents/agentSearchScoring.test.js +23 -0
  33. package/dist/__tests__/agents/agentSearchScoring.test.js.map +1 -0
  34. package/dist/__tests__/agents/agentSearchShared.test.js +78 -0
  35. package/dist/__tests__/agents/agentSearchShared.test.js.map +1 -0
  36. package/dist/__tests__/agents/agentStateMachine.test.js +58 -0
  37. package/dist/__tests__/agents/agentStateMachine.test.js.map +1 -0
  38. package/dist/__tests__/agents/agentTaskPersistence.test.js +156 -0
  39. package/dist/__tests__/agents/agentTaskPersistence.test.js.map +1 -0
  40. package/dist/__tests__/agents/agentTools.test.js +69 -0
  41. package/dist/__tests__/agents/agentTools.test.js.map +1 -0
  42. package/dist/__tests__/agents/agentTransform.test.js +779 -0
  43. package/dist/__tests__/agents/agentTransform.test.js.map +1 -0
  44. package/dist/__tests__/agents/analysisPlanGenStep.test.js +157 -0
  45. package/dist/__tests__/agents/analysisPlanGenStep.test.js.map +1 -0
  46. package/dist/__tests__/agents/answerOnlyCompletion.test.js +75 -0
  47. package/dist/__tests__/agents/answerOnlyCompletion.test.js.map +1 -0
  48. package/dist/__tests__/agents/decideNextAction.test.js +1662 -0
  49. package/dist/__tests__/agents/decideNextAction.test.js.map +1 -0
  50. package/dist/__tests__/agents/deriveFocusFromSearchStep.test.js +258 -0
  51. package/dist/__tests__/agents/deriveFocusFromSearchStep.test.js.map +1 -0
  52. package/dist/__tests__/agents/evidenceVerifierStep.test.js +113 -0
  53. package/dist/__tests__/agents/evidenceVerifierStep.test.js.map +1 -0
  54. package/dist/__tests__/agents/executionPolicyResolver.test.js +208 -0
  55. package/dist/__tests__/agents/executionPolicyResolver.test.js.map +1 -0
  56. package/dist/__tests__/agents/fileCheckStep.test.js +299 -0
  57. package/dist/__tests__/agents/fileCheckStep.test.js.map +1 -0
  58. package/dist/__tests__/agents/giveUpEvaluatorStep.test.js +35 -0
  59. package/dist/__tests__/agents/giveUpEvaluatorStep.test.js.map +1 -0
  60. package/dist/__tests__/agents/guardState.test.js +297 -0
  61. package/dist/__tests__/agents/guardState.test.js.map +1 -0
  62. package/dist/__tests__/agents/mainAgentHeuristics.test.js +72 -0
  63. package/dist/__tests__/agents/mainAgentHeuristics.test.js.map +1 -0
  64. package/dist/__tests__/agents/objectiveEvaluatorStep.test.js +60 -0
  65. package/dist/__tests__/agents/objectiveEvaluatorStep.test.js.map +1 -0
  66. package/dist/__tests__/agents/outerLoopRecoveryEvaluator.test.js +207 -0
  67. package/dist/__tests__/agents/outerLoopRecoveryEvaluator.test.js.map +1 -0
  68. package/dist/__tests__/agents/prompting.test.js +363 -0
  69. package/dist/__tests__/agents/prompting.test.js.map +1 -0
  70. package/dist/__tests__/agents/readinessGateStep.test.js +180 -0
  71. package/dist/__tests__/agents/readinessGateStep.test.js.map +1 -0
  72. package/dist/__tests__/agents/reasonNextStep.test.js +56 -0
  73. package/dist/__tests__/agents/reasonNextStep.test.js.map +1 -0
  74. package/dist/__tests__/agents/reasonNextTaskStep.test.js +284 -0
  75. package/dist/__tests__/agents/reasonNextTaskStep.test.js.map +1 -0
  76. package/dist/__tests__/agents/resolveAgentTargetClassification.test.js +170 -0
  77. package/dist/__tests__/agents/resolveAgentTargetClassification.test.js.map +1 -0
  78. package/dist/__tests__/agents/resolveProgressState.test.js +526 -0
  79. package/dist/__tests__/agents/resolveProgressState.test.js.map +1 -0
  80. package/dist/__tests__/agents/resumeCheckpoint.test.js +50 -0
  81. package/dist/__tests__/agents/resumeCheckpoint.test.js.map +1 -0
  82. package/dist/__tests__/agents/routingDecisionStep.test.js +134 -0
  83. package/dist/__tests__/agents/routingDecisionStep.test.js.map +1 -0
  84. package/dist/__tests__/agents/scopeClassificationStep.test.js +118 -0
  85. package/dist/__tests__/agents/scopeClassificationStep.test.js.map +1 -0
  86. package/dist/__tests__/agents/searchContext.test.js +97 -0
  87. package/dist/__tests__/agents/searchContext.test.js.map +1 -0
  88. package/dist/__tests__/agents/selectRelevantSourcesStep.test.js +73 -0
  89. package/dist/__tests__/agents/selectRelevantSourcesStep.test.js.map +1 -0
  90. package/dist/__tests__/agents/structuredOutput.test.js +45 -0
  91. package/dist/__tests__/agents/structuredOutput.test.js.map +1 -0
  92. package/dist/__tests__/agents/transformPlanGenStep.fallback.test.js +59 -0
  93. package/dist/__tests__/agents/transformPlanGenStep.fallback.test.js.map +1 -0
  94. package/dist/__tests__/agents/transformPlanGenStep.test.js +92 -0
  95. package/dist/__tests__/agents/transformPlanGenStep.test.js.map +1 -0
  96. package/dist/__tests__/agents/understandIntentStep.test.js +237 -0
  97. package/dist/__tests__/agents/understandIntentStep.test.js.map +1 -0
  98. package/dist/__tests__/agents/understandResumeContext.test.js +65 -0
  99. package/dist/__tests__/agents/understandResumeContext.test.js.map +1 -0
  100. package/dist/__tests__/agents/understandScope.test.js +227 -0
  101. package/dist/__tests__/agents/understandScope.test.js.map +1 -0
  102. package/dist/__tests__/agents/validateChangesStep.test.js +52 -0
  103. package/dist/__tests__/agents/validateChangesStep.test.js.map +1 -0
  104. package/dist/__tests__/askCommandTaskBinding.test.js +176 -0
  105. package/dist/__tests__/askCommandTaskBinding.test.js.map +1 -0
  106. package/dist/__tests__/commandVisibility.test.js +25 -0
  107. package/dist/__tests__/commandVisibility.test.js.map +1 -0
  108. package/dist/__tests__/config.devOutput.test.js +82 -0
  109. package/dist/__tests__/config.devOutput.test.js.map +1 -0
  110. package/dist/__tests__/currentContext.test.js +43 -0
  111. package/dist/__tests__/currentContext.test.js.map +1 -0
  112. package/dist/__tests__/daemonWorker.test.js +51 -0
  113. package/dist/__tests__/daemonWorker.test.js.map +1 -0
  114. package/dist/__tests__/dialogState.test.js +113 -0
  115. package/dist/__tests__/dialogState.test.js.map +1 -0
  116. package/dist/__tests__/evalCommands.test.js +506 -0
  117. package/dist/__tests__/evalCommands.test.js.map +1 -0
  118. package/dist/__tests__/evalCommandsSummary.test.js +68 -0
  119. package/dist/__tests__/evalCommandsSummary.test.js.map +1 -0
  120. package/dist/__tests__/example.test.js +1 -0
  121. package/dist/__tests__/example.test.js.map +1 -0
  122. package/dist/__tests__/factory.commitCommand.test.js +45 -0
  123. package/dist/__tests__/factory.commitCommand.test.js.map +1 -0
  124. package/dist/__tests__/factory.devOutputCommand.test.js +122 -0
  125. package/dist/__tests__/factory.devOutputCommand.test.js.map +1 -0
  126. package/dist/__tests__/factory.evalCommands.test.js +38 -0
  127. package/dist/__tests__/factory.evalCommands.test.js.map +1 -0
  128. package/dist/__tests__/factory.planCommand.test.js +35 -0
  129. package/dist/__tests__/factory.planCommand.test.js.map +1 -0
  130. package/dist/__tests__/factory.setupCommand.test.js +34 -0
  131. package/dist/__tests__/factory.setupCommand.test.js.map +1 -0
  132. package/dist/__tests__/factory.statusCommand.test.js +54 -0
  133. package/dist/__tests__/factory.statusCommand.test.js.map +1 -0
  134. package/dist/__tests__/fileRules/queryTokenRules.test.js +35 -0
  135. package/dist/__tests__/fileRules/queryTokenRules.test.js.map +1 -0
  136. package/dist/__tests__/fileRules/searchPathClassification.test.js +57 -0
  137. package/dist/__tests__/fileRules/searchPathClassification.test.js.map +1 -0
  138. package/dist/__tests__/generate.ollamaRecovery.test.js +344 -0
  139. package/dist/__tests__/generate.ollamaRecovery.test.js.map +1 -0
  140. package/dist/__tests__/index.modelStartup.test.js +24 -0
  141. package/dist/__tests__/index.modelStartup.test.js.map +1 -0
  142. package/dist/__tests__/indexCmd.test.js +85 -0
  143. package/dist/__tests__/indexCmd.test.js.map +1 -0
  144. package/dist/__tests__/indexSlashCommand.test.js +50 -0
  145. package/dist/__tests__/indexSlashCommand.test.js.map +1 -0
  146. package/dist/__tests__/ollamaService.test.js +103 -0
  147. package/dist/__tests__/ollamaService.test.js.map +1 -0
  148. package/dist/__tests__/pipeline/modules/codeTransformModule.small-file.test.js +68 -0
  149. package/dist/__tests__/pipeline/modules/codeTransformModule.small-file.test.js.map +1 -0
  150. package/dist/__tests__/pipeline/modules/commitSuggesterModule.test.js +68 -0
  151. package/dist/__tests__/pipeline/modules/commitSuggesterModule.test.js.map +1 -0
  152. package/dist/__tests__/pipeline/modules/fileSearchModule.test.js +284 -0
  153. package/dist/__tests__/pipeline/modules/fileSearchModule.test.js.map +1 -0
  154. package/dist/__tests__/pipeline/modules/finalAnswerModule.test.js +1139 -0
  155. package/dist/__tests__/pipeline/modules/finalAnswerModule.test.js.map +1 -0
  156. package/dist/__tests__/pipeline/modules/readFileModule.test.js +146 -0
  157. package/dist/__tests__/pipeline/modules/readFileModule.test.js.map +1 -0
  158. package/dist/__tests__/pipeline/modules/semanticAnalysisModule.test.js +192 -0
  159. package/dist/__tests__/pipeline/modules/semanticAnalysisModule.test.js.map +1 -0
  160. package/dist/__tests__/repoIdentity.test.js +31 -0
  161. package/dist/__tests__/repoIdentity.test.js.map +1 -0
  162. package/dist/__tests__/resumeContext.test.js +87 -0
  163. package/dist/__tests__/resumeContext.test.js.map +1 -0
  164. package/dist/__tests__/resumeState.test.js +239 -0
  165. package/dist/__tests__/resumeState.test.js.map +1 -0
  166. package/dist/__tests__/search/SearchOrchestrator.test.js +836 -0
  167. package/dist/__tests__/search/SearchOrchestrator.test.js.map +1 -0
  168. package/dist/__tests__/shellDialogUi.test.js +52 -0
  169. package/dist/__tests__/shellDialogUi.test.js.map +1 -0
  170. package/dist/__tests__/shellSession.test.js +102 -0
  171. package/dist/__tests__/shellSession.test.js.map +1 -0
  172. package/dist/__tests__/statusOwner.test.js +215 -0
  173. package/dist/__tests__/statusOwner.test.js.map +1 -0
  174. package/dist/__tests__/testing/contextEval.test.js +244 -0
  175. package/dist/__tests__/testing/contextEval.test.js.map +1 -0
  176. package/dist/__tests__/testing/harnessArtifacts.test.js +124 -0
  177. package/dist/__tests__/testing/harnessArtifacts.test.js.map +1 -0
  178. package/dist/__tests__/testing/llmTraceSession.test.js +67 -0
  179. package/dist/__tests__/testing/llmTraceSession.test.js.map +1 -0
  180. package/dist/__tests__/testing/registerDevCliCommands.test.js +35 -0
  181. package/dist/__tests__/testing/registerDevCliCommands.test.js.map +1 -0
  182. package/dist/__tests__/testing/runDiagnosis.test.js +159 -0
  183. package/dist/__tests__/testing/runDiagnosis.test.js.map +1 -0
  184. package/dist/__tests__/testing/runtimeLogReader.test.js +66 -0
  185. package/dist/__tests__/testing/runtimeLogReader.test.js.map +1 -0
  186. package/dist/__tests__/testing/testCommands.test.js +53 -0
  187. package/dist/__tests__/testing/testCommands.test.js.map +1 -0
  188. package/dist/__tests__/utils/compileSearchQuery.test.js +38 -0
  189. package/dist/__tests__/utils/compileSearchQuery.test.js.map +1 -0
  190. package/dist/__tests__/utils/consolePresentation.test.js +105 -0
  191. package/dist/__tests__/utils/consolePresentation.test.js.map +1 -0
  192. package/dist/__tests__/utils/extractFileReferences.test.js +41 -0
  193. package/dist/__tests__/utils/extractFileReferences.test.js.map +1 -0
  194. package/dist/__tests__/utils/log.test.js +34 -0
  195. package/dist/__tests__/utils/log.test.js.map +1 -0
  196. package/dist/__tests__/utils/runtimeLogger.test.js +200 -0
  197. package/dist/__tests__/utils/runtimeLogger.test.js.map +1 -0
  198. package/dist/__tests__/utils/spinner.test.js +31 -0
  199. package/dist/__tests__/utils/spinner.test.js.map +1 -0
  200. package/dist/__tests__/utils/verifyFocusPreference.test.js +41 -0
  201. package/dist/__tests__/utils/verifyFocusPreference.test.js.map +1 -0
  202. package/dist/agent/actions/index.js +301 -0
  203. package/dist/agent/actions/index.js.map +1 -0
  204. package/dist/agent/actions/normalize.js +360 -0
  205. package/dist/agent/actions/normalize.js.map +1 -0
  206. package/dist/agent/actions/schemas.js +129 -0
  207. package/dist/agent/actions/schemas.js.map +1 -0
  208. package/dist/agent/evidence/index.js +320 -0
  209. package/dist/agent/evidence/index.js.map +1 -0
  210. package/dist/agent/feedback/index.js +187 -0
  211. package/dist/agent/feedback/index.js.map +1 -0
  212. package/dist/agent/finalization/index.js +35 -0
  213. package/dist/agent/finalization/index.js.map +1 -0
  214. package/dist/agent/index.js +126 -0
  215. package/dist/agent/index.js.map +1 -0
  216. package/dist/agent/logging/index.js +350 -0
  217. package/dist/agent/logging/index.js.map +1 -0
  218. package/dist/agent/persistence/boot.js +58 -0
  219. package/dist/agent/persistence/boot.js.map +1 -0
  220. package/dist/agent/persistence/currentTask.js +36 -0
  221. package/dist/agent/persistence/currentTask.js.map +1 -0
  222. package/dist/agent/persistence/hydrate.js +42 -0
  223. package/dist/agent/persistence/hydrate.js.map +1 -0
  224. package/dist/agent/persistence/index.js +15 -0
  225. package/dist/agent/persistence/index.js.map +1 -0
  226. package/dist/agent/persistence/snapshots.js +97 -0
  227. package/dist/agent/persistence/snapshots.js.map +1 -0
  228. package/dist/agent/persistence/steps.js +95 -0
  229. package/dist/agent/persistence/steps.js.map +1 -0
  230. package/dist/agent/persistence/tasks.js +195 -0
  231. package/dist/agent/persistence/tasks.js.map +1 -0
  232. package/dist/agent/persistence/turns.js +92 -0
  233. package/dist/agent/persistence/turns.js.map +1 -0
  234. package/dist/agent/policy/ambiguityResolution.js +226 -0
  235. package/dist/agent/policy/ambiguityResolution.js.map +1 -0
  236. package/dist/agent/policy/contracts.js +2 -0
  237. package/dist/agent/policy/contracts.js.map +1 -0
  238. package/dist/agent/policy/coveragePolicy.js +309 -0
  239. package/dist/agent/policy/coveragePolicy.js.map +1 -0
  240. package/dist/agent/policy/endDecisionPolicy.js +31 -0
  241. package/dist/agent/policy/endDecisionPolicy.js.map +1 -0
  242. package/dist/agent/policy/index.js +344 -0
  243. package/dist/agent/policy/index.js.map +1 -0
  244. package/dist/agent/policy/loopReview.js +778 -0
  245. package/dist/agent/policy/loopReview.js.map +1 -0
  246. package/dist/agent/policy/readinessPolicy.js +108 -0
  247. package/dist/agent/policy/readinessPolicy.js.map +1 -0
  248. package/dist/agent/policy/resolutionPipeline.js +356 -0
  249. package/dist/agent/policy/resolutionPipeline.js.map +1 -0
  250. package/dist/agent/policy/targetClassification.js +33 -0
  251. package/dist/agent/policy/targetClassification.js.map +1 -0
  252. package/dist/agent/prompting/actionChoice.js +90 -0
  253. package/dist/agent/prompting/actionChoice.js.map +1 -0
  254. package/dist/agent/prompting/finalAnswer.js +38 -0
  255. package/dist/agent/prompting/finalAnswer.js.map +1 -0
  256. package/dist/agent/prompting/index.js +14 -0
  257. package/dist/agent/prompting/index.js.map +1 -0
  258. package/dist/agent/prompting/plan.js +59 -0
  259. package/dist/agent/prompting/plan.js.map +1 -0
  260. package/dist/agent/prompting/transform.js +175 -0
  261. package/dist/agent/prompting/transform.js.map +1 -0
  262. package/dist/agent/prompting/understand.js +70 -0
  263. package/dist/agent/prompting/understand.js.map +1 -0
  264. package/dist/agent/read/freshness.js +29 -0
  265. package/dist/agent/read/freshness.js.map +1 -0
  266. package/dist/agent/read/fullReadPrompt.js +43 -0
  267. package/dist/agent/read/fullReadPrompt.js.map +1 -0
  268. package/dist/agent/read/index.js +140 -0
  269. package/dist/agent/read/index.js.map +1 -0
  270. package/dist/agent/read/persistence.js +88 -0
  271. package/dist/agent/read/persistence.js.map +1 -0
  272. package/dist/agent/read/summarizeReadEvidence.js +733 -0
  273. package/dist/agent/read/summarizeReadEvidence.js.map +1 -0
  274. package/dist/agent/read/targetResolution.js +126 -0
  275. package/dist/agent/read/targetResolution.js.map +1 -0
  276. package/dist/agent/resume/checkpoint.js +41 -0
  277. package/dist/agent/resume/checkpoint.js.map +1 -0
  278. package/dist/agent/runtime/lifecycle.js +67 -0
  279. package/dist/agent/runtime/lifecycle.js.map +1 -0
  280. package/dist/agent/runtime/progress.js +178 -0
  281. package/dist/agent/runtime/progress.js.map +1 -0
  282. package/dist/agent/runtime/runAgentLoop.js +402 -0
  283. package/dist/agent/runtime/runAgentLoop.js.map +1 -0
  284. package/dist/agent/runtime/runAgentPlanOnly.js +127 -0
  285. package/dist/agent/runtime/runAgentPlanOnly.js.map +1 -0
  286. package/dist/agent/runtime/understand.js +336 -0
  287. package/dist/agent/runtime/understand.js.map +1 -0
  288. package/dist/agent/search/batchPlanner.js +274 -0
  289. package/dist/agent/search/batchPlanner.js.map +1 -0
  290. package/dist/agent/search/candidateRetentionPolicy.js +184 -0
  291. package/dist/agent/search/candidateRetentionPolicy.js.map +1 -0
  292. package/dist/agent/search/directory.js +51 -0
  293. package/dist/agent/search/directory.js.map +1 -0
  294. package/dist/agent/search/exactTarget.js +151 -0
  295. package/dist/agent/search/exactTarget.js.map +1 -0
  296. package/dist/agent/search/fragment.js +110 -0
  297. package/dist/agent/search/fragment.js.map +1 -0
  298. package/dist/agent/search/index.js +166 -0
  299. package/dist/agent/search/index.js.map +1 -0
  300. package/dist/agent/search/laneClassifier.js +119 -0
  301. package/dist/agent/search/laneClassifier.js.map +1 -0
  302. package/dist/agent/search/limits.js +10 -0
  303. package/dist/agent/search/limits.js.map +1 -0
  304. package/dist/agent/search/ranking.js +22 -0
  305. package/dist/agent/search/ranking.js.map +1 -0
  306. package/dist/agent/search/regex.js +83 -0
  307. package/dist/agent/search/regex.js.map +1 -0
  308. package/dist/agent/search/routePolicy.js +11 -0
  309. package/dist/agent/search/routePolicy.js.map +1 -0
  310. package/dist/agent/search/searchContext.js +128 -0
  311. package/dist/agent/search/searchContext.js.map +1 -0
  312. package/dist/agent/search/semantic.js +113 -0
  313. package/dist/agent/search/semantic.js.map +1 -0
  314. package/dist/agent/search/semanticIndexSearch.js +202 -0
  315. package/dist/agent/search/semanticIndexSearch.js.map +1 -0
  316. package/dist/agent/search/shared.js +283 -0
  317. package/dist/agent/search/shared.js.map +1 -0
  318. package/dist/agent/search/shell.js +202 -0
  319. package/dist/agent/search/shell.js.map +1 -0
  320. package/dist/agent/search/snippetEvidence.js +57 -0
  321. package/dist/agent/search/snippetEvidence.js.map +1 -0
  322. package/dist/agent/search/types.js +2 -0
  323. package/dist/agent/search/types.js.map +1 -0
  324. package/dist/agent/state/index.js +99 -0
  325. package/dist/agent/state/index.js.map +1 -0
  326. package/dist/agent/state/memory.js +56 -0
  327. package/dist/agent/state/memory.js.map +1 -0
  328. package/dist/agent/structuredOutput/index.js +28 -0
  329. package/dist/agent/structuredOutput/index.js.map +1 -0
  330. package/dist/agent/tools/index.js +199 -0
  331. package/dist/agent/tools/index.js.map +1 -0
  332. package/dist/agent/transform/index.js +519 -0
  333. package/dist/agent/transform/index.js.map +1 -0
  334. package/dist/agent/transform/syntax.js +49 -0
  335. package/dist/agent/transform/syntax.js.map +1 -0
  336. package/dist/agent/types.js +20 -0
  337. package/dist/agent/types.js.map +1 -0
  338. package/dist/agents/actionRegistry.js +114 -0
  339. package/dist/agents/actionRegistry.js.map +1 -0
  340. package/dist/agents/agent.js +5 -0
  341. package/dist/agents/agent.js.map +1 -0
  342. package/dist/agents/agentActions.js +5 -0
  343. package/dist/agents/agentActions.js.map +1 -0
  344. package/dist/agents/agentEvidence.js +5 -0
  345. package/dist/agents/agentEvidence.js.map +1 -0
  346. package/dist/agents/agentFeedback.js +5 -0
  347. package/dist/agents/agentFeedback.js.map +1 -0
  348. package/dist/agents/agentLogging.js +5 -0
  349. package/dist/agents/agentLogging.js.map +1 -0
  350. package/dist/agents/agentLoop.js +5 -0
  351. package/dist/agents/agentLoop.js.map +1 -0
  352. package/dist/agents/agentMemory.js +5 -0
  353. package/dist/agents/agentMemory.js.map +1 -0
  354. package/dist/agents/agentPlanMode.js +5 -0
  355. package/dist/agents/agentPlanMode.js.map +1 -0
  356. package/dist/agents/agentPolicyState.js +5 -0
  357. package/dist/agents/agentPolicyState.js.map +1 -0
  358. package/dist/agents/agentProgress.js +93 -0
  359. package/dist/agents/agentProgress.js.map +1 -0
  360. package/dist/agents/agentSchemas.js +5 -0
  361. package/dist/agents/agentSchemas.js.map +1 -0
  362. package/dist/agents/agentSearchScoring.js +5 -0
  363. package/dist/agents/agentSearchScoring.js.map +1 -0
  364. package/dist/agents/agentStateMachine.js +5 -0
  365. package/dist/agents/agentStateMachine.js.map +1 -0
  366. package/dist/agents/agentTools.js +5 -0
  367. package/dist/agents/agentTools.js.map +1 -0
  368. package/dist/agents/agentTypes.js +5 -0
  369. package/dist/agents/agentTypes.js.map +1 -0
  370. package/dist/agents/agentUnderstand.js +5 -0
  371. package/dist/agents/agentUnderstand.js.map +1 -0
  372. package/dist/agents/analysisPlanGenStep.js +194 -17
  373. package/dist/agents/analysisPlanGenStep.js.map +1 -0
  374. package/dist/agents/answerOnlyCompletion.js +32 -0
  375. package/dist/agents/answerOnlyCompletion.js.map +1 -0
  376. package/dist/agents/collaboratorStep.js +1 -0
  377. package/dist/agents/collaboratorStep.js.map +1 -0
  378. package/dist/agents/decideNextAction.js +444 -0
  379. package/dist/agents/decideNextAction.js.map +1 -0
  380. package/dist/agents/deriveFocusFromSearchStep.js +83 -0
  381. package/dist/agents/deriveFocusFromSearchStep.js.map +1 -0
  382. package/dist/agents/evidenceVerifierStep.js +104 -13
  383. package/dist/agents/evidenceVerifierStep.js.map +1 -0
  384. package/dist/agents/fileCheckStep.js +381 -12
  385. package/dist/agents/fileCheckStep.js.map +1 -0
  386. package/dist/agents/giveUpEvaluatorStep.js +63 -0
  387. package/dist/agents/giveUpEvaluatorStep.js.map +1 -0
  388. package/dist/agents/guardPolicy.js +20 -0
  389. package/dist/agents/guardPolicy.js.map +1 -0
  390. package/dist/agents/guards/executionPolicyResolver.js +165 -0
  391. package/dist/agents/guards/executionPolicyResolver.js.map +1 -0
  392. package/dist/agents/guards/guardState.js +195 -0
  393. package/dist/agents/guards/guardState.js.map +1 -0
  394. package/dist/agents/guards/resolveProgressState.js +403 -0
  395. package/dist/agents/guards/resolveProgressState.js.map +1 -0
  396. package/dist/agents/infoPlanGenStep.js +66 -8
  397. package/dist/agents/infoPlanGenStep.js.map +1 -0
  398. package/dist/agents/integrateFeedbackStep.js +1 -0
  399. package/dist/agents/integrateFeedbackStep.js.map +1 -0
  400. package/dist/agents/iterationFileSelector.js +8 -7
  401. package/dist/agents/iterationFileSelector.js.map +1 -0
  402. package/dist/agents/mainAgentActivityLog.js +85 -0
  403. package/dist/agents/mainAgentActivityLog.js.map +1 -0
  404. package/dist/agents/mainAgentHeuristics.js +173 -0
  405. package/dist/agents/mainAgentHeuristics.js.map +1 -0
  406. package/dist/agents/mainAgentVerify.js +159 -0
  407. package/dist/agents/mainAgentVerify.js.map +1 -0
  408. package/dist/agents/objectiveEvaluatorStep.js +103 -0
  409. package/dist/agents/objectiveEvaluatorStep.js.map +1 -0
  410. package/dist/agents/outerLoopRecoveryEvaluator.js +108 -0
  411. package/dist/agents/outerLoopRecoveryEvaluator.js.map +1 -0
  412. package/dist/agents/readinessGateStep.js +95 -9
  413. package/dist/agents/readinessGateStep.js.map +1 -0
  414. package/dist/agents/reasonNextStep.js +9 -8
  415. package/dist/agents/reasonNextStep.js.map +1 -0
  416. package/dist/agents/reasonNextTaskStep.js +267 -144
  417. package/dist/agents/reasonNextTaskStep.js.map +1 -0
  418. package/dist/agents/researchPlanGenStep.js +61 -25
  419. package/dist/agents/researchPlanGenStep.js.map +1 -0
  420. package/dist/agents/resolveAgentTargetClassification.js +5 -0
  421. package/dist/agents/resolveAgentTargetClassification.js.map +1 -0
  422. package/dist/agents/resolveExecutionModeStep.js +1 -0
  423. package/dist/agents/resolveExecutionModeStep.js.map +1 -0
  424. package/dist/agents/resolveExplicitTargetsStep.js +74 -0
  425. package/dist/agents/resolveExplicitTargetsStep.js.map +1 -0
  426. package/dist/agents/routingDecisionStep.js +58 -11
  427. package/dist/agents/routingDecisionStep.js.map +1 -0
  428. package/dist/agents/scopeClassificationStep.js +66 -3
  429. package/dist/agents/scopeClassificationStep.js.map +1 -0
  430. package/dist/agents/selectRelevantSourcesStep.js +13 -5
  431. package/dist/agents/selectRelevantSourcesStep.js.map +1 -0
  432. package/dist/agents/structuralPreloadStep.js +3 -4
  433. package/dist/agents/structuralPreloadStep.js.map +1 -0
  434. package/dist/agents/transformPlanGenStep.js +105 -18
  435. package/dist/agents/transformPlanGenStep.js.map +1 -0
  436. package/dist/agents/understandIntentStep.js +237 -17
  437. package/dist/agents/understandIntentStep.js.map +1 -0
  438. package/dist/agents/validateChangesStep.js +16 -2
  439. package/dist/agents/validateChangesStep.js.map +1 -0
  440. package/dist/agents/writeFileStep.js +1 -0
  441. package/dist/agents/writeFileStep.js.map +1 -0
  442. package/dist/commands/AskCmd.js +139 -44
  443. package/dist/commands/AskCmd.js.map +1 -0
  444. package/dist/commands/BackupCmd.js +1 -0
  445. package/dist/commands/BackupCmd.js.map +1 -0
  446. package/dist/commands/ChangeLogUpdateCmd.js +1 -0
  447. package/dist/commands/ChangeLogUpdateCmd.js.map +1 -0
  448. package/dist/commands/CommitSuggesterCmd.js +55 -13
  449. package/dist/commands/CommitSuggesterCmd.js.map +1 -0
  450. package/dist/commands/DaemonCmd.js +52 -14
  451. package/dist/commands/DaemonCmd.js.map +1 -0
  452. package/dist/commands/DeleteIndex.js +1 -0
  453. package/dist/commands/DeleteIndex.js.map +1 -0
  454. package/dist/commands/EvalReportCmd.js +374 -0
  455. package/dist/commands/EvalReportCmd.js.map +1 -0
  456. package/dist/commands/FindCmd.js +1 -0
  457. package/dist/commands/FindCmd.js.map +1 -0
  458. package/dist/commands/GitCmd.js +1 -0
  459. package/dist/commands/GitCmd.js.map +1 -0
  460. package/dist/commands/IndexCmd.js +11 -79
  461. package/dist/commands/IndexCmd.js.map +1 -0
  462. package/dist/commands/InspectCmd.js +1 -0
  463. package/dist/commands/InspectCmd.js.map +1 -0
  464. package/dist/commands/ModelCmd.js +24 -0
  465. package/dist/commands/ModelCmd.js.map +1 -0
  466. package/dist/commands/ReadlineSingleton.js +1 -0
  467. package/dist/commands/ReadlineSingleton.js.map +1 -0
  468. package/dist/commands/ResetDbCmd.js +18 -1
  469. package/dist/commands/ResetDbCmd.js.map +1 -0
  470. package/dist/commands/ReviewCmd.js +1 -0
  471. package/dist/commands/ReviewCmd.js.map +1 -0
  472. package/dist/commands/StatusCmd.js +22 -0
  473. package/dist/commands/StatusCmd.js.map +1 -0
  474. package/dist/commands/StopDaemonCmd.js +1 -0
  475. package/dist/commands/StopDaemonCmd.js.map +1 -0
  476. package/dist/commands/SummaryCmd.js +1 -0
  477. package/dist/commands/SummaryCmd.js.map +1 -0
  478. package/dist/commands/SwitchCmd.js +9 -15
  479. package/dist/commands/SwitchCmd.js.map +1 -0
  480. package/dist/commands/TasksCmd.js +142 -57
  481. package/dist/commands/TasksCmd.js.map +1 -0
  482. package/dist/commands/TestCmd.js +66 -0
  483. package/dist/commands/TestCmd.js.map +1 -0
  484. package/dist/commands/WorkflowCmd.js +1 -0
  485. package/dist/commands/WorkflowCmd.js.map +1 -0
  486. package/dist/commands/commandVisibility.js +27 -0
  487. package/dist/commands/commandVisibility.js.map +1 -0
  488. package/dist/commands/evalCommands.js +1337 -0
  489. package/dist/commands/evalCommands.js.map +1 -0
  490. package/dist/commands/factory.js +206 -38
  491. package/dist/commands/factory.js.map +1 -0
  492. package/dist/config.js +62 -11
  493. package/dist/config.js.map +1 -0
  494. package/dist/constants.js +21 -3
  495. package/dist/constants.js.map +1 -0
  496. package/dist/context.js +33 -32
  497. package/dist/context.js.map +1 -0
  498. package/dist/daemon/daemonQueues.js +1 -20
  499. package/dist/daemon/daemonQueues.js.map +1 -0
  500. package/dist/daemon/daemonWorker.js +26 -37
  501. package/dist/daemon/daemonWorker.js.map +1 -0
  502. package/dist/daemon/generateSummaries.js +1 -0
  503. package/dist/daemon/generateSummaries.js.map +1 -0
  504. package/dist/daemon/runFolderCapsuleBatch.js +1 -0
  505. package/dist/daemon/runFolderCapsuleBatch.js.map +1 -0
  506. package/dist/daemon/runIndexingBatch.js +1 -0
  507. package/dist/daemon/runIndexingBatch.js.map +1 -0
  508. package/dist/daemon/runKgBatch.js +9 -1
  509. package/dist/daemon/runKgBatch.js.map +1 -0
  510. package/dist/db/backup.js +1 -0
  511. package/dist/db/backup.js.map +1 -0
  512. package/dist/db/client.js +18 -3
  513. package/dist/db/client.js.map +1 -0
  514. package/dist/db/fileIndex.js +110 -152
  515. package/dist/db/fileIndex.js.map +1 -0
  516. package/dist/db/functionExtractors/extractFromJava.js +1 -0
  517. package/dist/db/functionExtractors/extractFromJava.js.map +1 -0
  518. package/dist/db/functionExtractors/extractFromJs.js +1 -0
  519. package/dist/db/functionExtractors/extractFromJs.js.map +1 -0
  520. package/dist/db/functionExtractors/extractFromTs.js +1 -0
  521. package/dist/db/functionExtractors/extractFromTs.js.map +1 -0
  522. package/dist/db/functionExtractors/extractFromXML.js +1 -0
  523. package/dist/db/functionExtractors/extractFromXML.js.map +1 -0
  524. package/dist/db/functionExtractors/index.js +1 -0
  525. package/dist/db/functionExtractors/index.js.map +1 -0
  526. package/dist/db/functionIndex.js +9 -0
  527. package/dist/db/functionIndex.js.map +1 -0
  528. package/dist/db/schema.js +314 -99
  529. package/dist/db/schema.js.map +1 -0
  530. package/dist/db/sqlTemplates.js +1 -0
  531. package/dist/db/sqlTemplates.js.map +1 -0
  532. package/dist/fileRules/builtins.js +1 -0
  533. package/dist/fileRules/builtins.js.map +1 -0
  534. package/dist/fileRules/classifyFile.js +1 -0
  535. package/dist/fileRules/classifyFile.js.map +1 -0
  536. package/dist/fileRules/codeAllowedExtensions.js +1 -0
  537. package/dist/fileRules/codeAllowedExtensions.js.map +1 -0
  538. package/dist/fileRules/detectFileType.js +1 -0
  539. package/dist/fileRules/detectFileType.js.map +1 -0
  540. package/dist/fileRules/fileClassifier.js +1 -0
  541. package/dist/fileRules/fileClassifier.js.map +1 -0
  542. package/dist/fileRules/fileExceptions.js +1 -0
  543. package/dist/fileRules/fileExceptions.js.map +1 -0
  544. package/dist/fileRules/ignoredExtensions.js +1 -0
  545. package/dist/fileRules/ignoredExtensions.js.map +1 -0
  546. package/dist/fileRules/ignoredPaths.js +48 -5
  547. package/dist/fileRules/ignoredPaths.js.map +1 -0
  548. package/dist/fileRules/queryTokenRules.js +176 -0
  549. package/dist/fileRules/queryTokenRules.js.map +1 -0
  550. package/dist/fileRules/searchPathClassification.js +58 -0
  551. package/dist/fileRules/searchPathClassification.js.map +1 -0
  552. package/dist/fileRules/shouldIgnoreFiles.js +1 -0
  553. package/dist/fileRules/shouldIgnoreFiles.js.map +1 -0
  554. package/dist/fileRules/stopWords.js +9 -0
  555. package/dist/fileRules/stopWords.js.map +1 -0
  556. package/dist/fileRules/wellKnownRepoFiles.js +1 -0
  557. package/dist/fileRules/wellKnownRepoFiles.js.map +1 -0
  558. package/dist/git/commitSummary.js +227 -0
  559. package/dist/git/commitSummary.js.map +1 -0
  560. package/dist/github/api.js +1 -0
  561. package/dist/github/api.js.map +1 -0
  562. package/dist/github/auth.js +1 -0
  563. package/dist/github/auth.js.map +1 -0
  564. package/dist/github/github.js +1 -0
  565. package/dist/github/github.js.map +1 -0
  566. package/dist/github/githubAuthCheck.js +1 -0
  567. package/dist/github/githubAuthCheck.js.map +1 -0
  568. package/dist/github/postComments.js +1 -0
  569. package/dist/github/postComments.js.map +1 -0
  570. package/dist/github/repo.js +15 -24
  571. package/dist/github/repo.js.map +1 -0
  572. package/dist/github/token.js +1 -0
  573. package/dist/github/token.js.map +1 -0
  574. package/dist/github/types.js +1 -0
  575. package/dist/github/types.js.map +1 -0
  576. package/dist/index.js +318 -37
  577. package/dist/index.js.map +1 -0
  578. package/dist/lib/generate.js +264 -20
  579. package/dist/lib/generate.js.map +1 -0
  580. package/dist/lib/generateFolderCapsules.js +1 -0
  581. package/dist/lib/generateFolderCapsules.js.map +1 -0
  582. package/dist/lib/ollamaModelPolicy.js +59 -0
  583. package/dist/lib/ollamaModelPolicy.js.map +1 -0
  584. package/dist/lib/spinner.js +29 -9
  585. package/dist/lib/spinner.js.map +1 -0
  586. package/dist/modelSetup.js +25 -78
  587. package/dist/modelSetup.js.map +1 -0
  588. package/dist/pipeline/modules/changeLogModule.js +10 -1
  589. package/dist/pipeline/modules/changeLogModule.js.map +1 -0
  590. package/dist/pipeline/modules/cleanupModule.js +1 -0
  591. package/dist/pipeline/modules/cleanupModule.js.map +1 -0
  592. package/dist/pipeline/modules/codeTransformModule.js +10 -16
  593. package/dist/pipeline/modules/codeTransformModule.js.map +1 -0
  594. package/dist/pipeline/modules/commentModule.js +12 -0
  595. package/dist/pipeline/modules/commentModule.js.map +1 -0
  596. package/dist/pipeline/modules/commitSuggesterModule.js +82 -12
  597. package/dist/pipeline/modules/commitSuggesterModule.js.map +1 -0
  598. package/dist/pipeline/modules/contextReviewModule.js +12 -1
  599. package/dist/pipeline/modules/contextReviewModule.js.map +1 -0
  600. package/dist/pipeline/modules/dialogAnswerModule.js +58 -0
  601. package/dist/pipeline/modules/dialogAnswerModule.js.map +1 -0
  602. package/dist/pipeline/modules/fileSearchModule.js +5 -143
  603. package/dist/pipeline/modules/fileSearchModule.js.map +1 -0
  604. package/dist/pipeline/modules/finalAnswerModule.js +1176 -151
  605. package/dist/pipeline/modules/finalAnswerModule.js.map +1 -0
  606. package/dist/pipeline/modules/kgModule.js +18 -1
  607. package/dist/pipeline/modules/kgModule.js.map +1 -0
  608. package/dist/pipeline/modules/planAnswerModule.js +99 -0
  609. package/dist/pipeline/modules/planAnswerModule.js.map +1 -0
  610. package/dist/pipeline/modules/readFileModule.js +300 -0
  611. package/dist/pipeline/modules/readFileModule.js.map +1 -0
  612. package/dist/pipeline/modules/reviewModule.js +10 -1
  613. package/dist/pipeline/modules/reviewModule.js.map +1 -0
  614. package/dist/pipeline/modules/searchDbModule.js +159 -0
  615. package/dist/pipeline/modules/searchDbModule.js.map +1 -0
  616. package/dist/pipeline/modules/searchListDirectoryModule.js +62 -0
  617. package/dist/pipeline/modules/searchListDirectoryModule.js.map +1 -0
  618. package/dist/pipeline/modules/searchModuleShared.js +71 -0
  619. package/dist/pipeline/modules/searchModuleShared.js.map +1 -0
  620. package/dist/pipeline/modules/searchRegexModule.js +59 -0
  621. package/dist/pipeline/modules/searchRegexModule.js.map +1 -0
  622. package/dist/pipeline/modules/semanticAnalysisModule.js +185 -28
  623. package/dist/pipeline/modules/semanticAnalysisModule.js.map +1 -0
  624. package/dist/pipeline/modules/summaryModule.js +11 -1
  625. package/dist/pipeline/modules/summaryModule.js.map +1 -0
  626. package/dist/pipeline/registry/moduleRegistry.js +9 -0
  627. package/dist/pipeline/registry/moduleRegistry.js.map +1 -0
  628. package/dist/pipeline/runModulePipeline.js +1 -0
  629. package/dist/pipeline/runModulePipeline.js.map +1 -0
  630. package/dist/scripts/dbScriptSupport.js +172 -0
  631. package/dist/scripts/dbScriptSupport.js.map +1 -0
  632. package/dist/scripts/dbcheck.js +173 -267
  633. package/dist/scripts/dbcheck.js.map +1 -0
  634. package/dist/scripts/dboverview.js +161 -0
  635. package/dist/scripts/dboverview.js.map +1 -0
  636. package/dist/scripts/migrateDb.js +1 -0
  637. package/dist/scripts/migrateDb.js.map +1 -0
  638. package/dist/search/SearchOrchestrator.js +928 -0
  639. package/dist/search/SearchOrchestrator.js.map +1 -0
  640. package/dist/search/sharedRankingPolicy.js +283 -0
  641. package/dist/search/sharedRankingPolicy.js.map +1 -0
  642. package/dist/setup/reindexOwner.js +97 -0
  643. package/dist/setup/reindexOwner.js.map +1 -0
  644. package/dist/setup/setupOwner.js +100 -0
  645. package/dist/setup/setupOwner.js.map +1 -0
  646. package/dist/shell/dialogUi.js +81 -0
  647. package/dist/shell/dialogUi.js.map +1 -0
  648. package/dist/shellSession.js +126 -0
  649. package/dist/shellSession.js.map +1 -0
  650. package/dist/status/statusOwner.js +239 -0
  651. package/dist/status/statusOwner.js.map +1 -0
  652. package/dist/testing/contextEval.js +514 -0
  653. package/dist/testing/contextEval.js.map +1 -0
  654. package/dist/testing/fixtures/transform/small-file.input.js +5 -0
  655. package/dist/testing/fixtures/transform/small-file.input.js.map +1 -0
  656. package/dist/testing/harnessArtifacts.js +112 -0
  657. package/dist/testing/harnessArtifacts.js.map +1 -0
  658. package/dist/testing/llmTraceSession.js +67 -0
  659. package/dist/testing/llmTraceSession.js.map +1 -0
  660. package/dist/testing/registerDevCliCommands.js +43 -0
  661. package/dist/testing/registerDevCliCommands.js.map +1 -0
  662. package/dist/testing/runDiagnosis.js +248 -0
  663. package/dist/testing/runDiagnosis.js.map +1 -0
  664. package/dist/testing/runtimeLogReader.js +144 -0
  665. package/dist/testing/runtimeLogReader.js.map +1 -0
  666. package/dist/testing/testCommands.js +35 -303
  667. package/dist/testing/testCommands.js.map +1 -0
  668. package/dist/testing/testRegistry.js +233 -0
  669. package/dist/testing/testRegistry.js.map +1 -0
  670. package/dist/types.js +1 -0
  671. package/dist/types.js.map +1 -0
  672. package/dist/utils/buildContextualPrompt.js +26 -75
  673. package/dist/utils/buildContextualPrompt.js.map +1 -0
  674. package/dist/utils/changeLogPrompt.js +1 -0
  675. package/dist/utils/changeLogPrompt.js.map +1 -0
  676. package/dist/utils/checkModel.js +17 -92
  677. package/dist/utils/checkModel.js.map +1 -0
  678. package/dist/utils/commentMap.js +1 -0
  679. package/dist/utils/commentMap.js.map +1 -0
  680. package/dist/utils/compileSearchQuery.js +23 -9
  681. package/dist/utils/compileSearchQuery.js.map +1 -0
  682. package/dist/utils/consolePresentation.js +208 -0
  683. package/dist/utils/consolePresentation.js.map +1 -0
  684. package/dist/utils/contentUtils.js +17 -2
  685. package/dist/utils/contentUtils.js.map +1 -0
  686. package/dist/utils/debugContext.js +1 -0
  687. package/dist/utils/debugContext.js.map +1 -0
  688. package/dist/utils/dialogState.js +201 -0
  689. package/dist/utils/dialogState.js.map +1 -0
  690. package/dist/utils/editor.js +1 -0
  691. package/dist/utils/editor.js.map +1 -0
  692. package/dist/utils/executionEvidence.js +50 -0
  693. package/dist/utils/executionEvidence.js.map +1 -0
  694. package/dist/utils/extractFileReferences.js +140 -6
  695. package/dist/utils/extractFileReferences.js.map +1 -0
  696. package/dist/utils/fileEvidenceCache.js +50 -0
  697. package/dist/utils/fileEvidenceCache.js.map +1 -0
  698. package/dist/utils/fileTree.js +1 -0
  699. package/dist/utils/fileTree.js.map +1 -0
  700. package/dist/utils/loadRelevantFolderCapsules.js +35 -5
  701. package/dist/utils/loadRelevantFolderCapsules.js.map +1 -0
  702. package/dist/utils/log.js +10 -1
  703. package/dist/utils/log.js.map +1 -0
  704. package/dist/utils/normalizeData.js +1 -0
  705. package/dist/utils/normalizeData.js.map +1 -0
  706. package/dist/utils/ollamaModelStatus.js +28 -0
  707. package/dist/utils/ollamaModelStatus.js.map +1 -0
  708. package/dist/utils/ollamaService.js +294 -0
  709. package/dist/utils/ollamaService.js.map +1 -0
  710. package/dist/utils/outputFormatter.js +1 -0
  711. package/dist/utils/outputFormatter.js.map +1 -0
  712. package/dist/utils/parseTaggedContent.js +1 -0
  713. package/dist/utils/parseTaggedContent.js.map +1 -0
  714. package/dist/utils/planActions.js +27 -46
  715. package/dist/utils/planActions.js.map +1 -0
  716. package/dist/utils/promptBuilderHelper.js +1 -0
  717. package/dist/utils/promptBuilderHelper.js.map +1 -0
  718. package/dist/utils/promptLogHelper.js +29 -13
  719. package/dist/utils/promptLogHelper.js.map +1 -0
  720. package/dist/utils/queryAnchors.js +71 -0
  721. package/dist/utils/queryAnchors.js.map +1 -0
  722. package/dist/utils/repoIdentity.js +82 -0
  723. package/dist/utils/repoIdentity.js.map +1 -0
  724. package/dist/utils/repoKey.js +1 -0
  725. package/dist/utils/repoKey.js.map +1 -0
  726. package/dist/utils/resolveTargetsToFiles.js +1 -0
  727. package/dist/utils/resolveTargetsToFiles.js.map +1 -0
  728. package/dist/utils/resumeContext.js +219 -0
  729. package/dist/utils/resumeContext.js.map +1 -0
  730. package/dist/utils/resumeState.js +310 -0
  731. package/dist/utils/resumeState.js.map +1 -0
  732. package/dist/utils/rollingPlan.js +118 -0
  733. package/dist/utils/rollingPlan.js.map +1 -0
  734. package/dist/utils/runQueryWithDaemonControl.js +11 -3
  735. package/dist/utils/runQueryWithDaemonControl.js.map +1 -0
  736. package/dist/utils/runtimeLogger.js +252 -0
  737. package/dist/utils/runtimeLogger.js.map +1 -0
  738. package/dist/utils/sanitizeQuery.js +1 -0
  739. package/dist/utils/sanitizeQuery.js.map +1 -0
  740. package/dist/utils/sharedUtils.js +1 -0
  741. package/dist/utils/sharedUtils.js.map +1 -0
  742. package/dist/utils/sleep.js +1 -0
  743. package/dist/utils/sleep.js.map +1 -0
  744. package/dist/utils/splitCodeIntoChunk.js +1 -0
  745. package/dist/utils/splitCodeIntoChunk.js.map +1 -0
  746. package/dist/utils/time.js +66 -0
  747. package/dist/utils/time.js.map +1 -0
  748. package/dist/utils/verifyFocusPreference.js +107 -0
  749. package/dist/utils/verifyFocusPreference.js.map +1 -0
  750. package/dist/utils/vscode.js +1 -0
  751. package/dist/utils/vscode.js.map +1 -0
  752. package/dist/workflow/workflowResolver.js +1 -0
  753. package/dist/workflow/workflowResolver.js.map +1 -0
  754. package/dist/workflow/workflowRunner.js +1 -0
  755. package/dist/workflow/workflowRunner.js.map +1 -0
  756. package/package.json +3 -3
  757. package/dist/agents/MainAgent.js +0 -1886
  758. package/dist/agents/contextReviewStep.js +0 -101
  759. package/dist/agents/finalPlanGenStep.js +0 -107
  760. package/dist/agents/structuralAnalysisStep.js +0 -46
  761. package/dist/agents/validationAnalysisStep.js +0 -87
  762. package/dist/pipeline/modules/chunkManagerModule.js +0 -24
  763. package/dist/pipeline/modules/cleanGeneratedTestsModule.js +0 -33
  764. package/dist/pipeline/modules/fileReaderModule.js +0 -72
  765. package/dist/pipeline/modules/gatherInfoModule.js +0 -181
  766. package/dist/pipeline/modules/generateTestsModule.js +0 -68
  767. package/dist/pipeline/modules/preserveCodeModule.js +0 -195
  768. package/dist/pipeline/modules/refactorModule.js +0 -40
  769. package/dist/pipeline/modules/repairTestsModule.js +0 -48
  770. package/dist/pipeline/modules/runTestsModule.js +0 -37
@@ -0,0 +1,1723 @@
1
+ /**
2
+ * Covers the first simple agent loop slice without touching the legacy MainAgent.
3
+ *
4
+ * Why this file exists:
5
+ * - the new runtime needs one tight end-to-end test while it is still small
6
+ * - read-only answer flow should stay stable before more actions are reintroduced
7
+ * - mocked model calls let the test focus on loop behavior instead of model variance
8
+ */
9
+ import fs from "fs";
10
+ import os from "os";
11
+ import path from "path";
12
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
+ const configState = vi.hoisted(() => ({
14
+ getDevOutput: vi.fn(() => false),
15
+ }));
16
+ const searchState = vi.hoisted(() => ({
17
+ dbFileCandidates: new Map(),
18
+ }));
19
+ vi.mock("../../config.js", () => ({
20
+ Config: {
21
+ getDevOutput: configState.getDevOutput,
22
+ },
23
+ }));
24
+ import { runAgentLoop } from "../../agents/agentLoop.js";
25
+ import { createAgentMemory } from "../../agents/agentMemory.js";
26
+ const generateMock = vi.fn();
27
+ const state = vi.hoisted(() => ({
28
+ logRuntimeRecord: vi.fn(),
29
+ }));
30
+ vi.mock("../../lib/generate.js", () => ({
31
+ generate: (...args) => generateMock(...args),
32
+ }));
33
+ vi.mock("../../utils/promptLogHelper.js", () => ({
34
+ logInputOutput: vi.fn(),
35
+ }));
36
+ vi.mock("../../search/SearchOrchestrator.js", () => ({
37
+ SearchOrchestrator: class {
38
+ async runDbSearch(args) {
39
+ const filePaths = searchState.dbFileCandidates.get(args.query) ?? [];
40
+ return {
41
+ route: "db",
42
+ mode: args.query.includes("/resume") ? "exact-target" : "semantic-retrieval",
43
+ stagesRun: args.query.includes("/resume")
44
+ ? ["explicit-target-resolver", "fallback-semantic-retriever", "result-merger-and-ranker"]
45
+ : ["semantic-retriever", "result-merger-and-ranker"],
46
+ resolvedFiles: [],
47
+ resolvedFolders: [],
48
+ resolvedSymbols: [],
49
+ unresolvedRefs: args.query.includes("/resume") ? ["/resume"] : [],
50
+ ambiguousRefs: [],
51
+ fileCandidates: filePaths.map((filePath) => ({
52
+ id: 1,
53
+ path: filePath,
54
+ filename: path.basename(filePath),
55
+ summary: null,
56
+ type: path.extname(filePath).replace(/^\./, "") || "ts",
57
+ lastModified: new Date(0).toISOString(),
58
+ })),
59
+ folderCandidates: [],
60
+ symbolCandidates: [],
61
+ fragmentMatches: [],
62
+ patternMatches: [],
63
+ directoryEntries: [],
64
+ fallbackUsed: args.query.includes("/resume"),
65
+ };
66
+ }
67
+ async runRegexSearch() {
68
+ return {
69
+ route: "regex",
70
+ mode: "regex-execution",
71
+ stagesRun: ["regex-searcher", "result-merger-and-ranker"],
72
+ resolvedFiles: [],
73
+ resolvedFolders: [],
74
+ resolvedSymbols: [],
75
+ unresolvedRefs: [],
76
+ ambiguousRefs: [],
77
+ fileCandidates: [{
78
+ id: 1,
79
+ path: "/repo/mock-regex-hit.ts",
80
+ filename: "mock-regex-hit.ts",
81
+ summary: null,
82
+ type: "ts",
83
+ lastModified: new Date(0).toISOString(),
84
+ }],
85
+ folderCandidates: [],
86
+ symbolCandidates: [],
87
+ fragmentMatches: [],
88
+ patternMatches: [{
89
+ path: "/repo/mock-regex-hit.ts",
90
+ line: 1,
91
+ column: 1,
92
+ snippet: "mock regex hit",
93
+ }],
94
+ directoryEntries: [],
95
+ fallbackUsed: false,
96
+ };
97
+ }
98
+ runDirectoryListing(args) {
99
+ return {
100
+ route: "list-directory",
101
+ mode: "directory-listing",
102
+ stagesRun: ["directory-listing"],
103
+ resolvedFiles: [],
104
+ resolvedFolders: [args.query],
105
+ resolvedSymbols: [],
106
+ unresolvedRefs: [],
107
+ ambiguousRefs: [],
108
+ fileCandidates: [],
109
+ folderCandidates: [args.query],
110
+ symbolCandidates: [],
111
+ fragmentMatches: [],
112
+ patternMatches: [],
113
+ directoryEntries: [],
114
+ fallbackUsed: false,
115
+ };
116
+ }
117
+ },
118
+ }));
119
+ vi.mock("../../utils/runtimeLogger.js", () => ({
120
+ logRuntimeRecord: state.logRuntimeRecord,
121
+ clearRuntimeLogs: vi.fn(),
122
+ appendHumanRuntimeEntry: vi.fn(),
123
+ }));
124
+ describe("runAgentLoop", () => {
125
+ let tempDir;
126
+ let targetFile;
127
+ beforeEach(() => {
128
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scai-agent-loop-"));
129
+ targetFile = path.join(tempDir, "sample.ts");
130
+ fs.writeFileSync(targetFile, [
131
+ "/**",
132
+ " * Sample file for the new agent loop test.",
133
+ " * It exposes a few exported contracts for grounding checks.",
134
+ " */",
135
+ "export interface SampleConfig {",
136
+ " enabled: boolean;",
137
+ "}",
138
+ "export function sampleAnswer() {",
139
+ " return 'answer';",
140
+ "}",
141
+ "export const sampleLabel = 'sample';",
142
+ ].join("\n"), "utf-8");
143
+ generateMock.mockReset();
144
+ state.logRuntimeRecord.mockReset();
145
+ searchState.dbFileCandidates.clear();
146
+ configState.getDevOutput.mockReset();
147
+ configState.getDevOutput.mockReturnValue(false);
148
+ });
149
+ afterEach(() => {
150
+ fs.rmSync(tempDir, { recursive: true, force: true });
151
+ });
152
+ it("reads one explicit file and produces a final answer", async () => {
153
+ configState.getDevOutput.mockReturnValue(true);
154
+ generateMock
155
+ .mockResolvedValueOnce({
156
+ trace: {
157
+ callId: "llm-0001-agent-understand",
158
+ startedAt: "2026-05-01T06:16:21.000+02:00",
159
+ finishedAt: "2026-05-01T06:16:23.000+02:00",
160
+ durationMs: 2000,
161
+ attemptCount: 1,
162
+ },
163
+ data: JSON.stringify({
164
+ normalizedQuery: `Explain ${targetFile}`,
165
+ explicitTargets: [targetFile],
166
+ constraints: [],
167
+ primaryIntent: "Explain the target file.",
168
+ alternativeIntents: [],
169
+ ambiguityFlags: [],
170
+ }),
171
+ })
172
+ .mockResolvedValueOnce({
173
+ trace: {
174
+ callId: "llm-0002-agent-action-choice",
175
+ startedAt: "2026-05-01T06:16:24.000+02:00",
176
+ finishedAt: "2026-05-01T06:16:25.000+02:00",
177
+ durationMs: 1000,
178
+ attemptCount: 1,
179
+ },
180
+ data: JSON.stringify({
181
+ action: "read-file",
182
+ args: { target: targetFile },
183
+ reason: "Read the explicit file first.",
184
+ }),
185
+ })
186
+ .mockResolvedValueOnce({
187
+ trace: {
188
+ callId: "llm-0003-agent-read-file",
189
+ startedAt: "2026-05-01T06:16:25.100+02:00",
190
+ finishedAt: "2026-05-01T06:16:25.400+02:00",
191
+ durationMs: 300,
192
+ attemptCount: 1,
193
+ },
194
+ data: JSON.stringify({
195
+ summary: "Sample file for the new agent loop test.",
196
+ relevanceExplanation: "This file defines sampleAnswer and the exported sample contracts used by the query.",
197
+ role: "primary",
198
+ keySymbols: ["SampleConfig", "sampleAnswer", "sampleLabel"],
199
+ excerpts: [{
200
+ description: "The main answer function.",
201
+ startLine: 8,
202
+ endLine: 10,
203
+ symbols: ["sampleAnswer"],
204
+ code: "export function sampleAnswer() {\n return 'answer';\n}",
205
+ }],
206
+ risks: [],
207
+ }),
208
+ })
209
+ .mockResolvedValueOnce({
210
+ trace: {
211
+ callId: "llm-0004-agent-action-choice",
212
+ startedAt: "2026-05-01T06:16:25.500+02:00",
213
+ finishedAt: "2026-05-01T06:16:26.000+02:00",
214
+ durationMs: 500,
215
+ attemptCount: 1,
216
+ },
217
+ data: JSON.stringify({
218
+ action: "final-answer",
219
+ args: {},
220
+ reason: "The file read is enough to answer now.",
221
+ }),
222
+ })
223
+ .mockResolvedValueOnce({
224
+ trace: {
225
+ callId: "llm-0005-agent-final-answer",
226
+ startedAt: "2026-05-01T06:16:26.000+02:00",
227
+ finishedAt: "2026-05-01T06:16:29.000+02:00",
228
+ durationMs: 3000,
229
+ attemptCount: 1,
230
+ },
231
+ data: "The file defines sampleAnswer in sample.ts.",
232
+ });
233
+ const memory = createAgentMemory(`Explain ${targetFile}`, tempDir, tempDir);
234
+ const ui = {
235
+ update: vi.fn(),
236
+ message: vi.fn(),
237
+ pause: vi.fn((fn) => fn()),
238
+ succeed: vi.fn(),
239
+ fail: vi.fn(),
240
+ stop: vi.fn(),
241
+ };
242
+ await runAgentLoop(memory, ui);
243
+ const finalStep = memory.steps.find((step) => step.action === "final-answer");
244
+ expect(memory.task.status).toBe("done");
245
+ expect(memory.artifacts.finalAnswer).toContain("sampleAnswer");
246
+ expect(Object.keys(memory.evidence.fileReads)).toContain(targetFile);
247
+ expect(memory.events.map((event) => event.type)).toContain("task-finalized");
248
+ expect(finalStep).toMatchObject({
249
+ status: "done",
250
+ resultSummary: "Task completed with a final answer.",
251
+ });
252
+ expect(finalStep?.startedAt).toBeTruthy();
253
+ expect(finalStep?.completedAt).toBeTruthy();
254
+ expect(ui.update).toHaveBeenCalledWith("Understanding request");
255
+ expect(ui.update).toHaveBeenCalledWith("Choosing next action (turn 1/12)");
256
+ expect(ui.update).toHaveBeenCalledWith("Reading file (turn 1/12)");
257
+ expect(ui.update).toHaveBeenCalledWith("Producing final answer (turn 2/12)");
258
+ expect(ui.message).toHaveBeenCalledWith("[LLM] understand finished | 2s");
259
+ expect(ui.message).toHaveBeenCalledWith("[LLM] action-choice finished | 1s");
260
+ expect(ui.message).toHaveBeenCalledWith("[LLM] action-choice finished | 500ms");
261
+ expect(ui.message).toHaveBeenCalledWith("[LLM] final-answer finished | 3s");
262
+ expect(ui.message).toHaveBeenCalledWith("[STATE] step step-001 created -> pending | action=read-file");
263
+ expect(ui.message).toHaveBeenCalledWith(expect.stringContaining("[STATE] step step-001 pending -> running | action=read-file"));
264
+ expect(ui.message).toHaveBeenCalledWith("[STATE] step step-002 created -> pending | action=final-answer");
265
+ expect(ui.message).toHaveBeenCalledWith("[STATE] task active -> done | reason=completed");
266
+ expect(ui.message).not.toHaveBeenCalledWith("Understanding request.");
267
+ expect(ui.message).not.toHaveBeenCalledWith("Chose direct file read.");
268
+ expect(ui.message).not.toHaveBeenCalledWith("Producing final answer.");
269
+ expect(state.logRuntimeRecord).toHaveBeenCalledWith(expect.objectContaining({
270
+ kind: "llm",
271
+ phase: "output",
272
+ payload: expect.objectContaining({
273
+ durationMs: 3000,
274
+ attemptCount: 1,
275
+ callId: "llm-0005-agent-final-answer",
276
+ }),
277
+ }));
278
+ expect(generateMock).toHaveBeenLastCalledWith(expect.objectContaining({
279
+ content: expect.stringContaining('"keySymbols": ['),
280
+ }), expect.objectContaining({
281
+ inputContext: expect.objectContaining({
282
+ fileReads: [
283
+ expect.objectContaining({
284
+ filePath: targetFile,
285
+ keySymbols: expect.arrayContaining(["SampleConfig", "sampleAnswer", "sampleLabel"]),
286
+ }),
287
+ ],
288
+ }),
289
+ }));
290
+ });
291
+ it("lists a resolved folder first and then reads canonical folder files before answering", async () => {
292
+ const guardsDir = path.join(tempDir, "cli", "src", "agents", "guards");
293
+ const resolverFile = path.join(guardsDir, "executionPolicyResolver.ts");
294
+ const stateFile = path.join(guardsDir, "guardState.ts");
295
+ fs.mkdirSync(guardsDir, { recursive: true });
296
+ fs.writeFileSync(resolverFile, "export const resolver = true;\n", "utf-8");
297
+ fs.writeFileSync(stateFile, "export const guard = true;\n", "utf-8");
298
+ generateMock
299
+ .mockResolvedValueOnce({
300
+ data: JSON.stringify({
301
+ normalizedQuery: guardsDir,
302
+ explicitTargets: [guardsDir],
303
+ constraints: [],
304
+ primaryIntent: "Explain the guards directory.",
305
+ alternativeIntents: [],
306
+ ambiguityFlags: [],
307
+ }),
308
+ })
309
+ .mockResolvedValueOnce({
310
+ data: JSON.stringify({
311
+ action: "read-file",
312
+ args: { target: guardsDir },
313
+ reason: "Read the directory directly.",
314
+ }),
315
+ })
316
+ .mockResolvedValueOnce({
317
+ data: JSON.stringify({
318
+ action: "read-file",
319
+ args: { target: guardsDir },
320
+ reason: "Read one file from the directory.",
321
+ }),
322
+ })
323
+ .mockResolvedValueOnce({
324
+ data: JSON.stringify({
325
+ summary: "Execution policy owner.",
326
+ relevanceExplanation: "This file resolves execution policy for the guards area.",
327
+ role: "primary",
328
+ excerpts: [{
329
+ description: "Resolver export.",
330
+ startLine: 1,
331
+ endLine: 1,
332
+ symbols: ["resolver"],
333
+ code: "export const resolver = true;",
334
+ }],
335
+ risks: [],
336
+ }),
337
+ })
338
+ .mockResolvedValueOnce({
339
+ data: JSON.stringify({
340
+ action: "final-answer",
341
+ args: {},
342
+ reason: "One file should be enough.",
343
+ }),
344
+ })
345
+ .mockResolvedValueOnce({
346
+ data: JSON.stringify({
347
+ summary: "Guard state owner.",
348
+ relevanceExplanation: "This file holds guard state used by the directory the user asked about.",
349
+ role: "supporting",
350
+ excerpts: [{
351
+ description: "Guard export.",
352
+ startLine: 1,
353
+ endLine: 1,
354
+ symbols: ["guard"],
355
+ code: "export const guard = true;",
356
+ }],
357
+ risks: [],
358
+ }),
359
+ })
360
+ .mockResolvedValueOnce({
361
+ data: JSON.stringify({
362
+ action: "final-answer",
363
+ args: {},
364
+ reason: "The folder evidence is grounded now.",
365
+ }),
366
+ })
367
+ .mockResolvedValueOnce({
368
+ data: "The guards directory includes executionPolicyResolver.ts and guardState.ts.",
369
+ });
370
+ const memory = createAgentMemory(guardsDir, tempDir, tempDir);
371
+ const ui = {
372
+ update: vi.fn(),
373
+ message: vi.fn(),
374
+ pause: vi.fn((fn) => fn()),
375
+ succeed: vi.fn(),
376
+ fail: vi.fn(),
377
+ stop: vi.fn(),
378
+ };
379
+ await runAgentLoop(memory, ui);
380
+ expect(memory.task.status).toBe("done");
381
+ expect(memory.steps.map((step) => step.action)).toEqual([
382
+ "search-list-directory",
383
+ "read-file",
384
+ "read-file",
385
+ "final-answer",
386
+ ]);
387
+ expect(memory.steps.some((step) => step.status === "failed")).toBe(false);
388
+ expect(Object.keys(memory.evidence.fileReads)).toEqual(expect.arrayContaining([resolverFile, stateFile]));
389
+ expect(memory.artifacts.finalAnswer).toContain("executionPolicyResolver.ts");
390
+ });
391
+ it("does not allow final-answer until all explicit targets have been read", async () => {
392
+ const secondFile = path.join(tempDir, "factory.ts");
393
+ const thirdFile = path.join(tempDir, "runQueryWithDaemonControl.ts");
394
+ fs.writeFileSync(secondFile, "export function createFactory() {}\n", "utf-8");
395
+ fs.writeFileSync(thirdFile, "export function runQueryWithDaemonControl() {}\n", "utf-8");
396
+ generateMock
397
+ .mockResolvedValueOnce({
398
+ data: JSON.stringify({
399
+ normalizedQuery: "Summarize CLI architecture",
400
+ explicitTargets: [targetFile, secondFile, thirdFile],
401
+ constraints: [],
402
+ primaryIntent: "Summarize the CLI architecture.",
403
+ alternativeIntents: [],
404
+ ambiguityFlags: [],
405
+ }),
406
+ })
407
+ .mockResolvedValueOnce({
408
+ data: JSON.stringify({
409
+ action: "read-file",
410
+ args: { target: targetFile },
411
+ reason: "Read the first explicit target.",
412
+ }),
413
+ })
414
+ .mockResolvedValueOnce({
415
+ data: JSON.stringify({
416
+ summary: "Primary sample file.",
417
+ relevanceExplanation: "This file is one explicit target in the requested architecture summary.",
418
+ role: "primary",
419
+ excerpts: [{
420
+ description: "Sample answer export.",
421
+ startLine: 8,
422
+ endLine: 10,
423
+ symbols: ["sampleAnswer"],
424
+ code: "export function sampleAnswer() {\n return 'answer';\n}",
425
+ }],
426
+ risks: [],
427
+ }),
428
+ })
429
+ .mockResolvedValueOnce({
430
+ data: JSON.stringify({
431
+ action: "final-answer",
432
+ args: {},
433
+ reason: "One file seems enough.",
434
+ }),
435
+ })
436
+ .mockResolvedValueOnce({
437
+ data: JSON.stringify({
438
+ summary: "Factory file.",
439
+ relevanceExplanation: "This explicit target contributes the factory step in the requested architecture summary.",
440
+ role: "supporting",
441
+ excerpts: [{
442
+ description: "Factory export.",
443
+ startLine: 1,
444
+ endLine: 1,
445
+ symbols: ["createFactory"],
446
+ code: "export function createFactory() {}",
447
+ }],
448
+ risks: [],
449
+ }),
450
+ })
451
+ .mockResolvedValueOnce({
452
+ data: JSON.stringify({
453
+ action: "read-file",
454
+ args: { target: targetFile },
455
+ reason: "Read the next explicit target instead of answering.",
456
+ }),
457
+ })
458
+ .mockResolvedValueOnce({
459
+ data: JSON.stringify({
460
+ summary: "Daemon control file.",
461
+ relevanceExplanation: "This final explicit target completes the architecture path the user asked about.",
462
+ role: "supporting",
463
+ excerpts: [{
464
+ description: "Daemon control export.",
465
+ startLine: 1,
466
+ endLine: 1,
467
+ symbols: ["runQueryWithDaemonControl"],
468
+ code: "export function runQueryWithDaemonControl() {}",
469
+ }],
470
+ risks: [],
471
+ }),
472
+ })
473
+ .mockResolvedValueOnce({
474
+ data: JSON.stringify({
475
+ action: "final-answer",
476
+ args: {},
477
+ reason: "Two files seem enough.",
478
+ }),
479
+ })
480
+ .mockResolvedValueOnce({
481
+ data: JSON.stringify({
482
+ action: "read-file",
483
+ args: { target: targetFile },
484
+ reason: "Read the last explicit target instead of answering.",
485
+ }),
486
+ })
487
+ .mockResolvedValueOnce({
488
+ data: JSON.stringify({
489
+ summary: "README install guide.",
490
+ relevanceExplanation: "This README is the resolved documentation file that contains the install instructions.",
491
+ role: "primary",
492
+ excerpts: [{
493
+ description: "Install section.",
494
+ startLine: 1,
495
+ endLine: 3,
496
+ symbols: ["README"],
497
+ code: "# Install\n\nRun `npm install -g scai-cli`.",
498
+ }],
499
+ risks: [],
500
+ }),
501
+ })
502
+ .mockResolvedValueOnce({
503
+ data: JSON.stringify({
504
+ action: "final-answer",
505
+ args: {},
506
+ reason: "All explicit targets are grounded now.",
507
+ }),
508
+ })
509
+ .mockResolvedValueOnce({
510
+ data: "The architecture flows from sample.ts into factory.ts and then runQueryWithDaemonControl.ts.",
511
+ });
512
+ const memory = createAgentMemory("Summarize CLI architecture", tempDir, tempDir);
513
+ const ui = {
514
+ update: vi.fn(),
515
+ message: vi.fn(),
516
+ pause: vi.fn((fn) => fn()),
517
+ succeed: vi.fn(),
518
+ fail: vi.fn(),
519
+ stop: vi.fn(),
520
+ };
521
+ await runAgentLoop(memory, ui);
522
+ expect(memory.task.status).toBe("done");
523
+ expect(memory.steps.map((step) => step.action)).toEqual([
524
+ "read-file",
525
+ "read-file",
526
+ "read-file",
527
+ "final-answer",
528
+ ]);
529
+ expect(Object.keys(memory.evidence.fileReads)).toEqual(expect.arrayContaining([targetFile, secondFile, thirdFile]));
530
+ });
531
+ it("auto-resolves the scoped duplicate filename before reading when sibling explicit targets make the scope clear", async () => {
532
+ const cliIndex = path.join(tempDir, "cli", "src", "index.ts");
533
+ const webIndex = path.join(tempDir, "web", "src", "index.ts");
534
+ const factoryFile = path.join(tempDir, "cli", "src", "commands", "factory.ts");
535
+ const daemonFile = path.join(tempDir, "cli", "src", "runQueryWithDaemonControl.ts");
536
+ fs.mkdirSync(path.dirname(cliIndex), { recursive: true });
537
+ fs.mkdirSync(path.dirname(webIndex), { recursive: true });
538
+ fs.mkdirSync(path.dirname(factoryFile), { recursive: true });
539
+ fs.writeFileSync(cliIndex, "export const cli = true;\n", "utf-8");
540
+ fs.writeFileSync(webIndex, "export const web = true;\n", "utf-8");
541
+ fs.writeFileSync(factoryFile, "export function createFactory() {}\n", "utf-8");
542
+ fs.writeFileSync(daemonFile, "export function runQueryWithDaemonControl() {}\n", "utf-8");
543
+ generateMock
544
+ .mockResolvedValueOnce({
545
+ data: JSON.stringify({
546
+ normalizedQuery: "Summarize CLI architecture from index.ts, commands/factory.ts, and runQueryWithDaemonControl.ts",
547
+ explicitTargets: ["index.ts", "commands/factory.ts", "runQueryWithDaemonControl.ts"],
548
+ constraints: [],
549
+ primaryIntent: "Summarize the CLI architecture from the requested files.",
550
+ alternativeIntents: [],
551
+ ambiguityFlags: [],
552
+ }),
553
+ })
554
+ .mockResolvedValueOnce({
555
+ data: JSON.stringify({
556
+ action: "search-db",
557
+ args: { query: "Summarize CLI architecture from index.ts, commands/factory.ts, and runQueryWithDaemonControl.ts" },
558
+ reason: "Resolve the explicit targets first.",
559
+ }),
560
+ })
561
+ .mockResolvedValueOnce({
562
+ data: JSON.stringify({
563
+ action: "final-answer",
564
+ args: {},
565
+ reason: "Try to answer immediately.",
566
+ }),
567
+ })
568
+ .mockResolvedValueOnce({
569
+ data: JSON.stringify({
570
+ summary: "CLI index entrypoint.",
571
+ relevanceExplanation: "This is the scoped CLI index target selected from the duplicate basename candidates.",
572
+ role: "primary",
573
+ excerpts: [{
574
+ description: "CLI export.",
575
+ startLine: 1,
576
+ endLine: 1,
577
+ symbols: ["cli"],
578
+ code: "export const cli = true;",
579
+ }],
580
+ risks: [],
581
+ }),
582
+ })
583
+ .mockResolvedValueOnce({
584
+ data: JSON.stringify({
585
+ action: "final-answer",
586
+ args: {},
587
+ reason: "Try to answer again.",
588
+ }),
589
+ })
590
+ .mockResolvedValueOnce({
591
+ data: JSON.stringify({
592
+ summary: "Factory file.",
593
+ relevanceExplanation: "This file contributes the command factory step in the requested architecture summary.",
594
+ role: "supporting",
595
+ excerpts: [{
596
+ description: "Factory export.",
597
+ startLine: 1,
598
+ endLine: 1,
599
+ symbols: ["createFactory"],
600
+ code: "export function createFactory() {}",
601
+ }],
602
+ risks: [],
603
+ }),
604
+ })
605
+ .mockResolvedValueOnce({
606
+ data: JSON.stringify({
607
+ action: "final-answer",
608
+ args: {},
609
+ reason: "Try to answer a third time.",
610
+ }),
611
+ })
612
+ .mockResolvedValueOnce({
613
+ data: JSON.stringify({
614
+ summary: "Daemon control file.",
615
+ relevanceExplanation: "This file completes the daemon-controlled query path the user asked about.",
616
+ role: "supporting",
617
+ excerpts: [{
618
+ description: "Daemon control export.",
619
+ startLine: 1,
620
+ endLine: 1,
621
+ symbols: ["runQueryWithDaemonControl"],
622
+ code: "export function runQueryWithDaemonControl() {}",
623
+ }],
624
+ risks: [],
625
+ }),
626
+ })
627
+ .mockResolvedValueOnce({
628
+ data: JSON.stringify({
629
+ action: "final-answer",
630
+ args: {},
631
+ reason: "All requested files are grounded now.",
632
+ }),
633
+ })
634
+ .mockResolvedValueOnce({
635
+ data: "The CLI architecture flows from cli/src/index.ts into commands/factory.ts and then runQueryWithDaemonControl.ts.",
636
+ });
637
+ const memory = createAgentMemory("Summarize CLI architecture from index.ts, commands/factory.ts, and runQueryWithDaemonControl.ts", tempDir, tempDir);
638
+ const ui = {
639
+ update: vi.fn(),
640
+ message: vi.fn(),
641
+ pause: vi.fn((fn) => fn()),
642
+ succeed: vi.fn(),
643
+ fail: vi.fn(),
644
+ stop: vi.fn(),
645
+ };
646
+ await runAgentLoop(memory, ui);
647
+ expect(memory.task.status).toBe("done");
648
+ expect(memory.task.dialogState).toBeUndefined();
649
+ expect(memory.steps.map((step) => step.action)).toEqual([
650
+ "search-db",
651
+ "read-file",
652
+ "read-file",
653
+ "read-file",
654
+ "final-answer",
655
+ ]);
656
+ expect(Object.keys(memory.evidence.fileReads)).toEqual(expect.arrayContaining([cliIndex, factoryFile, daemonFile]));
657
+ expect(Object.keys(memory.evidence.fileReads)).not.toContain(webIndex);
658
+ });
659
+ it("waits for user input immediately when a duplicate filename stays ambiguous", async () => {
660
+ const cliIndex = path.join(tempDir, "cli", "src", "index.ts");
661
+ const webIndex = path.join(tempDir, "web", "src", "index.ts");
662
+ fs.mkdirSync(path.dirname(cliIndex), { recursive: true });
663
+ fs.mkdirSync(path.dirname(webIndex), { recursive: true });
664
+ fs.writeFileSync(cliIndex, "export const cli = true;\n", "utf-8");
665
+ fs.writeFileSync(webIndex, "export const web = true;\n", "utf-8");
666
+ generateMock
667
+ .mockResolvedValueOnce({
668
+ data: JSON.stringify({
669
+ normalizedQuery: "Summarize architecture from index.ts",
670
+ explicitTargets: ["index.ts"],
671
+ constraints: [],
672
+ primaryIntent: "Summarize the requested file.",
673
+ alternativeIntents: [],
674
+ ambiguityFlags: [],
675
+ }),
676
+ })
677
+ .mockResolvedValueOnce({
678
+ data: JSON.stringify({
679
+ action: "search-db",
680
+ args: { query: "Summarize architecture from index.ts" },
681
+ reason: "Resolve the explicit target first.",
682
+ }),
683
+ });
684
+ const memory = createAgentMemory("Summarize architecture from index.ts", tempDir, tempDir);
685
+ const ui = {
686
+ update: vi.fn(),
687
+ message: vi.fn(),
688
+ pause: vi.fn((fn) => fn()),
689
+ succeed: vi.fn(),
690
+ fail: vi.fn(),
691
+ stop: vi.fn(),
692
+ };
693
+ await runAgentLoop(memory, ui);
694
+ expect(memory.task.status).toBe("waiting");
695
+ expect(memory.steps.map((step) => step.action)).toEqual([
696
+ "search-db",
697
+ "request-user-input",
698
+ ]);
699
+ expect(memory.task.dialogState).toMatchObject({
700
+ status: "awaiting-user",
701
+ target: "index.ts",
702
+ options: [cliIndex, webIndex],
703
+ });
704
+ expect(ui.message).toHaveBeenCalledWith("Requesting user input.");
705
+ });
706
+ it("reads a resolved README candidate before answering doc-style install questions", async () => {
707
+ const readmeFile = path.join(tempDir, "README.md");
708
+ fs.writeFileSync(readmeFile, "# Install\n\nRun `npm install -g scai-cli`.\n", "utf-8");
709
+ searchState.dbFileCandidates.set("please check the readme for how to install scai cli?", [readmeFile]);
710
+ generateMock
711
+ .mockResolvedValueOnce({
712
+ data: JSON.stringify({
713
+ normalizedQuery: "please check the readme for how to install scai cli?",
714
+ explicitTargets: ["readme"],
715
+ constraints: [],
716
+ primaryIntent: "Explain the install instructions from the docs.",
717
+ alternativeIntents: [],
718
+ ambiguityFlags: [],
719
+ }),
720
+ })
721
+ .mockResolvedValueOnce({
722
+ data: JSON.stringify({
723
+ action: "search-db",
724
+ args: { query: "please check the readme for how to install scai cli?" },
725
+ reason: "Find the likely documentation file first.",
726
+ }),
727
+ })
728
+ .mockResolvedValueOnce({
729
+ data: JSON.stringify({
730
+ action: "read-file",
731
+ args: { target: "readme" },
732
+ reason: "Read the resolved README before answering.",
733
+ }),
734
+ })
735
+ .mockResolvedValueOnce({
736
+ data: JSON.stringify({
737
+ summary: "README completeness audit target.",
738
+ relevanceExplanation: "This README is the explicit file the user asked to audit for missing sections.",
739
+ role: "primary",
740
+ excerpts: [{
741
+ description: "Install and usage sections.",
742
+ startLine: 1,
743
+ endLine: 9,
744
+ symbols: ["README"],
745
+ code: "# CLI\n\n## Install\n\nRun `npm install -g scai-cli`.\n\n## Usage\n\nUse `scai ask` to query the repo.",
746
+ }],
747
+ risks: [],
748
+ }),
749
+ })
750
+ .mockResolvedValueOnce({
751
+ data: JSON.stringify({
752
+ action: "final-answer",
753
+ args: {},
754
+ reason: "The grounded README read is enough to answer.",
755
+ }),
756
+ })
757
+ .mockResolvedValueOnce({
758
+ data: "The install instructions are in README.md.",
759
+ });
760
+ const memory = createAgentMemory("please check the readme for how to install scai cli?", tempDir, tempDir);
761
+ const ui = {
762
+ update: vi.fn(),
763
+ message: vi.fn(),
764
+ pause: vi.fn((fn) => fn()),
765
+ succeed: vi.fn(),
766
+ fail: vi.fn(),
767
+ stop: vi.fn(),
768
+ };
769
+ await runAgentLoop(memory, ui);
770
+ expect(memory.task.status).toBe("done");
771
+ expect(Object.keys(memory.evidence.fileReads)).toContain(readmeFile);
772
+ expect(memory.steps.map((step) => step.action)).toEqual(["search-db", "read-file", "final-answer"]);
773
+ });
774
+ it("uses one read-file step for README completeness questions and avoids rereading the same file", async () => {
775
+ const readmeFile = path.join(tempDir, "README.md");
776
+ fs.writeFileSync(readmeFile, [
777
+ "# CLI",
778
+ "",
779
+ "## Install",
780
+ "",
781
+ "Run `npm install -g scai-cli`.",
782
+ "",
783
+ "## Usage",
784
+ "",
785
+ "Use `scai ask` to query the repo.",
786
+ ].join("\n"), "utf-8");
787
+ generateMock
788
+ .mockResolvedValueOnce({
789
+ data: JSON.stringify({
790
+ normalizedQuery: "is anything missing from the readme?",
791
+ explicitTargets: [readmeFile],
792
+ constraints: [],
793
+ primaryIntent: "Audit the README for missing information.",
794
+ alternativeIntents: [],
795
+ ambiguityFlags: [],
796
+ }),
797
+ })
798
+ .mockResolvedValueOnce({
799
+ data: JSON.stringify({
800
+ action: "read-file",
801
+ args: { target: readmeFile },
802
+ reason: "Read the README first.",
803
+ }),
804
+ })
805
+ .mockResolvedValueOnce({
806
+ data: JSON.stringify({
807
+ summary: "Matching scai README.",
808
+ relevanceExplanation: "The resolved README directly matches the search request and contains the scai docs.",
809
+ role: "primary",
810
+ excerpts: [{
811
+ description: "README body.",
812
+ startLine: 1,
813
+ endLine: 2,
814
+ code: "# Scai\n\nThis README explains how to find and use the scai project docs.",
815
+ }],
816
+ risks: [],
817
+ }),
818
+ })
819
+ .mockResolvedValueOnce({
820
+ data: JSON.stringify({
821
+ action: "final-answer",
822
+ args: {},
823
+ reason: "The grounded README read is enough to answer.",
824
+ }),
825
+ })
826
+ .mockResolvedValueOnce({
827
+ data: "The README covers install and usage, but I do not see sections for configuration or contribution guidance.",
828
+ });
829
+ const memory = createAgentMemory("is anything missing from the readme?", tempDir, tempDir);
830
+ const ui = {
831
+ update: vi.fn(),
832
+ message: vi.fn(),
833
+ pause: vi.fn((fn) => fn()),
834
+ succeed: vi.fn(),
835
+ fail: vi.fn(),
836
+ stop: vi.fn(),
837
+ };
838
+ await runAgentLoop(memory, ui);
839
+ expect(memory.task.status).toBe("done");
840
+ expect(memory.steps.map((step) => step.action)).toEqual(["read-file", "final-answer"]);
841
+ expect(memory.evidence.fileReads[readmeFile]).toMatchObject({
842
+ filePath: readmeFile,
843
+ relevantContext: expect.arrayContaining([
844
+ expect.objectContaining({ kind: "purpose" }),
845
+ ]),
846
+ });
847
+ expect(memory.artifacts.finalAnswer).toContain("configuration");
848
+ });
849
+ it("normalizes search-db pattern args into query before validation", async () => {
850
+ const readmeFile = path.join(tempDir, "README.md");
851
+ fs.writeFileSync(readmeFile, "# Scai\n\nThis README explains how to find and use the scai project docs.\n", "utf-8");
852
+ searchState.dbFileCandidates.set("scai", [readmeFile]);
853
+ generateMock
854
+ .mockResolvedValueOnce({
855
+ data: JSON.stringify({
856
+ normalizedQuery: "find readme files for scai",
857
+ explicitTargets: ["readme"],
858
+ constraints: [],
859
+ primaryIntent: "Locate readme files for scai.",
860
+ alternativeIntents: [],
861
+ ambiguityFlags: [],
862
+ }),
863
+ })
864
+ .mockResolvedValueOnce({
865
+ data: JSON.stringify({
866
+ action: "search-db",
867
+ args: { pattern: "scai" },
868
+ reason: "Search for likely scai docs first.",
869
+ }),
870
+ })
871
+ .mockResolvedValueOnce({
872
+ data: JSON.stringify({
873
+ action: "read-file",
874
+ args: { target: "readme" },
875
+ reason: "Read the matching readme.",
876
+ }),
877
+ })
878
+ .mockResolvedValueOnce({
879
+ data: JSON.stringify({
880
+ action: "final-answer",
881
+ args: {},
882
+ reason: "The grounded readme is enough to answer.",
883
+ }),
884
+ })
885
+ .mockResolvedValueOnce({
886
+ data: "README.md matches the scai search.",
887
+ });
888
+ const memory = createAgentMemory("find readme files for scai", tempDir, tempDir);
889
+ const ui = {
890
+ update: vi.fn(),
891
+ message: vi.fn(),
892
+ pause: vi.fn((fn) => fn()),
893
+ succeed: vi.fn(),
894
+ fail: vi.fn(),
895
+ stop: vi.fn(),
896
+ };
897
+ await runAgentLoop(memory, ui);
898
+ expect(memory.events.some((event) => event.type === "validation-failed")).toBe(false);
899
+ expect(memory.evidence.searches[0]).toMatchObject({
900
+ action: "search-db",
901
+ query: "find readme files for scai",
902
+ });
903
+ expect(Object.keys(memory.evidence.fileReads)).toContain(readmeFile);
904
+ expect(memory.task.status).toBe("done");
905
+ });
906
+ it("keeps new-agent timing out of console milestones when dev output is off", async () => {
907
+ generateMock
908
+ .mockResolvedValueOnce({
909
+ trace: {
910
+ callId: "llm-0001-agent-understand",
911
+ startedAt: "2026-05-01T06:16:21.000+02:00",
912
+ finishedAt: "2026-05-01T06:16:23.000+02:00",
913
+ durationMs: 2000,
914
+ attemptCount: 1,
915
+ },
916
+ data: JSON.stringify({
917
+ normalizedQuery: `Explain ${targetFile}`,
918
+ explicitTargets: [targetFile],
919
+ constraints: [],
920
+ primaryIntent: "Explain the target file.",
921
+ alternativeIntents: [],
922
+ ambiguityFlags: [],
923
+ }),
924
+ })
925
+ .mockResolvedValueOnce({
926
+ trace: {
927
+ callId: "llm-0002-agent-action-choice",
928
+ startedAt: "2026-05-01T06:16:24.000+02:00",
929
+ finishedAt: "2026-05-01T06:16:25.000+02:00",
930
+ durationMs: 1000,
931
+ attemptCount: 1,
932
+ },
933
+ data: JSON.stringify({
934
+ action: "read-file",
935
+ args: { target: targetFile },
936
+ reason: "Read the explicit file first.",
937
+ }),
938
+ })
939
+ .mockResolvedValueOnce({
940
+ trace: {
941
+ callId: "llm-0003-agent-read-file",
942
+ startedAt: "2026-05-01T06:16:25.100+02:00",
943
+ finishedAt: "2026-05-01T06:16:25.400+02:00",
944
+ durationMs: 300,
945
+ attemptCount: 1,
946
+ },
947
+ data: JSON.stringify({
948
+ summary: "Sample file for the new agent loop test.",
949
+ relevanceExplanation: "This file defines sampleAnswer and the exported sample contracts used by the query.",
950
+ role: "primary",
951
+ excerpts: [{
952
+ description: "The main answer function.",
953
+ startLine: 8,
954
+ endLine: 10,
955
+ symbols: ["sampleAnswer"],
956
+ code: "export function sampleAnswer() {\n return 'answer';\n}",
957
+ }],
958
+ risks: [],
959
+ }),
960
+ })
961
+ .mockResolvedValueOnce({
962
+ trace: {
963
+ callId: "llm-0004-agent-action-choice",
964
+ startedAt: "2026-05-01T06:16:25.500+02:00",
965
+ finishedAt: "2026-05-01T06:16:26.000+02:00",
966
+ durationMs: 500,
967
+ attemptCount: 1,
968
+ },
969
+ data: JSON.stringify({
970
+ action: "final-answer",
971
+ args: {},
972
+ reason: "The file read is enough to answer now.",
973
+ }),
974
+ })
975
+ .mockResolvedValueOnce({
976
+ trace: {
977
+ callId: "llm-0005-agent-final-answer",
978
+ startedAt: "2026-05-01T06:16:26.000+02:00",
979
+ finishedAt: "2026-05-01T06:16:29.000+02:00",
980
+ durationMs: 3000,
981
+ attemptCount: 1,
982
+ },
983
+ data: "The file defines sampleAnswer in sample.ts.",
984
+ });
985
+ const memory = createAgentMemory(`Explain ${targetFile}`, tempDir, tempDir);
986
+ const ui = {
987
+ update: vi.fn(),
988
+ message: vi.fn(),
989
+ pause: vi.fn((fn) => fn()),
990
+ succeed: vi.fn(),
991
+ fail: vi.fn(),
992
+ stop: vi.fn(),
993
+ };
994
+ await runAgentLoop(memory, ui);
995
+ expect(ui.message).not.toHaveBeenCalledWith("Understanding request (2s).");
996
+ expect(ui.message).not.toHaveBeenCalledWith("Choosing next action (1s).");
997
+ expect(ui.message).not.toHaveBeenCalledWith("Producing final answer (3s).");
998
+ });
999
+ it("requests one focused user input when no grounded evidence exists and scope stays broad", async () => {
1000
+ generateMock
1001
+ .mockResolvedValueOnce({
1002
+ trace: {
1003
+ callId: "llm-0001-agent-understand",
1004
+ startedAt: "2026-05-01T06:16:21.000+02:00",
1005
+ finishedAt: "2026-05-01T06:16:23.000+02:00",
1006
+ durationMs: 2000,
1007
+ attemptCount: 1,
1008
+ },
1009
+ data: JSON.stringify({
1010
+ normalizedQuery: "Explain how missing evidence flow works",
1011
+ queryScope: "broad-repo",
1012
+ explicitTargets: [],
1013
+ constraints: [],
1014
+ primaryIntent: "Explain how missing evidence flow works.",
1015
+ alternativeIntents: [],
1016
+ ambiguityFlags: [],
1017
+ }),
1018
+ })
1019
+ .mockResolvedValueOnce({
1020
+ trace: {
1021
+ callId: "llm-0002-agent-action-choice",
1022
+ startedAt: "2026-05-01T06:16:24.000+02:00",
1023
+ finishedAt: "2026-05-01T06:16:25.000+02:00",
1024
+ durationMs: 1000,
1025
+ attemptCount: 1,
1026
+ },
1027
+ data: JSON.stringify({
1028
+ action: "plan",
1029
+ args: {},
1030
+ reason: "Plan the next step.",
1031
+ }),
1032
+ })
1033
+ .mockResolvedValueOnce({
1034
+ trace: {
1035
+ callId: "llm-0003-agent-plan",
1036
+ startedAt: "2026-05-01T06:16:26.000+02:00",
1037
+ finishedAt: "2026-05-01T06:16:28.000+02:00",
1038
+ durationMs: 2000,
1039
+ attemptCount: 1,
1040
+ },
1041
+ data: JSON.stringify({
1042
+ goal: "Explain missing evidence flow",
1043
+ why: "Need one step first.",
1044
+ steps: [
1045
+ {
1046
+ action: "search-db",
1047
+ args: { query: "missing evidence flow" },
1048
+ purpose: "Try indexed search.",
1049
+ expectedEvidence: ["candidate files"],
1050
+ },
1051
+ ],
1052
+ }),
1053
+ });
1054
+ const memory = createAgentMemory("Explain how missing evidence flow works", tempDir, tempDir);
1055
+ memory.task.maxTurns = 2;
1056
+ const ui = {
1057
+ update: vi.fn(),
1058
+ message: vi.fn(),
1059
+ pause: vi.fn((fn) => fn()),
1060
+ succeed: vi.fn(),
1061
+ fail: vi.fn(),
1062
+ stop: vi.fn(),
1063
+ };
1064
+ await runAgentLoop(memory, ui);
1065
+ const waitingStep = memory.steps.find((step) => step.action === "request-user-input");
1066
+ expect(memory.task.status).toBe("waiting");
1067
+ expect(memory.artifacts.stopMessage).toContain("I could not answer yet.");
1068
+ expect(memory.artifacts.stopMessage).toContain("I need one thing from you:");
1069
+ expect(memory.task.dialogState).toMatchObject({
1070
+ status: "awaiting-user",
1071
+ kind: "clarification",
1072
+ });
1073
+ expect(waitingStep).toMatchObject({
1074
+ status: "done",
1075
+ resultSummary: "Requested one focused user input.",
1076
+ });
1077
+ expect(waitingStep?.startedAt).toBeTruthy();
1078
+ expect(waitingStep?.completedAt).toBeTruthy();
1079
+ expect(ui.message).toHaveBeenCalledWith("Requesting user input.");
1080
+ });
1081
+ it("normalizes loose regex-search args before validation", async () => {
1082
+ generateMock
1083
+ .mockResolvedValueOnce({
1084
+ data: JSON.stringify({
1085
+ normalizedQuery: "Find action names with regex",
1086
+ explicitTargets: [],
1087
+ constraints: [],
1088
+ primaryIntent: "Find action names with regex.",
1089
+ alternativeIntents: [],
1090
+ ambiguityFlags: [],
1091
+ }),
1092
+ })
1093
+ .mockResolvedValueOnce({
1094
+ data: JSON.stringify({
1095
+ action: "search-regex",
1096
+ args: {
1097
+ pattern: "plan|search-db|search-regex",
1098
+ directory: ".",
1099
+ },
1100
+ reason: "Search action names by regex.",
1101
+ }),
1102
+ })
1103
+ .mockResolvedValueOnce({
1104
+ data: "I searched file content for the requested regex pattern.",
1105
+ });
1106
+ const memory = createAgentMemory("Find action names with regex", tempDir, tempDir);
1107
+ memory.task.maxTurns = 1;
1108
+ const ui = {
1109
+ update: vi.fn(),
1110
+ message: vi.fn(),
1111
+ pause: vi.fn((fn) => fn()),
1112
+ succeed: vi.fn(),
1113
+ fail: vi.fn(),
1114
+ stop: vi.fn(),
1115
+ };
1116
+ await runAgentLoop(memory, ui);
1117
+ expect(memory.events.map((event) => event.type)).not.toContain("validation-failed");
1118
+ expect(memory.evidence.searches.some((search) => search.action === "search-regex")).toBe(true);
1119
+ expect(memory.task.status).toBe("blocked");
1120
+ expect(memory.artifacts.stopMessage).toContain("I could not answer from grounded repo evidence.");
1121
+ });
1122
+ it("reports cannot-answer when explicit targets still do not produce grounded evidence", async () => {
1123
+ generateMock
1124
+ .mockResolvedValueOnce({
1125
+ data: JSON.stringify({
1126
+ normalizedQuery: "Explain how /etc/hosts is handled",
1127
+ explicitTargets: ["/etc/hosts"],
1128
+ constraints: [],
1129
+ primaryIntent: "Explain how the target file is handled.",
1130
+ alternativeIntents: [],
1131
+ ambiguityFlags: [],
1132
+ }),
1133
+ })
1134
+ .mockResolvedValueOnce({
1135
+ data: JSON.stringify({
1136
+ action: "plan",
1137
+ args: {},
1138
+ reason: "The explicit path is outside the repo, so plan a safer stop.",
1139
+ }),
1140
+ })
1141
+ .mockResolvedValueOnce({
1142
+ data: JSON.stringify({
1143
+ goal: "Find a safe next step.",
1144
+ why: "The direct file read failed.",
1145
+ steps: [],
1146
+ }),
1147
+ });
1148
+ const memory = createAgentMemory("Explain how /etc/hosts is handled", tempDir, tempDir);
1149
+ memory.task.maxTurns = 1;
1150
+ const ui = {
1151
+ update: vi.fn(),
1152
+ message: vi.fn(),
1153
+ pause: vi.fn((fn) => fn()),
1154
+ succeed: vi.fn(),
1155
+ fail: vi.fn(),
1156
+ stop: vi.fn(),
1157
+ };
1158
+ await runAgentLoop(memory, ui);
1159
+ const stopStep = memory.steps.find((step) => step.action === "cannot-answer");
1160
+ expect(memory.task.status).toBe("blocked");
1161
+ expect(memory.artifacts.stopMessage).toContain("I could not answer from grounded repo evidence.");
1162
+ expect(stopStep).toMatchObject({
1163
+ status: "done",
1164
+ resultSummary: "Reported that the agent could not answer.",
1165
+ });
1166
+ expect(Object.keys(memory.evidence.fileReads)).toHaveLength(0);
1167
+ });
1168
+ it("logs validation failures without pretending the step started", async () => {
1169
+ generateMock
1170
+ .mockResolvedValueOnce({
1171
+ data: JSON.stringify({
1172
+ normalizedQuery: "Run a regex search for test files",
1173
+ queryScope: "targeted",
1174
+ explicitTargets: [],
1175
+ constraints: [],
1176
+ primaryIntent: "Run a regex search safely.",
1177
+ alternativeIntents: [],
1178
+ ambiguityFlags: [],
1179
+ }),
1180
+ })
1181
+ .mockResolvedValueOnce({
1182
+ data: JSON.stringify({
1183
+ action: "search-regex",
1184
+ args: {},
1185
+ reason: "Try a regex search first.",
1186
+ }),
1187
+ })
1188
+ .mockResolvedValueOnce({
1189
+ data: JSON.stringify({
1190
+ goal: "Find a safer step.",
1191
+ why: "The first step was invalid.",
1192
+ steps: [],
1193
+ }),
1194
+ })
1195
+ .mockResolvedValueOnce({
1196
+ data: "I could not complete the regex search because the step arguments were invalid.",
1197
+ });
1198
+ const memory = createAgentMemory("Run a regex search for test files", tempDir, tempDir);
1199
+ memory.task.maxTurns = 2;
1200
+ const ui = {
1201
+ update: vi.fn(),
1202
+ message: vi.fn(),
1203
+ pause: vi.fn((fn) => fn()),
1204
+ succeed: vi.fn(),
1205
+ fail: vi.fn(),
1206
+ stop: vi.fn(),
1207
+ };
1208
+ await runAgentLoop(memory, ui);
1209
+ const blockedStep = memory.steps.find((step) => step.action === "search-regex");
1210
+ expect(memory.events.map((event) => event.type)).toContain("validation-failed");
1211
+ expect(blockedStep?.status).toBe("blocked");
1212
+ expect(blockedStep?.startedAt).toBeUndefined();
1213
+ expect(memory.events.some((event) => event.type === "step-started" && event.stepId === blockedStep?.id)).toBe(false);
1214
+ expect(ui.update).toHaveBeenCalledWith("Planning next steps (turn 1/2)");
1215
+ expect(ui.message).toHaveBeenCalledWith("Planning next steps.");
1216
+ });
1217
+ it("marks a running step failed and finalizes once when the end-of-run answer throws", async () => {
1218
+ generateMock
1219
+ .mockResolvedValueOnce({
1220
+ data: JSON.stringify({
1221
+ normalizedQuery: `Explain ${targetFile}`,
1222
+ explicitTargets: [targetFile],
1223
+ constraints: [],
1224
+ primaryIntent: "Explain the target file.",
1225
+ alternativeIntents: [],
1226
+ ambiguityFlags: [],
1227
+ }),
1228
+ })
1229
+ .mockResolvedValueOnce({
1230
+ data: JSON.stringify({
1231
+ action: "read-file",
1232
+ args: { target: targetFile },
1233
+ reason: "Read the explicit file first.",
1234
+ }),
1235
+ })
1236
+ .mockResolvedValueOnce({
1237
+ data: JSON.stringify({
1238
+ summary: "Sample file for the explicit-target explanation.",
1239
+ relevanceExplanation: "This explicit file is the user-requested answer source.",
1240
+ role: "primary",
1241
+ excerpts: [{
1242
+ description: "Main export block.",
1243
+ startLine: 8,
1244
+ endLine: 10,
1245
+ symbols: ["sampleAnswer"],
1246
+ code: "export function sampleAnswer() {\n return 'answer';\n}",
1247
+ }],
1248
+ risks: [],
1249
+ }),
1250
+ })
1251
+ .mockResolvedValueOnce({
1252
+ data: JSON.stringify({
1253
+ action: "final-answer",
1254
+ args: {},
1255
+ reason: "The file read is enough to answer now.",
1256
+ }),
1257
+ })
1258
+ .mockRejectedValueOnce(Object.assign(new Error("final answer model failure"), {
1259
+ trace: {
1260
+ callId: "llm-0004-agent-final-answer",
1261
+ },
1262
+ }));
1263
+ const memory = createAgentMemory(`Explain ${targetFile}`, tempDir, tempDir);
1264
+ const ui = {
1265
+ update: vi.fn(),
1266
+ message: vi.fn(),
1267
+ pause: vi.fn((fn) => fn()),
1268
+ succeed: vi.fn(),
1269
+ fail: vi.fn(),
1270
+ stop: vi.fn(),
1271
+ };
1272
+ await expect(runAgentLoop(memory, ui)).rejects.toThrow("final answer model failure");
1273
+ const finalStep = memory.steps.find((step) => step.action === "final-answer");
1274
+ const finalizedEvents = memory.events.filter((event) => event.type === "task-finalized");
1275
+ expect(finalStep).toMatchObject({
1276
+ status: "failed",
1277
+ resultSummary: "final answer model failure",
1278
+ });
1279
+ expect(memory.task.status).toBe("failed");
1280
+ expect(memory.task.stopReason).toBe("final-answer");
1281
+ expect(finalizedEvents).toHaveLength(1);
1282
+ });
1283
+ it("uses search fallback results for /resume-style queries instead of ending empty", async () => {
1284
+ const resumeFile = path.join(tempDir, "cli", "src", "commands", "TasksCmd.ts");
1285
+ fs.mkdirSync(path.dirname(resumeFile), { recursive: true });
1286
+ fs.writeFileSync(resumeFile, [
1287
+ "/**",
1288
+ " * Handles task shell commands, including resume.",
1289
+ " */",
1290
+ "export function runResumeCommand() {",
1291
+ " return 'resume';",
1292
+ "}",
1293
+ ].join("\n"), "utf-8");
1294
+ searchState.dbFileCandidates.set("How does /resume for the simple agent work?", [resumeFile]);
1295
+ generateMock
1296
+ .mockResolvedValueOnce({
1297
+ data: JSON.stringify({
1298
+ normalizedQuery: "How does /resume for the simple agent work?",
1299
+ explicitTargets: ["/resume"],
1300
+ constraints: [],
1301
+ primaryIntent: "Explain the resume command flow.",
1302
+ alternativeIntents: [],
1303
+ ambiguityFlags: [],
1304
+ }),
1305
+ })
1306
+ .mockResolvedValueOnce({
1307
+ data: JSON.stringify({
1308
+ action: "search-db",
1309
+ args: { query: "How does /resume for the simple agent work?" },
1310
+ reason: "Search for the command implementation first.",
1311
+ }),
1312
+ })
1313
+ .mockResolvedValueOnce({
1314
+ data: JSON.stringify({
1315
+ action: "read-file",
1316
+ args: { target: resumeFile },
1317
+ reason: "Read the best fallback candidate before answering.",
1318
+ }),
1319
+ })
1320
+ .mockResolvedValueOnce({
1321
+ data: JSON.stringify({
1322
+ summary: "Resume command implementation.",
1323
+ relevanceExplanation: "This fallback candidate implements the resume command flow the user asked about.",
1324
+ role: "primary",
1325
+ excerpts: [{
1326
+ description: "Resume command function.",
1327
+ startLine: 4,
1328
+ endLine: 6,
1329
+ symbols: ["runResumeCommand"],
1330
+ code: "export function runResumeCommand() {\n return 'resume';\n}",
1331
+ }],
1332
+ risks: [],
1333
+ }),
1334
+ })
1335
+ .mockResolvedValueOnce({
1336
+ data: JSON.stringify({
1337
+ action: "final-answer",
1338
+ args: {},
1339
+ reason: "The candidate read is enough to answer now.",
1340
+ }),
1341
+ })
1342
+ .mockResolvedValueOnce({
1343
+ data: "The resume flow is handled in TasksCmd.ts.",
1344
+ });
1345
+ const memory = createAgentMemory("How does /resume for the simple agent work?", tempDir, tempDir);
1346
+ const ui = {
1347
+ update: vi.fn(),
1348
+ message: vi.fn(),
1349
+ pause: vi.fn((fn) => fn()),
1350
+ succeed: vi.fn(),
1351
+ fail: vi.fn(),
1352
+ stop: vi.fn(),
1353
+ };
1354
+ await runAgentLoop(memory, ui);
1355
+ expect(memory.task.status).toBe("done");
1356
+ expect(memory.evidence.candidateFiles).toContain(resumeFile);
1357
+ expect(Object.keys(memory.evidence.fileReads).length).toBeGreaterThan(0);
1358
+ expect(memory.artifacts.finalAnswer).toContain("TasksCmd.ts");
1359
+ expect(memory.steps.some((step) => step.action === "cannot-answer")).toBe(false);
1360
+ });
1361
+ it("searches before reading when the explicit target is only a basename", async () => {
1362
+ const indexFile = path.join(tempDir, "cli", "src", "index.ts");
1363
+ fs.mkdirSync(path.dirname(indexFile), { recursive: true });
1364
+ fs.writeFileSync(indexFile, [
1365
+ "/**",
1366
+ " * CLI entrypoint for the basename resolution test.",
1367
+ " */",
1368
+ "export function runCliEntry() {",
1369
+ " return 'cli';",
1370
+ "}",
1371
+ ].join("\n"), "utf-8");
1372
+ searchState.dbFileCandidates.set("Explain index.ts", [indexFile]);
1373
+ generateMock
1374
+ .mockResolvedValueOnce({
1375
+ data: JSON.stringify({
1376
+ normalizedQuery: "Explain index.ts",
1377
+ explicitTargets: ["index.ts"],
1378
+ constraints: [],
1379
+ primaryIntent: "Explain the index file.",
1380
+ alternativeIntents: [],
1381
+ ambiguityFlags: [],
1382
+ }),
1383
+ })
1384
+ .mockResolvedValueOnce({
1385
+ data: JSON.stringify({
1386
+ action: "search-db",
1387
+ args: { query: "Explain index.ts" },
1388
+ reason: "Resolve the basename before reading.",
1389
+ }),
1390
+ })
1391
+ .mockResolvedValueOnce({
1392
+ data: JSON.stringify({
1393
+ action: "read-file",
1394
+ args: { target: "index.ts" },
1395
+ reason: "Read the resolved basename target.",
1396
+ }),
1397
+ })
1398
+ .mockResolvedValueOnce({
1399
+ data: JSON.stringify({
1400
+ summary: "Resolved basename target.",
1401
+ relevanceExplanation: "This file is the resolved index.ts target and contains the CLI entrypoint.",
1402
+ role: "primary",
1403
+ excerpts: [{
1404
+ description: "CLI entrypoint export.",
1405
+ startLine: 4,
1406
+ endLine: 6,
1407
+ symbols: ["runCliEntry"],
1408
+ code: "export function runCliEntry() {\n return 'cli';\n}",
1409
+ }],
1410
+ risks: [],
1411
+ }),
1412
+ })
1413
+ .mockResolvedValueOnce({
1414
+ data: JSON.stringify({
1415
+ action: "final-answer",
1416
+ args: {},
1417
+ reason: "The grounded read is enough to answer.",
1418
+ }),
1419
+ })
1420
+ .mockResolvedValueOnce({
1421
+ data: "The basename target resolves to cli/src/index.ts and defines runCliEntry.",
1422
+ });
1423
+ const memory = createAgentMemory("Explain index.ts", tempDir, tempDir);
1424
+ const ui = {
1425
+ update: vi.fn(),
1426
+ message: vi.fn(),
1427
+ pause: vi.fn((fn) => fn()),
1428
+ succeed: vi.fn(),
1429
+ fail: vi.fn(),
1430
+ stop: vi.fn(),
1431
+ };
1432
+ await runAgentLoop(memory, ui);
1433
+ expect(memory.task.status).toBe("done");
1434
+ expect(memory.evidence.candidateFiles).toContain(indexFile);
1435
+ expect(Object.keys(memory.evidence.fileReads)).toContain(indexFile);
1436
+ expect(memory.steps.filter((step) => step.action === "read-file" && step.status === "failed")).toHaveLength(0);
1437
+ expect(memory.artifacts.finalAnswer).toContain("cli/src/index.ts");
1438
+ });
1439
+ it("normalizes read-file path aliases from model output so /resume-style reads do not block", async () => {
1440
+ const resumeFile = path.join(tempDir, "cli", "src", "agents", "resumeContinuityStep.ts");
1441
+ fs.mkdirSync(path.dirname(resumeFile), { recursive: true });
1442
+ fs.writeFileSync(resumeFile, [
1443
+ "/**",
1444
+ " * Explains how resume continuity works.",
1445
+ " */",
1446
+ "export function resumeContinuityStep() {",
1447
+ " return 'resume continuity';",
1448
+ "}",
1449
+ ].join("\n"), "utf-8");
1450
+ searchState.dbFileCandidates.set("How does /resume for the simple agent work, and when is in-memory glob data reused?", [resumeFile]);
1451
+ generateMock
1452
+ .mockResolvedValueOnce({
1453
+ data: JSON.stringify({
1454
+ normalizedQuery: "How does /resume for the simple agent work, and when is in-memory glob data reused?",
1455
+ explicitTargets: ["/resume"],
1456
+ constraints: [],
1457
+ primaryIntent: "Explain the resume command flow and continuity reuse.",
1458
+ alternativeIntents: [],
1459
+ ambiguityFlags: [],
1460
+ }),
1461
+ })
1462
+ .mockResolvedValueOnce({
1463
+ data: JSON.stringify({
1464
+ action: "search-db",
1465
+ args: { query: "How does /resume for the simple agent work, and when is in-memory glob data reused?" },
1466
+ reason: "Find likely implementation files first.",
1467
+ }),
1468
+ })
1469
+ .mockResolvedValueOnce({
1470
+ data: JSON.stringify({
1471
+ action: "read-file",
1472
+ args: {
1473
+ path: resumeFile,
1474
+ target: "/resume",
1475
+ },
1476
+ reason: "Read the most relevant candidate file next.",
1477
+ }),
1478
+ })
1479
+ .mockResolvedValueOnce({
1480
+ data: JSON.stringify({
1481
+ summary: "Resume continuity implementation.",
1482
+ relevanceExplanation: "This candidate contains the resume continuity logic named in the question.",
1483
+ role: "primary",
1484
+ excerpts: [{
1485
+ description: "Resume continuity export.",
1486
+ startLine: 4,
1487
+ endLine: 6,
1488
+ symbols: ["resumeContinuityStep"],
1489
+ code: "export function resumeContinuityStep() {\n return 'resume continuity';\n}",
1490
+ }],
1491
+ risks: [],
1492
+ }),
1493
+ })
1494
+ .mockResolvedValueOnce({
1495
+ data: JSON.stringify({
1496
+ action: "final-answer",
1497
+ args: {},
1498
+ reason: "The file read is enough to answer now.",
1499
+ }),
1500
+ })
1501
+ .mockResolvedValueOnce({
1502
+ data: "The resume continuity flow is handled in resumeContinuityStep.ts.",
1503
+ });
1504
+ const memory = createAgentMemory("How does /resume for the simple agent work, and when is in-memory glob data reused?", tempDir, tempDir);
1505
+ const ui = {
1506
+ update: vi.fn(),
1507
+ message: vi.fn(),
1508
+ pause: vi.fn((fn) => fn()),
1509
+ succeed: vi.fn(),
1510
+ fail: vi.fn(),
1511
+ stop: vi.fn(),
1512
+ };
1513
+ await runAgentLoop(memory, ui);
1514
+ expect(memory.task.status).toBe("done");
1515
+ expect(Object.keys(memory.evidence.fileReads)).toContain(resumeFile);
1516
+ expect(memory.steps.find((step) => step.action === "read-file")).toMatchObject({
1517
+ status: "done",
1518
+ });
1519
+ expect(memory.events.some((event) => event.type === "validation-failed")).toBe(false);
1520
+ });
1521
+ it("keeps reading ranked candidates until the model has enough evidence", async () => {
1522
+ const loopFile = path.join(tempDir, "cli", "src", "agents", "agentLoop.ts");
1523
+ const actionsFile = path.join(tempDir, "cli", "src", "agents", "agentActions.ts");
1524
+ const stateFile = path.join(tempDir, "cli", "src", "agents", "agentStateMachine.ts");
1525
+ fs.mkdirSync(path.dirname(loopFile), { recursive: true });
1526
+ fs.writeFileSync(loopFile, "export function runAgentLoop() {}\n", "utf-8");
1527
+ fs.writeFileSync(actionsFile, "export const buildActionMenu = () => [];\n", "utf-8");
1528
+ fs.writeFileSync(stateFile, "export const startStep = () => {};\n", "utf-8");
1529
+ searchState.dbFileCandidates.set("Explain how agentMemory, agentLoop, agentActions, and agentStateMachine divide responsibilities", [loopFile, actionsFile, stateFile]);
1530
+ generateMock
1531
+ .mockResolvedValueOnce({
1532
+ data: JSON.stringify({
1533
+ normalizedQuery: "Explain how agentMemory, agentLoop, agentActions, and agentStateMachine divide responsibilities",
1534
+ queryScope: "multi-file",
1535
+ explicitTargets: [],
1536
+ constraints: [],
1537
+ primaryIntent: "Explain how the simple-agent parts divide responsibilities.",
1538
+ alternativeIntents: [],
1539
+ ambiguityFlags: [],
1540
+ }),
1541
+ })
1542
+ .mockResolvedValueOnce({
1543
+ data: JSON.stringify({
1544
+ action: "search-db",
1545
+ args: {
1546
+ query: "Explain how agentMemory, agentLoop, agentActions, and agentStateMachine divide responsibilities",
1547
+ },
1548
+ reason: "Find likely source files first.",
1549
+ }),
1550
+ })
1551
+ .mockResolvedValueOnce({
1552
+ data: JSON.stringify({
1553
+ action: "read-file",
1554
+ args: {
1555
+ target: loopFile,
1556
+ },
1557
+ reason: "Read the top-ranked candidate first.",
1558
+ }),
1559
+ })
1560
+ .mockResolvedValueOnce({
1561
+ data: JSON.stringify({
1562
+ summary: "Loop coordinator file.",
1563
+ relevanceExplanation: "This top-ranked candidate owns the loop execution part of the responsibilities question.",
1564
+ role: "primary",
1565
+ excerpts: [{
1566
+ description: "Loop export.",
1567
+ startLine: 1,
1568
+ endLine: 1,
1569
+ symbols: ["runAgentLoop"],
1570
+ code: "export function runAgentLoop() {}",
1571
+ }],
1572
+ risks: [],
1573
+ }),
1574
+ })
1575
+ .mockResolvedValueOnce({
1576
+ data: JSON.stringify({
1577
+ action: "read-file",
1578
+ args: {},
1579
+ reason: "One read was not enough, so read the next ranked candidate.",
1580
+ }),
1581
+ })
1582
+ .mockResolvedValueOnce({
1583
+ data: JSON.stringify({
1584
+ summary: "Action menu file.",
1585
+ relevanceExplanation: "This second candidate owns action construction and complements the loop file for the requested explanation.",
1586
+ role: "supporting",
1587
+ excerpts: [{
1588
+ description: "Action menu export.",
1589
+ startLine: 1,
1590
+ endLine: 1,
1591
+ symbols: ["buildActionMenu"],
1592
+ code: "export const buildActionMenu = () => [];",
1593
+ }],
1594
+ risks: [],
1595
+ }),
1596
+ })
1597
+ .mockResolvedValueOnce({
1598
+ data: JSON.stringify({
1599
+ action: "final-answer",
1600
+ args: {},
1601
+ reason: "The grounded file reads are enough to answer now.",
1602
+ }),
1603
+ })
1604
+ .mockResolvedValueOnce({
1605
+ data: "agentLoop coordinates execution, agentActions normalizes and runs actions, and agentStateMachine owns transitions.",
1606
+ });
1607
+ const memory = createAgentMemory("Explain how agentMemory, agentLoop, agentActions, and agentStateMachine divide responsibilities", tempDir, tempDir);
1608
+ const ui = {
1609
+ update: vi.fn(),
1610
+ message: vi.fn(),
1611
+ pause: vi.fn((fn) => fn()),
1612
+ succeed: vi.fn(),
1613
+ fail: vi.fn(),
1614
+ stop: vi.fn(),
1615
+ };
1616
+ await runAgentLoop(memory, ui);
1617
+ expect(memory.events.map((event) => event.type)).not.toContain("validation-failed");
1618
+ expect(Object.keys(memory.evidence.fileReads)).toEqual(expect.arrayContaining([loopFile, actionsFile]));
1619
+ expect(Object.keys(memory.evidence.fileReads)).not.toContain(stateFile);
1620
+ expect(memory.task.status).toBe("done");
1621
+ expect(memory.artifacts.finalAnswer).toContain("agentLoop coordinates execution");
1622
+ });
1623
+ it("uses triage-file before full reads on broad candidate sets", async () => {
1624
+ const loopFile = path.join(tempDir, "cli", "src", "agents", "agentLoop.ts");
1625
+ const actionsFile = path.join(tempDir, "cli", "src", "agents", "agentActions.ts");
1626
+ const stateFile = path.join(tempDir, "cli", "src", "agents", "agentStateMachine.ts");
1627
+ const policyFile = path.join(tempDir, "cli", "src", "agents", "agentPolicyState.ts");
1628
+ fs.mkdirSync(path.dirname(loopFile), { recursive: true });
1629
+ fs.writeFileSync(loopFile, "export function runAgentLoop() {}\n", "utf-8");
1630
+ fs.writeFileSync(actionsFile, "export const buildActionMenu = () => [];\n", "utf-8");
1631
+ fs.writeFileSync(stateFile, "export const startStep = () => {};\n", "utf-8");
1632
+ fs.writeFileSync(policyFile, "export const resolvePolicyState = () => ({});\n", "utf-8");
1633
+ searchState.dbFileCandidates.set("Explain how the modular agent chooses between triage-file and read-file", [loopFile, actionsFile, stateFile, policyFile]);
1634
+ generateMock
1635
+ .mockResolvedValueOnce({
1636
+ data: JSON.stringify({
1637
+ normalizedQuery: "Explain how the modular agent chooses between triage-file and read-file",
1638
+ queryScope: "multi-file",
1639
+ explicitTargets: [],
1640
+ constraints: [],
1641
+ primaryIntent: "Explain read and triage decision ownership.",
1642
+ alternativeIntents: [],
1643
+ ambiguityFlags: [],
1644
+ }),
1645
+ })
1646
+ .mockResolvedValueOnce({
1647
+ data: JSON.stringify({
1648
+ action: "search-db",
1649
+ args: {
1650
+ query: "Explain how the modular agent chooses between triage-file and read-file",
1651
+ },
1652
+ reason: "Find likely source files first.",
1653
+ }),
1654
+ })
1655
+ .mockResolvedValueOnce({
1656
+ data: JSON.stringify({
1657
+ action: "triage-file",
1658
+ args: { target: loopFile },
1659
+ reason: "Triage the strongest ranked candidate first.",
1660
+ }),
1661
+ })
1662
+ .mockResolvedValueOnce({
1663
+ data: JSON.stringify({
1664
+ action: "triage-file",
1665
+ args: { target: actionsFile },
1666
+ reason: "Triage one more top candidate before a full read.",
1667
+ }),
1668
+ })
1669
+ .mockResolvedValueOnce({
1670
+ data: JSON.stringify({
1671
+ action: "read-file",
1672
+ args: { target: actionsFile },
1673
+ reason: "The second triaged candidate now looks like the clearest owner.",
1674
+ }),
1675
+ })
1676
+ .mockResolvedValueOnce({
1677
+ data: JSON.stringify({
1678
+ summary: "Action normalization and menu wiring.",
1679
+ relevanceExplanation: "This file owns action contracts and shows how triage-file and read-file are surfaced to the runtime.",
1680
+ role: "primary",
1681
+ excerpts: [{
1682
+ description: "Action registration block.",
1683
+ startLine: 1,
1684
+ endLine: 1,
1685
+ symbols: ["buildActionMenu"],
1686
+ code: "export const buildActionMenu = () => [];",
1687
+ }],
1688
+ risks: [],
1689
+ }),
1690
+ })
1691
+ .mockResolvedValueOnce({
1692
+ data: JSON.stringify({
1693
+ action: "final-answer",
1694
+ args: {},
1695
+ reason: "The promoted full read is enough to answer now.",
1696
+ }),
1697
+ })
1698
+ .mockResolvedValueOnce({
1699
+ data: "agentActions owns action contracts while policy decides when triage-file or read-file should run.",
1700
+ });
1701
+ const memory = createAgentMemory("Explain how the modular agent chooses between triage-file and read-file", tempDir, tempDir);
1702
+ const ui = {
1703
+ update: vi.fn(),
1704
+ message: vi.fn(),
1705
+ pause: vi.fn((fn) => fn()),
1706
+ succeed: vi.fn(),
1707
+ fail: vi.fn(),
1708
+ stop: vi.fn(),
1709
+ };
1710
+ await runAgentLoop(memory, ui);
1711
+ expect(memory.steps.map((step) => step.action)).toEqual([
1712
+ "search-db",
1713
+ "triage-file",
1714
+ "triage-file",
1715
+ "read-file",
1716
+ "final-answer",
1717
+ ]);
1718
+ expect(Object.values(memory.evidence.fileReads).filter((read) => read.readMode === "triage").length).toBeGreaterThanOrEqual(1);
1719
+ expect(Object.values(memory.evidence.fileReads).filter((read) => read.readMode === "full")).toHaveLength(1);
1720
+ expect(memory.artifacts.finalAnswer).toContain("triage-file");
1721
+ });
1722
+ });
1723
+ //# sourceMappingURL=agentLoop.test.js.map