@mcoda/mswarm 0.1.56 → 0.1.60

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 (250) hide show
  1. package/README.md +20 -1
  2. package/dist/codali-executor.d.ts +266 -0
  3. package/dist/codali-executor.d.ts.map +1 -0
  4. package/dist/codali-executor.js +227 -0
  5. package/dist/codali-executor.js.map +1 -0
  6. package/dist/runtime.d.ts +47 -1
  7. package/dist/runtime.d.ts.map +1 -1
  8. package/dist/runtime.js +248 -30
  9. package/dist/runtime.js.map +1 -1
  10. package/dist/server.d.ts.map +1 -1
  11. package/dist/server.js +83 -3
  12. package/dist/server.js.map +1 -1
  13. package/dist/vendor/codali/agents/AgentProtocol.d.ts +287 -0
  14. package/dist/vendor/codali/agents/AgentProtocol.d.ts.map +1 -0
  15. package/dist/vendor/codali/agents/AgentProtocol.js +365 -0
  16. package/dist/vendor/codali/agents/AgentResolver.d.ts +23 -0
  17. package/dist/vendor/codali/agents/AgentResolver.d.ts.map +1 -0
  18. package/dist/vendor/codali/agents/AgentResolver.js +77 -0
  19. package/dist/vendor/codali/agents/PhaseAgentSelector.d.ts +23 -0
  20. package/dist/vendor/codali/agents/PhaseAgentSelector.d.ts.map +1 -0
  21. package/dist/vendor/codali/agents/PhaseAgentSelector.js +287 -0
  22. package/dist/vendor/codali/cli/EvalCommand.d.ts +37 -0
  23. package/dist/vendor/codali/cli/EvalCommand.d.ts.map +1 -0
  24. package/dist/vendor/codali/cli/EvalCommand.js +333 -0
  25. package/dist/vendor/codali/cli/FeedbackCommand.d.ts +22 -0
  26. package/dist/vendor/codali/cli/FeedbackCommand.d.ts.map +1 -0
  27. package/dist/vendor/codali/cli/FeedbackCommand.js +163 -0
  28. package/dist/vendor/codali/cli/RunCommand.d.ts +78 -0
  29. package/dist/vendor/codali/cli/RunCommand.d.ts.map +1 -0
  30. package/dist/vendor/codali/cli/RunCommand.js +2261 -0
  31. package/dist/vendor/codali/cli.d.ts +3 -0
  32. package/dist/vendor/codali/cli.d.ts.map +1 -0
  33. package/dist/vendor/codali/cli.js +109 -0
  34. package/dist/vendor/codali/cognitive/ArchitectPlanner.d.ts +107 -0
  35. package/dist/vendor/codali/cognitive/ArchitectPlanner.d.ts.map +1 -0
  36. package/dist/vendor/codali/cognitive/ArchitectPlanner.js +1726 -0
  37. package/dist/vendor/codali/cognitive/BuilderOutputParser.d.ts +25 -0
  38. package/dist/vendor/codali/cognitive/BuilderOutputParser.d.ts.map +1 -0
  39. package/dist/vendor/codali/cognitive/BuilderOutputParser.js +164 -0
  40. package/dist/vendor/codali/cognitive/BuilderRunner.d.ts +76 -0
  41. package/dist/vendor/codali/cognitive/BuilderRunner.d.ts.map +1 -0
  42. package/dist/vendor/codali/cognitive/BuilderRunner.js +1159 -0
  43. package/dist/vendor/codali/cognitive/ContextAssembler.d.ts +91 -0
  44. package/dist/vendor/codali/cognitive/ContextAssembler.d.ts.map +1 -0
  45. package/dist/vendor/codali/cognitive/ContextAssembler.js +4547 -0
  46. package/dist/vendor/codali/cognitive/ContextBudget.d.ts +19 -0
  47. package/dist/vendor/codali/cognitive/ContextBudget.d.ts.map +1 -0
  48. package/dist/vendor/codali/cognitive/ContextBudget.js +35 -0
  49. package/dist/vendor/codali/cognitive/ContextFileLoader.d.ts +30 -0
  50. package/dist/vendor/codali/cognitive/ContextFileLoader.d.ts.map +1 -0
  51. package/dist/vendor/codali/cognitive/ContextFileLoader.js +307 -0
  52. package/dist/vendor/codali/cognitive/ContextManager.d.ts +47 -0
  53. package/dist/vendor/codali/cognitive/ContextManager.d.ts.map +1 -0
  54. package/dist/vendor/codali/cognitive/ContextManager.js +272 -0
  55. package/dist/vendor/codali/cognitive/ContextRedactor.d.ts +18 -0
  56. package/dist/vendor/codali/cognitive/ContextRedactor.d.ts.map +1 -0
  57. package/dist/vendor/codali/cognitive/ContextRedactor.js +53 -0
  58. package/dist/vendor/codali/cognitive/ContextSelector.d.ts +22 -0
  59. package/dist/vendor/codali/cognitive/ContextSelector.d.ts.map +1 -0
  60. package/dist/vendor/codali/cognitive/ContextSelector.js +431 -0
  61. package/dist/vendor/codali/cognitive/ContextSerializer.d.ts +8 -0
  62. package/dist/vendor/codali/cognitive/ContextSerializer.d.ts.map +1 -0
  63. package/dist/vendor/codali/cognitive/ContextSerializer.js +882 -0
  64. package/dist/vendor/codali/cognitive/ContextStore.d.ts +27 -0
  65. package/dist/vendor/codali/cognitive/ContextStore.d.ts.map +1 -0
  66. package/dist/vendor/codali/cognitive/ContextStore.js +79 -0
  67. package/dist/vendor/codali/cognitive/ContextSummarizer.d.ts +16 -0
  68. package/dist/vendor/codali/cognitive/ContextSummarizer.d.ts.map +1 -0
  69. package/dist/vendor/codali/cognitive/ContextSummarizer.js +45 -0
  70. package/dist/vendor/codali/cognitive/CostEstimator.d.ts +31 -0
  71. package/dist/vendor/codali/cognitive/CostEstimator.d.ts.map +1 -0
  72. package/dist/vendor/codali/cognitive/CostEstimator.js +66 -0
  73. package/dist/vendor/codali/cognitive/CriticEvaluator.d.ts +32 -0
  74. package/dist/vendor/codali/cognitive/CriticEvaluator.d.ts.map +1 -0
  75. package/dist/vendor/codali/cognitive/CriticEvaluator.js +297 -0
  76. package/dist/vendor/codali/cognitive/EvidenceGate.d.ts +9 -0
  77. package/dist/vendor/codali/cognitive/EvidenceGate.d.ts.map +1 -0
  78. package/dist/vendor/codali/cognitive/EvidenceGate.js +75 -0
  79. package/dist/vendor/codali/cognitive/GoldenExampleIndexer.d.ts +12 -0
  80. package/dist/vendor/codali/cognitive/GoldenExampleIndexer.d.ts.map +1 -0
  81. package/dist/vendor/codali/cognitive/GoldenExampleIndexer.js +34 -0
  82. package/dist/vendor/codali/cognitive/GoldenSetStore.d.ts +33 -0
  83. package/dist/vendor/codali/cognitive/GoldenSetStore.d.ts.map +1 -0
  84. package/dist/vendor/codali/cognitive/GoldenSetStore.js +159 -0
  85. package/dist/vendor/codali/cognitive/IntentSignals.d.ts +7 -0
  86. package/dist/vendor/codali/cognitive/IntentSignals.d.ts.map +1 -0
  87. package/dist/vendor/codali/cognitive/IntentSignals.js +285 -0
  88. package/dist/vendor/codali/cognitive/LearningGovernance.d.ts +100 -0
  89. package/dist/vendor/codali/cognitive/LearningGovernance.d.ts.map +1 -0
  90. package/dist/vendor/codali/cognitive/LearningGovernance.js +276 -0
  91. package/dist/vendor/codali/cognitive/MemoryWriteback.d.ts +64 -0
  92. package/dist/vendor/codali/cognitive/MemoryWriteback.d.ts.map +1 -0
  93. package/dist/vendor/codali/cognitive/MemoryWriteback.js +287 -0
  94. package/dist/vendor/codali/cognitive/PatchApplier.d.ts +49 -0
  95. package/dist/vendor/codali/cognitive/PatchApplier.d.ts.map +1 -0
  96. package/dist/vendor/codali/cognitive/PatchApplier.js +199 -0
  97. package/dist/vendor/codali/cognitive/PatchInterpreter.d.ts +35 -0
  98. package/dist/vendor/codali/cognitive/PatchInterpreter.d.ts.map +1 -0
  99. package/dist/vendor/codali/cognitive/PatchInterpreter.js +100 -0
  100. package/dist/vendor/codali/cognitive/PatchOutputNormalizer.d.ts +7 -0
  101. package/dist/vendor/codali/cognitive/PatchOutputNormalizer.d.ts.map +1 -0
  102. package/dist/vendor/codali/cognitive/PatchOutputNormalizer.js +59 -0
  103. package/dist/vendor/codali/cognitive/PostMortemAnalyzer.d.ts +17 -0
  104. package/dist/vendor/codali/cognitive/PostMortemAnalyzer.d.ts.map +1 -0
  105. package/dist/vendor/codali/cognitive/PostMortemAnalyzer.js +131 -0
  106. package/dist/vendor/codali/cognitive/PreferenceExtraction.d.ts +3 -0
  107. package/dist/vendor/codali/cognitive/PreferenceExtraction.d.ts.map +1 -0
  108. package/dist/vendor/codali/cognitive/PreferenceExtraction.js +85 -0
  109. package/dist/vendor/codali/cognitive/Prompts.d.ts +15 -0
  110. package/dist/vendor/codali/cognitive/Prompts.d.ts.map +1 -0
  111. package/dist/vendor/codali/cognitive/Prompts.js +326 -0
  112. package/dist/vendor/codali/cognitive/ProviderRouting.d.ts +16 -0
  113. package/dist/vendor/codali/cognitive/ProviderRouting.d.ts.map +1 -0
  114. package/dist/vendor/codali/cognitive/ProviderRouting.js +24 -0
  115. package/dist/vendor/codali/cognitive/QueryExtraction.d.ts +12 -0
  116. package/dist/vendor/codali/cognitive/QueryExtraction.d.ts.map +1 -0
  117. package/dist/vendor/codali/cognitive/QueryExtraction.js +262 -0
  118. package/dist/vendor/codali/cognitive/RunHistoryIndexer.d.ts +13 -0
  119. package/dist/vendor/codali/cognitive/RunHistoryIndexer.d.ts.map +1 -0
  120. package/dist/vendor/codali/cognitive/RunHistoryIndexer.js +125 -0
  121. package/dist/vendor/codali/cognitive/SmartPipeline.d.ts +92 -0
  122. package/dist/vendor/codali/cognitive/SmartPipeline.d.ts.map +1 -0
  123. package/dist/vendor/codali/cognitive/SmartPipeline.js +4804 -0
  124. package/dist/vendor/codali/cognitive/Types.d.ts +474 -0
  125. package/dist/vendor/codali/cognitive/Types.d.ts.map +1 -0
  126. package/dist/vendor/codali/cognitive/Types.js +7 -0
  127. package/dist/vendor/codali/cognitive/ValidationRunner.d.ts +57 -0
  128. package/dist/vendor/codali/cognitive/ValidationRunner.d.ts.map +1 -0
  129. package/dist/vendor/codali/cognitive/ValidationRunner.js +515 -0
  130. package/dist/vendor/codali/config/Config.d.ts +249 -0
  131. package/dist/vendor/codali/config/Config.d.ts.map +1 -0
  132. package/dist/vendor/codali/config/Config.js +200 -0
  133. package/dist/vendor/codali/config/ConfigLoader.d.ts +56 -0
  134. package/dist/vendor/codali/config/ConfigLoader.d.ts.map +1 -0
  135. package/dist/vendor/codali/config/ConfigLoader.js +1246 -0
  136. package/dist/vendor/codali/docdex/DocdexClient.d.ts +113 -0
  137. package/dist/vendor/codali/docdex/DocdexClient.d.ts.map +1 -0
  138. package/dist/vendor/codali/docdex/DocdexClient.js +524 -0
  139. package/dist/vendor/codali/eval/EvalRunner.d.ts +35 -0
  140. package/dist/vendor/codali/eval/EvalRunner.d.ts.map +1 -0
  141. package/dist/vendor/codali/eval/EvalRunner.js +38 -0
  142. package/dist/vendor/codali/eval/EvalTaskExecutor.d.ts +81 -0
  143. package/dist/vendor/codali/eval/EvalTaskExecutor.d.ts.map +1 -0
  144. package/dist/vendor/codali/eval/EvalTaskExecutor.js +371 -0
  145. package/dist/vendor/codali/eval/GateEvaluator.d.ts +31 -0
  146. package/dist/vendor/codali/eval/GateEvaluator.d.ts.map +1 -0
  147. package/dist/vendor/codali/eval/GateEvaluator.js +134 -0
  148. package/dist/vendor/codali/eval/MetricTypes.d.ts +28 -0
  149. package/dist/vendor/codali/eval/MetricTypes.d.ts.map +1 -0
  150. package/dist/vendor/codali/eval/MetricTypes.js +1 -0
  151. package/dist/vendor/codali/eval/MetricsAggregator.d.ts +4 -0
  152. package/dist/vendor/codali/eval/MetricsAggregator.d.ts.map +1 -0
  153. package/dist/vendor/codali/eval/MetricsAggregator.js +97 -0
  154. package/dist/vendor/codali/eval/RegressionComparator.d.ts +29 -0
  155. package/dist/vendor/codali/eval/RegressionComparator.d.ts.map +1 -0
  156. package/dist/vendor/codali/eval/RegressionComparator.js +155 -0
  157. package/dist/vendor/codali/eval/ReportInputAdapter.d.ts +52 -0
  158. package/dist/vendor/codali/eval/ReportInputAdapter.d.ts.map +1 -0
  159. package/dist/vendor/codali/eval/ReportInputAdapter.js +229 -0
  160. package/dist/vendor/codali/eval/ReportSerializer.d.ts +32 -0
  161. package/dist/vendor/codali/eval/ReportSerializer.d.ts.map +1 -0
  162. package/dist/vendor/codali/eval/ReportSerializer.js +33 -0
  163. package/dist/vendor/codali/eval/ReportStore.d.ts +18 -0
  164. package/dist/vendor/codali/eval/ReportStore.d.ts.map +1 -0
  165. package/dist/vendor/codali/eval/ReportStore.js +96 -0
  166. package/dist/vendor/codali/eval/SuiteLoader.d.ts +12 -0
  167. package/dist/vendor/codali/eval/SuiteLoader.d.ts.map +1 -0
  168. package/dist/vendor/codali/eval/SuiteLoader.js +51 -0
  169. package/dist/vendor/codali/eval/SuiteSchema.d.ts +56 -0
  170. package/dist/vendor/codali/eval/SuiteSchema.d.ts.map +1 -0
  171. package/dist/vendor/codali/eval/SuiteSchema.js +357 -0
  172. package/dist/vendor/codali/index.d.ts +11 -0
  173. package/dist/vendor/codali/index.d.ts.map +1 -0
  174. package/dist/vendor/codali/index.js +5 -0
  175. package/dist/vendor/codali/providers/CodexCliProvider.d.ts +8 -0
  176. package/dist/vendor/codali/providers/CodexCliProvider.d.ts.map +1 -0
  177. package/dist/vendor/codali/providers/CodexCliProvider.js +282 -0
  178. package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts +8 -0
  179. package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts.map +1 -0
  180. package/dist/vendor/codali/providers/OllamaRemoteProvider.js +300 -0
  181. package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts +8 -0
  182. package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -0
  183. package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +192 -0
  184. package/dist/vendor/codali/providers/ProviderRegistry.d.ts +12 -0
  185. package/dist/vendor/codali/providers/ProviderRegistry.d.ts.map +1 -0
  186. package/dist/vendor/codali/providers/ProviderRegistry.js +28 -0
  187. package/dist/vendor/codali/providers/ProviderTypes.d.ts +81 -0
  188. package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -0
  189. package/dist/vendor/codali/providers/ProviderTypes.js +1 -0
  190. package/dist/vendor/codali/runtime/CodaliRuntime.d.ts +183 -0
  191. package/dist/vendor/codali/runtime/CodaliRuntime.d.ts.map +1 -0
  192. package/dist/vendor/codali/runtime/CodaliRuntime.js +1363 -0
  193. package/dist/vendor/codali/runtime/DeepInvestigationErrors.d.ts +39 -0
  194. package/dist/vendor/codali/runtime/DeepInvestigationErrors.d.ts.map +1 -0
  195. package/dist/vendor/codali/runtime/DeepInvestigationErrors.js +57 -0
  196. package/dist/vendor/codali/runtime/RunContext.d.ts +27 -0
  197. package/dist/vendor/codali/runtime/RunContext.d.ts.map +1 -0
  198. package/dist/vendor/codali/runtime/RunContext.js +51 -0
  199. package/dist/vendor/codali/runtime/RunLogQuery.d.ts +48 -0
  200. package/dist/vendor/codali/runtime/RunLogQuery.d.ts.map +1 -0
  201. package/dist/vendor/codali/runtime/RunLogQuery.js +36 -0
  202. package/dist/vendor/codali/runtime/RunLogReader.d.ts +19 -0
  203. package/dist/vendor/codali/runtime/RunLogReader.d.ts.map +1 -0
  204. package/dist/vendor/codali/runtime/RunLogReader.js +361 -0
  205. package/dist/vendor/codali/runtime/RunLogger.d.ts +71 -0
  206. package/dist/vendor/codali/runtime/RunLogger.d.ts.map +1 -0
  207. package/dist/vendor/codali/runtime/RunLogger.js +100 -0
  208. package/dist/vendor/codali/runtime/RunTelemetryTypes.d.ts +117 -0
  209. package/dist/vendor/codali/runtime/RunTelemetryTypes.d.ts.map +1 -0
  210. package/dist/vendor/codali/runtime/RunTelemetryTypes.js +299 -0
  211. package/dist/vendor/codali/runtime/Runner.d.ts +66 -0
  212. package/dist/vendor/codali/runtime/Runner.d.ts.map +1 -0
  213. package/dist/vendor/codali/runtime/Runner.js +215 -0
  214. package/dist/vendor/codali/runtime/StoragePaths.d.ts +3 -0
  215. package/dist/vendor/codali/runtime/StoragePaths.d.ts.map +1 -0
  216. package/dist/vendor/codali/runtime/StoragePaths.js +19 -0
  217. package/dist/vendor/codali/runtime/WorkspaceLock.d.ts +30 -0
  218. package/dist/vendor/codali/runtime/WorkspaceLock.d.ts.map +1 -0
  219. package/dist/vendor/codali/runtime/WorkspaceLock.js +141 -0
  220. package/dist/vendor/codali/session/InstructionLoader.d.ts +14 -0
  221. package/dist/vendor/codali/session/InstructionLoader.d.ts.map +1 -0
  222. package/dist/vendor/codali/session/InstructionLoader.js +107 -0
  223. package/dist/vendor/codali/session/SessionStore.d.ts +81 -0
  224. package/dist/vendor/codali/session/SessionStore.d.ts.map +1 -0
  225. package/dist/vendor/codali/session/SessionStore.js +244 -0
  226. package/dist/vendor/codali/subagents/SubagentOrchestrator.d.ts +68 -0
  227. package/dist/vendor/codali/subagents/SubagentOrchestrator.d.ts.map +1 -0
  228. package/dist/vendor/codali/subagents/SubagentOrchestrator.js +150 -0
  229. package/dist/vendor/codali/tools/ToolRegistry.d.ts +9 -0
  230. package/dist/vendor/codali/tools/ToolRegistry.d.ts.map +1 -0
  231. package/dist/vendor/codali/tools/ToolRegistry.js +263 -0
  232. package/dist/vendor/codali/tools/ToolTypes.d.ts +66 -0
  233. package/dist/vendor/codali/tools/ToolTypes.d.ts.map +1 -0
  234. package/dist/vendor/codali/tools/ToolTypes.js +32 -0
  235. package/dist/vendor/codali/tools/diff/DiffTool.d.ts +3 -0
  236. package/dist/vendor/codali/tools/diff/DiffTool.d.ts.map +1 -0
  237. package/dist/vendor/codali/tools/diff/DiffTool.js +34 -0
  238. package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts +4 -0
  239. package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts.map +1 -0
  240. package/dist/vendor/codali/tools/docdex/DocdexTools.js +453 -0
  241. package/dist/vendor/codali/tools/filesystem/FileTools.d.ts +3 -0
  242. package/dist/vendor/codali/tools/filesystem/FileTools.d.ts.map +1 -0
  243. package/dist/vendor/codali/tools/filesystem/FileTools.js +141 -0
  244. package/dist/vendor/codali/tools/search/SearchTool.d.ts +3 -0
  245. package/dist/vendor/codali/tools/search/SearchTool.d.ts.map +1 -0
  246. package/dist/vendor/codali/tools/search/SearchTool.js +46 -0
  247. package/dist/vendor/codali/tools/shell/ShellTool.d.ts +3 -0
  248. package/dist/vendor/codali/tools/shell/ShellTool.d.ts.map +1 -0
  249. package/dist/vendor/codali/tools/shell/ShellTool.js +104 -0
  250. package/package.json +5 -3
@@ -0,0 +1,1726 @@
1
+ import { serializeContext } from "./ContextSerializer.js";
2
+ import { ARCHITECT_PROMPT, ARCHITECT_REVIEW_PROMPT, ARCHITECT_VALIDATE_PROMPT, } from "./Prompts.js";
3
+ import { parseAgentRequest } from "../agents/AgentProtocol.js";
4
+ export const ARCHITECT_WARNING_NON_DSL = "architect_output_unstructured_plaintext";
5
+ export const ARCHITECT_WARNING_CONTAINS_THINK = "architect_output_contains_think";
6
+ export const ARCHITECT_WARNING_CONTAINS_FENCE = "architect_output_contains_fence";
7
+ export const ARCHITECT_WARNING_MISSING_REQUIRED_SECTIONS = "architect_output_missing_required_sections";
8
+ export const ARCHITECT_WARNING_MULTIPLE_SECTION_BLOCKS = "architect_output_multiple_section_blocks";
9
+ export const ARCHITECT_WARNING_USED_JSON_FALLBACK = "architect_output_used_json_fallback";
10
+ export const ARCHITECT_WARNING_USED_PLAINTEXT_FALLBACK = "architect_output_used_plaintext_fallback";
11
+ export const ARCHITECT_WARNING_REPAIRED = "architect_output_repaired";
12
+ const buildContextNarrative = (context) => {
13
+ if (context.serialized?.mode === "bundle_text" && context.serialized.audience === "librarian") {
14
+ return context.serialized.content;
15
+ }
16
+ return serializeContext(context, { mode: "bundle_text", audience: "librarian" }).content;
17
+ };
18
+ const buildUserMessage = (context) => ({
19
+ role: "user",
20
+ content: buildContextNarrative(context),
21
+ });
22
+ const normalizeStrings = (values) => values.map((value) => value.trim()).filter((value) => value.length > 0);
23
+ const uniqueStrings = (values) => Array.from(new Set(values));
24
+ const normalizePath = (value) => value.replace(/\\/g, "/").replace(/^\.?\//, "").trim();
25
+ const REQUEST_FILE_PATTERN = /(?:[A-Za-z0-9._-]+\/)+[A-Za-z0-9._-]+\.[A-Za-z0-9]+/g;
26
+ const ENDPOINT_INTENT_PATTERN = /\b(endpoint|route|router|handler|api|health|healthz|status|ping)\b/i;
27
+ const UI_LAYOUT_REQUEST_PATTERN = /\b(ui|screen|page|homepage|header|footer|section|layout|render|styling|style)\b/i;
28
+ const DOC_PATH_PATTERN = /(^|\/)(docs?|openapi|specs?)\//i;
29
+ const TEST_PATH_PATTERN = /(^|\/)(__tests__|tests?|test)\//i;
30
+ const FRONTEND_PATH_PATTERN = /(^|\/)(public|frontend|client)\//i;
31
+ const IMPLEMENTATION_FILE_EXTENSIONS = new Set([
32
+ "ts",
33
+ "tsx",
34
+ "js",
35
+ "jsx",
36
+ "py",
37
+ "rs",
38
+ "go",
39
+ "java",
40
+ "cs",
41
+ "php",
42
+ "rb",
43
+ "kt",
44
+ "swift",
45
+ "html",
46
+ "htm",
47
+ "css",
48
+ "scss",
49
+ "sass",
50
+ "less",
51
+ "vue",
52
+ "svelte",
53
+ ]);
54
+ const REQUEST_TOKEN_STOPWORDS = new Set([
55
+ "a",
56
+ "an",
57
+ "the",
58
+ "to",
59
+ "of",
60
+ "for",
61
+ "and",
62
+ "or",
63
+ "with",
64
+ "create",
65
+ "add",
66
+ "introduce",
67
+ "scaffold",
68
+ "bootstrap",
69
+ "build",
70
+ "develop",
71
+ "implement",
72
+ "setup",
73
+ "set",
74
+ "up",
75
+ "check",
76
+ "system",
77
+ "script",
78
+ "file",
79
+ "endpoint",
80
+ "route",
81
+ "handler",
82
+ "api",
83
+ ]);
84
+ const PROSE_RECOVERY_STOPWORDS = new Set([
85
+ "think",
86
+ "thinking",
87
+ "okay",
88
+ "alright",
89
+ "sure",
90
+ "assistant",
91
+ "role",
92
+ "content",
93
+ "json",
94
+ "markdown",
95
+ "dsl",
96
+ "plan",
97
+ "targets",
98
+ "verify",
99
+ "risk",
100
+ "response",
101
+ "output",
102
+ ]);
103
+ const parseRepoMapPaths = (repoMap) => {
104
+ if (!repoMap)
105
+ return [];
106
+ const lines = repoMap
107
+ .split(/\r?\n/)
108
+ .map((line) => line.replace(/\s+$/g, ""))
109
+ .filter((line) => line.length > 0);
110
+ if (lines.length <= 1)
111
+ return [];
112
+ const stack = [];
113
+ const paths = [];
114
+ for (let i = 1; i < lines.length; i += 1) {
115
+ const line = lines[i];
116
+ const branchIndex = line.indexOf("├── ");
117
+ const leafIndex = line.indexOf("└── ");
118
+ const markerIndex = branchIndex >= 0 ? branchIndex : leafIndex >= 0 ? leafIndex : -1;
119
+ if (markerIndex < 0)
120
+ continue;
121
+ const nameRaw = line.slice(markerIndex + 4).trim();
122
+ if (!nameRaw)
123
+ continue;
124
+ const name = nameRaw.replace(/\s+\(.*\)\s*$/g, "").trim();
125
+ if (!name)
126
+ continue;
127
+ const depth = Math.floor(markerIndex / 4);
128
+ stack[depth] = name;
129
+ stack.length = depth + 1;
130
+ paths.push(normalizePath(stack.join("/")));
131
+ }
132
+ return uniqueStrings(paths).filter(Boolean);
133
+ };
134
+ const collectContextPaths = (context) => {
135
+ const selection = context.selection?.all ?? [];
136
+ const files = (context.files ?? []).map((entry) => entry.path);
137
+ const snippets = (context.snippets ?? []).map((entry) => entry.path ?? "");
138
+ const symbols = (context.symbols ?? []).map((entry) => entry.path);
139
+ const ast = (context.ast ?? []).map((entry) => entry.path);
140
+ const impact = (context.impact ?? []).map((entry) => entry.file);
141
+ const search = (context.search_results ?? [])
142
+ .flatMap((result) => result.hits ?? [])
143
+ .map((hit) => hit.path ?? "");
144
+ const repoMapPaths = parseRepoMapPaths(context.repo_map_raw ?? context.repo_map);
145
+ return uniqueStrings([...selection, ...files, ...snippets, ...symbols, ...ast, ...impact, ...search, ...repoMapPaths]
146
+ .map((entry) => normalizePath(entry))
147
+ .filter(Boolean));
148
+ };
149
+ const isDocPath = (value) => DOC_PATH_PATTERN.test(normalizePath(value)) || /\.(md|mdx|txt|rst)$/i.test(value);
150
+ const isTestPath = (value) => TEST_PATH_PATTERN.test(normalizePath(value)) || /\.(test|spec)\.[^.]+$/i.test(value);
151
+ const isSourceCodePath = (value) => {
152
+ const normalized = normalizePath(value);
153
+ if (!normalized)
154
+ return false;
155
+ if (isDocPath(normalized) || isTestPath(normalized))
156
+ return false;
157
+ const ext = normalized.slice(normalized.lastIndexOf(".") + 1).toLowerCase();
158
+ return IMPLEMENTATION_FILE_EXTENSIONS.has(ext);
159
+ };
160
+ const extractRequestTokens = (request) => request
161
+ .toLowerCase()
162
+ .replace(/[^a-z0-9/_-]/g, " ")
163
+ .split(/\s+/)
164
+ .filter((token) => token.length >= 3 && !REQUEST_TOKEN_STOPWORDS.has(token));
165
+ const extractRequestedPaths = (request) => uniqueStrings((request.match(REQUEST_FILE_PATTERN) ?? []).map((entry) => normalizePath(entry)));
166
+ const toPascalCase = (value) => value
167
+ .split(/[^a-zA-Z0-9]+/)
168
+ .filter(Boolean)
169
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`)
170
+ .join("");
171
+ const scoreEndpointTarget = (value) => {
172
+ const normalized = normalizePath(value).toLowerCase();
173
+ let score = 0;
174
+ if (!isSourceCodePath(normalized))
175
+ return -100;
176
+ if (FRONTEND_PATH_PATTERN.test(normalized))
177
+ score -= 40;
178
+ if (isTestPath(normalized))
179
+ score -= 30;
180
+ if (/server\.[^.]+$/.test(normalized))
181
+ score += 60;
182
+ if (/(^|\/)(api|routes?|router|handlers?|controllers?)\//.test(normalized))
183
+ score += 45;
184
+ if (/health|status|ping/.test(normalized))
185
+ score += 20;
186
+ return score;
187
+ };
188
+ const isMarkupLikePath = (value) => {
189
+ const normalized = normalizePath(value);
190
+ if (!normalized || isDocPath(normalized) || isTestPath(normalized))
191
+ return false;
192
+ if (/(^|\/)(views?|templates?)\//i.test(normalized))
193
+ return true;
194
+ return /\.(html?|jsx|tsx|vue|svelte)$/i.test(normalized);
195
+ };
196
+ const scoreMarkupTarget = (value) => {
197
+ const normalized = normalizePath(value).toLowerCase();
198
+ let score = 0;
199
+ if (!isMarkupLikePath(normalized))
200
+ return -100;
201
+ if (FRONTEND_PATH_PATTERN.test(normalized))
202
+ score += 30;
203
+ if (/\/index\.[a-z0-9]+$/.test(normalized))
204
+ score += 24;
205
+ if (/\.html?$/.test(normalized))
206
+ score += 18;
207
+ if (/\/(views?|templates?)\//.test(normalized))
208
+ score += 12;
209
+ return score;
210
+ };
211
+ const deriveFallbackTargetFiles = (context) => {
212
+ const request = context.request ?? "";
213
+ const requestPaths = extractRequestedPaths(request);
214
+ if (requestPaths.length > 0)
215
+ return requestPaths;
216
+ const discoveredPaths = collectContextPaths(context);
217
+ const includeUiMarkupTarget = (targets) => {
218
+ const dedupedTargets = uniqueStrings(targets.map((entry) => normalizePath(entry)).filter(Boolean));
219
+ if (!UI_LAYOUT_REQUEST_PATTERN.test(request))
220
+ return dedupedTargets;
221
+ if (dedupedTargets.some((entry) => isMarkupLikePath(entry)))
222
+ return dedupedTargets;
223
+ const markupCandidate = discoveredPaths
224
+ .map((entry) => ({ path: normalizePath(entry), score: scoreMarkupTarget(entry) }))
225
+ .filter((entry) => entry.score > 0)
226
+ .sort((a, b) => b.score - a.score)
227
+ .map((entry) => entry.path)[0];
228
+ if (!markupCandidate)
229
+ return dedupedTargets;
230
+ return uniqueStrings([...dedupedTargets, markupCandidate]);
231
+ };
232
+ const baseTargets = targetFilesFromContext(context).map((entry) => normalizePath(entry));
233
+ const nonPlaceholderTargets = baseTargets.filter((entry) => entry !== "unknown");
234
+ if (ENDPOINT_INTENT_PATTERN.test(request)) {
235
+ const endpointCandidates = discoveredPaths
236
+ .map((entry) => ({ path: normalizePath(entry), score: scoreEndpointTarget(entry) }))
237
+ .filter((entry) => entry.score > 0)
238
+ .sort((a, b) => b.score - a.score)
239
+ .map((entry) => entry.path);
240
+ if (endpointCandidates.length > 0) {
241
+ return uniqueStrings(endpointCandidates.slice(0, 2));
242
+ }
243
+ }
244
+ const baseCodeTargets = nonPlaceholderTargets.filter((entry) => isSourceCodePath(entry));
245
+ if (baseCodeTargets.length > 0 && !baseCodeTargets.every((entry) => isDocPath(entry) || isTestPath(entry))) {
246
+ return includeUiMarkupTarget(baseCodeTargets);
247
+ }
248
+ const discoveredCodeTargets = discoveredPaths.filter((entry) => isSourceCodePath(entry));
249
+ if (discoveredCodeTargets.length > 0) {
250
+ return includeUiMarkupTarget(uniqueStrings(discoveredCodeTargets).slice(0, 6));
251
+ }
252
+ const nonDocTargets = nonPlaceholderTargets.filter((entry) => !isDocPath(entry) && !isTestPath(entry));
253
+ if (nonDocTargets.length > 0)
254
+ return includeUiMarkupTarget(nonDocTargets);
255
+ return ["unknown"];
256
+ };
257
+ const computeCreateTargets = (context, targetFiles) => {
258
+ const existingPathSet = new Set(collectContextPaths(context).map((entry) => normalizePath(entry)));
259
+ return targetFiles.filter((path) => {
260
+ const normalized = normalizePath(path);
261
+ return normalized !== "unknown" && !existingPathSet.has(normalized);
262
+ });
263
+ };
264
+ const collectPlannerScopePaths = (context) => {
265
+ const focus = (context.selection?.focus ?? []).map((entry) => normalizePath(entry)).filter(Boolean);
266
+ const periphery = (context.selection?.periphery ?? []).map((entry) => normalizePath(entry)).filter(Boolean);
267
+ return uniqueStrings([...focus, ...periphery]);
268
+ };
269
+ const assessPlanTargetScope = (context, plan) => {
270
+ const scopePaths = collectPlannerScopePaths(context);
271
+ const scopeSet = new Set(scopePaths.map((entry) => entry.toLowerCase()));
272
+ const createTargets = uniqueStrings((plan.create_files ?? [])
273
+ .map((entry) => normalizePath(entry))
274
+ .filter((entry) => entry.length > 0 && entry !== "unknown"));
275
+ if (scopePaths.length === 0) {
276
+ return {
277
+ scopePaths,
278
+ createTargets,
279
+ invalidTargets: [],
280
+ };
281
+ }
282
+ const createSet = new Set(createTargets.map((entry) => entry.toLowerCase()));
283
+ const invalidTargets = uniqueStrings((plan.target_files ?? [])
284
+ .map((entry) => normalizePath(entry))
285
+ .filter((entry) => entry.length > 0 && entry !== "unknown")
286
+ .filter((entry) => !scopeSet.has(entry.toLowerCase()) && !createSet.has(entry.toLowerCase())));
287
+ return {
288
+ scopePaths,
289
+ createTargets,
290
+ invalidTargets,
291
+ };
292
+ };
293
+ const applyTargetScopeGuard = (context, plan, warnings) => {
294
+ const assessment = assessPlanTargetScope(context, plan);
295
+ if (assessment.scopePaths.length === 0) {
296
+ return {
297
+ plan: {
298
+ ...plan,
299
+ target_files: uniqueStrings((plan.target_files ?? [])
300
+ .map((entry) => normalizePath(entry))
301
+ .filter((entry) => entry.length > 0 && entry !== "unknown")),
302
+ create_files: assessment.createTargets.length > 0 ? assessment.createTargets : undefined,
303
+ },
304
+ warnings: uniqueStrings(warnings),
305
+ };
306
+ }
307
+ const nextWarnings = [...warnings];
308
+ if (assessment.invalidTargets.length > 0) {
309
+ nextWarnings.push(`plan_targets_outside_context:${assessment.invalidTargets.join(",")}`);
310
+ }
311
+ const invalidSet = new Set(assessment.invalidTargets.map((entry) => entry.toLowerCase()));
312
+ const createSet = new Set(assessment.createTargets.map((entry) => entry.toLowerCase()));
313
+ let nextTargets = uniqueStrings((plan.target_files ?? [])
314
+ .map((entry) => normalizePath(entry))
315
+ .filter((entry) => entry.length > 0 && entry !== "unknown")
316
+ .filter((entry) => !invalidSet.has(entry.toLowerCase()) || createSet.has(entry.toLowerCase())));
317
+ if (nextTargets.length === 0) {
318
+ nextWarnings.push("plan_target_scope_empty_after_filter");
319
+ }
320
+ return {
321
+ plan: {
322
+ ...plan,
323
+ target_files: nextTargets,
324
+ create_files: assessment.createTargets.length > 0 ? assessment.createTargets : undefined,
325
+ },
326
+ warnings: uniqueStrings(nextWarnings),
327
+ };
328
+ };
329
+ const compactWhitespace = (value) => value.replace(/\s+/g, " ").trim();
330
+ const summarizeRequestSubject = (request) => {
331
+ const compact = compactWhitespace(request ?? "");
332
+ if (!compact)
333
+ return "the requested change";
334
+ const tokens = extractRequestTokens(compact);
335
+ return tokens.length > 0 ? tokens.slice(0, 6).join(" ") : compact;
336
+ };
337
+ const pathBasename = (value) => {
338
+ const normalized = normalizePath(value);
339
+ const index = normalized.lastIndexOf("/");
340
+ return index >= 0 ? normalized.slice(index + 1) : normalized;
341
+ };
342
+ const stepMentionsTarget = (step, targetPath) => {
343
+ const normalizedTarget = normalizePath(targetPath).toLowerCase();
344
+ const basename = pathBasename(targetPath).toLowerCase();
345
+ const stepLower = step.toLowerCase();
346
+ if (normalizedTarget && stepLower.includes(normalizedTarget))
347
+ return true;
348
+ if (basename && basename.length >= 3 && stepLower.includes(basename))
349
+ return true;
350
+ return false;
351
+ };
352
+ const endpointPathPattern = /(^|\/)(api|routes?|router|handlers?|controllers?|server|health|status|ping)/i;
353
+ const buildTargetChangeStep = (context, targetPath, options) => {
354
+ const normalized = normalizePath(targetPath);
355
+ const subject = summarizeRequestSubject(context.request);
356
+ if (options.create) {
357
+ return `Create ${normalized}: add the module/function structure for ${subject}, then wire imports/exports and integration points needed by dependent files.`;
358
+ }
359
+ if (isDocPath(normalized)) {
360
+ return `Update ${normalized}: revise the relevant documentation sections for ${subject} and add any missing examples or usage notes.`;
361
+ }
362
+ if (isTestPath(normalized)) {
363
+ return `Update ${normalized}: change existing assertions for ${subject} and add coverage for the new behaviors introduced by this request.`;
364
+ }
365
+ if (ENDPOINT_INTENT_PATTERN.test(context.request ?? "") || endpointPathPattern.test(normalized)) {
366
+ return `Update ${normalized}: change route/handler behavior for ${subject} and add missing request validation or response payload helpers in this file.`;
367
+ }
368
+ if (FRONTEND_PATH_PATTERN.test(normalized) || /\.(html|css)$/i.test(normalized)) {
369
+ return `Update ${normalized}: change rendering/styling behavior for ${subject} and add any missing UI helpers or selectors needed by this file.`;
370
+ }
371
+ return `Update ${normalized}: change existing implementation for ${subject} and add any missing helper functions/constants needed in this file.`;
372
+ };
373
+ const enrichPlanWithTargetChangeDetails = (context, plan) => {
374
+ const normalizedTargets = uniqueStrings(plan.target_files
375
+ .map((value) => normalizePath(value))
376
+ .filter((value) => value.length > 0 && value !== "unknown"));
377
+ if (normalizedTargets.length === 0)
378
+ return { plan, warnings: [] };
379
+ const existingPathSet = new Set(collectContextPaths(context).map((entry) => normalizePath(entry)));
380
+ const createTargetSet = new Set(computeCreateTargets(context, normalizedTargets).map((entry) => normalizePath(entry)));
381
+ const nextSteps = [...plan.steps];
382
+ let added = 0;
383
+ for (const target of normalizedTargets) {
384
+ if (nextSteps.some((step) => stepMentionsTarget(step, target)))
385
+ continue;
386
+ const create = createTargetSet.has(target) || !existingPathSet.has(target);
387
+ nextSteps.push(buildTargetChangeStep(context, target, { create }));
388
+ added += 1;
389
+ }
390
+ if (added === 0) {
391
+ return {
392
+ plan: {
393
+ ...plan,
394
+ target_files: normalizedTargets,
395
+ },
396
+ warnings: [],
397
+ };
398
+ }
399
+ return {
400
+ plan: {
401
+ ...plan,
402
+ steps: nextSteps,
403
+ target_files: normalizedTargets,
404
+ },
405
+ warnings: [`plan_missing_target_change_details:${added}`],
406
+ };
407
+ };
408
+ const buildImplementationOutlineStep = (context, targetFiles) => {
409
+ const request = context.request ?? "";
410
+ const tokens = extractRequestTokens(request);
411
+ const feature = toPascalCase(tokens.slice(0, 2).join(" ")) || "Feature";
412
+ const primaryTarget = targetFiles.find((entry) => entry !== "unknown") ?? "target module";
413
+ if (ENDPOINT_INTENT_PATTERN.test(request)) {
414
+ const routeFn = `register${feature}Route`;
415
+ const handlerFn = `handle${feature}Request`;
416
+ const payloadFn = `build${feature}Payload`;
417
+ return `Define method responsibilities in ${primaryTarget}: ${routeFn} wires the route, ${handlerFn} handles request/response flow, and ${payloadFn} builds the health/status payload.`;
418
+ }
419
+ const moduleName = `${feature}Module`;
420
+ const parseFn = `parse${feature}Input`;
421
+ const executeFn = `execute${feature}`;
422
+ const validateFn = `validate${feature}State`;
423
+ return `Define object/method responsibilities in ${primaryTarget}: ${moduleName} orchestrates logic, ${parseFn} parses input, ${validateFn} enforces constraints, and ${executeFn} performs the core behavior.`;
424
+ };
425
+ const splitLines = (value) => {
426
+ const trimmed = value.trim();
427
+ if (!trimmed)
428
+ return [];
429
+ const lines = trimmed
430
+ .split(/\r?\n/)
431
+ .map((line) => line.replace(/^\s*[-*•]\s*/, "").replace(/^\s*\d+[.)]\s*/, "").trim())
432
+ .filter(Boolean);
433
+ if (lines.length > 1)
434
+ return lines;
435
+ const semi = trimmed.split(/\s*;\s*/).map((part) => part.trim()).filter(Boolean);
436
+ if (semi.length > 1)
437
+ return semi;
438
+ return [trimmed];
439
+ };
440
+ const toStringArray = (value) => {
441
+ if (Array.isArray(value)) {
442
+ const strings = value.filter((item) => typeof item === "string");
443
+ return strings.length ? normalizeStrings(strings) : undefined;
444
+ }
445
+ if (typeof value === "string") {
446
+ const parts = splitLines(value);
447
+ return parts.length ? normalizeStrings(parts) : undefined;
448
+ }
449
+ return undefined;
450
+ };
451
+ const toFileArray = (value) => {
452
+ if (Array.isArray(value)) {
453
+ const strings = value.filter((item) => typeof item === "string");
454
+ return strings.length ? normalizeStrings(strings) : undefined;
455
+ }
456
+ if (typeof value === "string") {
457
+ const parts = value
458
+ .split(/[\n,]+/)
459
+ .map((part) => part.trim())
460
+ .filter(Boolean);
461
+ return parts.length ? normalizeStrings(parts) : undefined;
462
+ }
463
+ return undefined;
464
+ };
465
+ const targetFilesFromContext = (context) => {
466
+ const focusFiles = context.files
467
+ ?.filter((entry) => entry.role === "focus")
468
+ .map((entry) => entry.path)
469
+ .filter((file) => Boolean(file)) ?? [];
470
+ if (focusFiles.length > 0)
471
+ return uniqueStrings(focusFiles);
472
+ const selectionFocus = context.selection?.focus ?? [];
473
+ const selectionAll = context.selection?.all ?? [];
474
+ const allFiles = context.files?.map((entry) => entry.path).filter((file) => Boolean(file)) ?? [];
475
+ const snippets = context.snippets?.map((snippet) => snippet.path).filter((file) => Boolean(file)) ?? [];
476
+ const symbols = context.symbols?.map((symbol) => symbol.path).filter((file) => Boolean(file)) ?? [];
477
+ const ast = context.ast?.map((node) => node.path).filter((file) => Boolean(file)) ?? [];
478
+ const impact = context.impact?.map((entry) => entry.file).filter((file) => Boolean(file)) ?? [];
479
+ const combined = uniqueStrings(normalizeStrings([...selectionFocus, ...selectionAll, ...allFiles, ...snippets, ...symbols, ...ast, ...impact]));
480
+ return combined.length > 0 ? combined : ["unknown"];
481
+ };
482
+ const VERIFICATION_COMMAND_PATTERN = /\b(pnpm|npm|yarn|bun|node|jest|vitest|mocha|ava|pytest|cargo|go|dotnet|mvn|gradle)\b.*\b(test|tests?|spec|check)\b/i;
483
+ const VERIFICATION_ACTION_PATTERN = /\b(run|execute|verify|check|assert|validate|curl|open|visit|navigate|request|hit|test|perform)\b/i;
484
+ const VERIFICATION_TYPE_PATTERN = /\b(unit|integration|component|e2e|end[- ]to[- ]end|api|curl|httpie|wget|browser|manual)\b/i;
485
+ const VERIFICATION_HTTP_URL_PATTERN = /\bhttps?:\/\/\S+/i;
486
+ const isConcreteVerificationStep = (step) => {
487
+ const value = step.trim();
488
+ if (!value)
489
+ return false;
490
+ if (VERIFICATION_COMMAND_PATTERN.test(value))
491
+ return true;
492
+ if (/\bcurl\b/i.test(value) && VERIFICATION_HTTP_URL_PATTERN.test(value))
493
+ return true;
494
+ if (/\b(open|visit|navigate)\b/i.test(value) && /\b(browser|localhost|https?:\/\/)\b/i.test(value)) {
495
+ return true;
496
+ }
497
+ return VERIFICATION_ACTION_PATTERN.test(value) && VERIFICATION_TYPE_PATTERN.test(value);
498
+ };
499
+ const fallbackVerification = (context, targetFiles, createTargets) => {
500
+ const normalizedRequest = compactWhitespace(context.request ?? "");
501
+ const requestTarget = normalizedRequest.length > 0 ? normalizedRequest : "the requested behavior";
502
+ const targetList = targetFiles.length > 0 ? targetFiles.join(", ") : requestTarget;
503
+ const checks = [];
504
+ if (createTargets.length > 0) {
505
+ checks.push(`Run unit/integration tests that cover new file wiring for: ${createTargets.join(", ")}.`);
506
+ }
507
+ if (ENDPOINT_INTENT_PATTERN.test(context.request ?? "")) {
508
+ checks.push(`Run unit/integration tests for API behavior covering: ${targetList}.`);
509
+ checks.push("Run manual API check: curl -sf http://localhost:3000 and verify endpoint response semantics.");
510
+ }
511
+ else {
512
+ checks.push(`Run unit/integration tests that cover: ${targetList}.`);
513
+ checks.push(`Run manual browser check: open http://localhost:3000 and verify "${requestTarget}" in the affected UI.`);
514
+ }
515
+ return uniqueStrings(checks);
516
+ };
517
+ const normalizeVerificationSteps = (context, targetFiles, createTargets, verification) => {
518
+ const normalized = normalizeStrings((verification ?? []).map((entry) => entry.trim()).filter(Boolean));
519
+ if (normalized.length === 0) {
520
+ return fallbackVerification(context, targetFiles, createTargets);
521
+ }
522
+ if (normalized.some((step) => isConcreteVerificationStep(step))) {
523
+ return normalized;
524
+ }
525
+ return uniqueStrings([
526
+ ...fallbackVerification(context, targetFiles, createTargets),
527
+ ...normalized,
528
+ ]);
529
+ };
530
+ const fallbackSteps = (context) => {
531
+ const request = context.request?.trim();
532
+ const targetFiles = deriveFallbackTargetFiles(context);
533
+ const createTargets = computeCreateTargets(context, targetFiles);
534
+ const outlineStep = buildImplementationOutlineStep(context, targetFiles);
535
+ const steps = [
536
+ "Review focus files and referenced context for the request.",
537
+ "Map request requirements to concrete implementation targets and interfaces.",
538
+ "Apply changes aligned to the request and constraints.",
539
+ "Run verification steps and summarize results.",
540
+ ];
541
+ if (request) {
542
+ steps[0] = `Review focus files for the request: ${request}`;
543
+ }
544
+ if (createTargets.length > 0) {
545
+ steps[1] = `Create missing implementation files: ${createTargets.join(", ")} and define module boundaries.`;
546
+ }
547
+ steps.splice(2, 0, outlineStep);
548
+ return steps;
549
+ };
550
+ const fallbackPlan = (context) => {
551
+ const targets = deriveFallbackTargetFiles(context);
552
+ const createTargets = computeCreateTargets(context, targets);
553
+ return {
554
+ steps: fallbackSteps(context),
555
+ target_files: targets,
556
+ create_files: createTargets.length > 0 ? createTargets : undefined,
557
+ risk_assessment: (() => {
558
+ if (createTargets.length > 0) {
559
+ return "medium: introduces new files and integration points";
560
+ }
561
+ if (ENDPOINT_INTENT_PATTERN.test(context.request ?? "")) {
562
+ return "medium: endpoint behavior and contract changes";
563
+ }
564
+ return "medium: fallback plan generated from context";
565
+ })(),
566
+ verification: fallbackVerification(context, targets, createTargets),
567
+ };
568
+ };
569
+ const coercePlan = (parsed, context) => {
570
+ const warnings = [];
571
+ if (!parsed || typeof parsed !== "object") {
572
+ warnings.push("architect_output_not_object");
573
+ return { plan: fallbackPlan(context), warnings };
574
+ }
575
+ const record = parsed;
576
+ const steps = toStringArray(record.steps) ??
577
+ toStringArray(record.plan) ??
578
+ toStringArray(record.todo) ??
579
+ undefined;
580
+ const targetFiles = toFileArray(record.target_files) ??
581
+ toFileArray(record.filesLikelyTouched) ??
582
+ toFileArray(record.files) ??
583
+ undefined;
584
+ const createFiles = toFileArray(record.create_files) ??
585
+ toFileArray(record.createFiles) ??
586
+ undefined;
587
+ const riskAssessment = typeof record.risk_assessment === "string"
588
+ ? record.risk_assessment
589
+ : typeof record.risk === "string"
590
+ ? record.risk
591
+ : undefined;
592
+ const risks = Array.isArray(record.risks)
593
+ ? record.risks.filter((item) => typeof item === "string")
594
+ : undefined;
595
+ const risk = riskAssessment ?? (risks && risks.length ? risks.join("; ") : undefined);
596
+ const verification = toStringArray(record.verification) ??
597
+ toStringArray(record.tests) ??
598
+ toStringArray(record.validate) ??
599
+ undefined;
600
+ const resolvedTargets = targetFiles && targetFiles.length > 0 ? targetFiles : deriveFallbackTargetFiles(context);
601
+ const explicitCreateTargets = createFiles && createFiles.length > 0
602
+ ? uniqueStrings(createFiles.map((entry) => normalizePath(entry)))
603
+ : [];
604
+ const verificationCreateTargets = explicitCreateTargets.length > 0
605
+ ? explicitCreateTargets
606
+ : computeCreateTargets(context, resolvedTargets);
607
+ const plan = {
608
+ steps: steps && steps.length > 0 ? steps : fallbackSteps(context),
609
+ target_files: resolvedTargets,
610
+ create_files: explicitCreateTargets.length > 0 ? explicitCreateTargets : undefined,
611
+ risk_assessment: risk && risk.length > 0 ? risk : "medium: fallback plan generated from context",
612
+ verification: normalizeVerificationSteps(context, resolvedTargets, verificationCreateTargets, verification),
613
+ };
614
+ if (!steps || steps.length === 0)
615
+ warnings.push("plan_missing_steps");
616
+ if (!targetFiles || targetFiles.length === 0)
617
+ warnings.push("plan_missing_target_files");
618
+ if (!risk || risk.length === 0)
619
+ warnings.push("plan_missing_risk_assessment");
620
+ if (!verification)
621
+ warnings.push("plan_missing_verification");
622
+ return { plan, warnings };
623
+ };
624
+ const parseJsonLoose = (content) => {
625
+ const trimmed = content.trim();
626
+ if (!trimmed)
627
+ return { error: "empty" };
628
+ const tryParse = (input) => {
629
+ try {
630
+ return { parsed: JSON.parse(input) };
631
+ }
632
+ catch {
633
+ return {};
634
+ }
635
+ };
636
+ const direct = tryParse(trimmed);
637
+ if (direct.parsed !== undefined) {
638
+ if (typeof direct.parsed === "string") {
639
+ const nested = tryParse(direct.parsed);
640
+ if (nested.parsed !== undefined)
641
+ return nested;
642
+ }
643
+ return direct;
644
+ }
645
+ const start = trimmed.indexOf("{");
646
+ const end = trimmed.lastIndexOf("}");
647
+ if (start >= 0 && end > start) {
648
+ const sliced = tryParse(trimmed.slice(start, end + 1));
649
+ if (sliced.parsed !== undefined)
650
+ return sliced;
651
+ }
652
+ return { error: "invalid_json" };
653
+ };
654
+ const ARCHITECT_THINK_PATTERN = /<\/?think\b[^>]*>/i;
655
+ const ARCHITECT_FENCE_PATTERN = /```/;
656
+ const ARCHITECT_THINK_BLOCK_PATTERN = /<think\b[^>]*>[\s\S]*?<\/think>/gi;
657
+ const ARCHITECT_FENCE_BLOCK_PATTERN = /```[^\n]*\n?([\s\S]*?)```/g;
658
+ const ARCHITECT_PLAN_HEADER_PATTERN = /^\s*(PLAN|IMPLEMENTATION PLAN)\s*:?\s*/im;
659
+ const ARCHITECT_TARGETS_HEADER_PATTERN = /^\s*(TARGETS|FILES TO TOUCH)\s*:?\s*/im;
660
+ const ARCHITECT_RISK_HEADER_PATTERN = /^\s*RISK\s*:?\s*/im;
661
+ const ARCHITECT_VERIFY_HEADER_PATTERN = /^\s*(VERIFY|VERIFICATION|VALIDATE|VALIDATION)\s*:?\s*/im;
662
+ const ARCHITECT_REQUIRED_HEADER_PATTERN = /^\s*WHAT\s+IS\s+REQUIRED\s*:?\s*/im;
663
+ const ARCHITECT_CONTEXT_HEADER_PATTERN = /^\s*CURRENT\s+CONTEXT\s*:?\s*/im;
664
+ const ARCHITECT_FOLDER_HEADER_PATTERN = /^\s*FOLDER\s+STRUCTURE\s*:?\s*/im;
665
+ const countHeaderMatches = (pattern, content) => {
666
+ const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
667
+ const globalPattern = new RegExp(pattern.source, flags);
668
+ let count = 0;
669
+ while (globalPattern.exec(content))
670
+ count += 1;
671
+ return count;
672
+ };
673
+ const detectDslHeaders = (content) => ({
674
+ plan: ARCHITECT_PLAN_HEADER_PATTERN.test(content),
675
+ targets: ARCHITECT_TARGETS_HEADER_PATTERN.test(content),
676
+ risk: ARCHITECT_RISK_HEADER_PATTERN.test(content),
677
+ verify: ARCHITECT_VERIFY_HEADER_PATTERN.test(content),
678
+ required: ARCHITECT_REQUIRED_HEADER_PATTERN.test(content),
679
+ context: ARCHITECT_CONTEXT_HEADER_PATTERN.test(content),
680
+ folder: ARCHITECT_FOLDER_HEADER_PATTERN.test(content),
681
+ });
682
+ const detectDslHeaderCounts = (content) => ({
683
+ plan: countHeaderMatches(ARCHITECT_PLAN_HEADER_PATTERN, content),
684
+ targets: countHeaderMatches(ARCHITECT_TARGETS_HEADER_PATTERN, content),
685
+ risk: countHeaderMatches(ARCHITECT_RISK_HEADER_PATTERN, content),
686
+ verify: countHeaderMatches(ARCHITECT_VERIFY_HEADER_PATTERN, content),
687
+ required: countHeaderMatches(ARCHITECT_REQUIRED_HEADER_PATTERN, content),
688
+ context: countHeaderMatches(ARCHITECT_CONTEXT_HEADER_PATTERN, content),
689
+ folder: countHeaderMatches(ARCHITECT_FOLDER_HEADER_PATTERN, content),
690
+ });
691
+ const normalizeArchitectDslCandidate = (content) => {
692
+ const withoutThinkBlocks = content.replace(ARCHITECT_THINK_BLOCK_PATTERN, "\n");
693
+ const withoutThinkTags = withoutThinkBlocks.replace(ARCHITECT_THINK_PATTERN, "");
694
+ const unwrappedFences = withoutThinkTags.replace(ARCHITECT_FENCE_BLOCK_PATTERN, (_full, inner) => inner ?? "");
695
+ return unwrappedFences.trim();
696
+ };
697
+ const classifyArchitectOutput = (content) => {
698
+ const warnings = [];
699
+ const trimmed = content.trim();
700
+ if (!trimmed)
701
+ return ["architect_output_empty"];
702
+ // Treat <think> wrappers as ignorable noise; they should not trigger protocol warnings.
703
+ if (ARCHITECT_FENCE_PATTERN.test(trimmed)) {
704
+ warnings.push(ARCHITECT_WARNING_CONTAINS_FENCE, ARCHITECT_WARNING_NON_DSL);
705
+ }
706
+ const headers = detectDslHeaders(trimmed);
707
+ const headerCounts = detectDslHeaderCounts(trimmed);
708
+ const duplicateSections = Object.values(headerCounts).some((count) => count > 1);
709
+ if (duplicateSections) {
710
+ warnings.push(ARCHITECT_WARNING_MULTIPLE_SECTION_BLOCKS, ARCHITECT_WARNING_NON_DSL);
711
+ }
712
+ const hasStructuredSignal = headers.plan || headers.targets || headers.risk || headers.verify
713
+ || headers.required || headers.context || headers.folder;
714
+ const hasCoreSections = headers.plan && headers.risk && headers.verify;
715
+ const hasTargetSignal = headers.targets || headers.folder;
716
+ if (hasStructuredSignal && !(hasCoreSections && hasTargetSignal)) {
717
+ warnings.push(ARCHITECT_WARNING_MISSING_REQUIRED_SECTIONS, ARCHITECT_WARNING_NON_DSL);
718
+ }
719
+ else if (!hasStructuredSignal) {
720
+ const parsed = parseJsonLoose(trimmed);
721
+ const record = parsed.parsed && typeof parsed.parsed === "object" && !Array.isArray(parsed.parsed)
722
+ ? parsed.parsed
723
+ : undefined;
724
+ const hasPlanShape = Boolean(record &&
725
+ (Object.prototype.hasOwnProperty.call(record, "steps") ||
726
+ Object.prototype.hasOwnProperty.call(record, "target_files") ||
727
+ Object.prototype.hasOwnProperty.call(record, "risk_assessment") ||
728
+ Object.prototype.hasOwnProperty.call(record, "verification") ||
729
+ Object.prototype.hasOwnProperty.call(record, "plan")));
730
+ if (!hasPlanShape)
731
+ warnings.push(ARCHITECT_WARNING_NON_DSL);
732
+ }
733
+ return uniqueStrings(warnings);
734
+ };
735
+ const withRepairWarnings = (warnings, reason) => {
736
+ const repaired = [...warnings];
737
+ if (!repaired.includes(ARCHITECT_WARNING_REPAIRED)) {
738
+ repaired.push(ARCHITECT_WARNING_REPAIRED);
739
+ }
740
+ repaired.push(`architect_output_repair_reason:${reason}`);
741
+ return uniqueStrings(repaired);
742
+ };
743
+ const REQUIRED_MISSING_PLAN_WARNINGS = new Set([
744
+ "plan_missing_steps",
745
+ "plan_missing_target_files",
746
+ "plan_missing_risk_assessment",
747
+ "plan_missing_verification",
748
+ ]);
749
+ const PATH_CANDIDATE_INLINE_PATTERN = /^(?:[`'"])?([A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)(?:[`'"])?$/;
750
+ const extractPathCandidatesFromLines = (lines) => {
751
+ const candidates = [];
752
+ for (const line of lines) {
753
+ const fromPattern = line.match(REQUEST_FILE_PATTERN) ?? [];
754
+ if (fromPattern.length > 0) {
755
+ candidates.push(...fromPattern.map((entry) => normalizePath(entry)));
756
+ continue;
757
+ }
758
+ const compact = line
759
+ .replace(/\s+\(.*\)\s*$/g, "")
760
+ .replace(/^[`'"]+|[`'"]+$/g, "")
761
+ .trim();
762
+ if (!compact)
763
+ continue;
764
+ const inlineMatch = PATH_CANDIDATE_INLINE_PATTERN.exec(compact);
765
+ if (!inlineMatch)
766
+ continue;
767
+ const candidate = normalizePath(inlineMatch[1] ?? "");
768
+ if (candidate)
769
+ candidates.push(candidate);
770
+ }
771
+ return uniqueStrings(candidates);
772
+ };
773
+ const parsePlanDsl = (content, context) => {
774
+ const warnings = [];
775
+ const trimmed = content.trim();
776
+ if (!trimmed)
777
+ return { warnings: ["architect_output_empty"] };
778
+ const steps = [];
779
+ const requiredNotes = [];
780
+ const contextNotes = [];
781
+ const folderStructure = [];
782
+ const targets = [];
783
+ const createFiles = [];
784
+ const verification = [];
785
+ let risk;
786
+ let section;
787
+ const headerCounts = {
788
+ required: 0,
789
+ context: 0,
790
+ folder: 0,
791
+ plan: 0,
792
+ targets: 0,
793
+ create: 0,
794
+ risk: 0,
795
+ verify: 0,
796
+ };
797
+ let duplicateSectionDetected = false;
798
+ const commitItem = (items, line) => {
799
+ const cleaned = line.replace(/^\s*[-*•]\s*/, "").replace(/^\s*\d+[.)]\s*/, "").trim();
800
+ if (cleaned)
801
+ items.push(cleaned);
802
+ };
803
+ const lines = trimmed.split(/\r?\n/);
804
+ for (const rawLine of lines) {
805
+ const line = rawLine.trim();
806
+ if (!line)
807
+ continue;
808
+ const requiredMatch = /^WHAT\s+IS\s+REQUIRED\s*:?\s*(.*)$/i.exec(line);
809
+ if (requiredMatch) {
810
+ headerCounts.required += 1;
811
+ if (headerCounts.required > 1) {
812
+ duplicateSectionDetected = true;
813
+ section = undefined;
814
+ continue;
815
+ }
816
+ section = "required";
817
+ if (requiredMatch[1])
818
+ commitItem(requiredNotes, requiredMatch[1]);
819
+ continue;
820
+ }
821
+ const contextMatch = /^CURRENT\s+CONTEXT\s*:?\s*(.*)$/i.exec(line);
822
+ if (contextMatch) {
823
+ headerCounts.context += 1;
824
+ if (headerCounts.context > 1) {
825
+ duplicateSectionDetected = true;
826
+ section = undefined;
827
+ continue;
828
+ }
829
+ section = "context";
830
+ if (contextMatch[1])
831
+ commitItem(contextNotes, contextMatch[1]);
832
+ continue;
833
+ }
834
+ const folderMatch = /^FOLDER\s+STRUCTURE\s*:?\s*(.*)$/i.exec(line);
835
+ if (folderMatch) {
836
+ headerCounts.folder += 1;
837
+ if (headerCounts.folder > 1) {
838
+ duplicateSectionDetected = true;
839
+ section = undefined;
840
+ continue;
841
+ }
842
+ section = "folder";
843
+ if (folderMatch[1])
844
+ commitItem(folderStructure, folderMatch[1]);
845
+ continue;
846
+ }
847
+ const planMatch = /^(PLAN|IMPLEMENTATION PLAN)\s*:?\s*(.*)$/i.exec(line);
848
+ if (planMatch) {
849
+ headerCounts.plan += 1;
850
+ if (headerCounts.plan > 1) {
851
+ duplicateSectionDetected = true;
852
+ section = undefined;
853
+ continue;
854
+ }
855
+ section = "steps";
856
+ if (planMatch[2])
857
+ commitItem(steps, planMatch[2]);
858
+ continue;
859
+ }
860
+ const targetsMatch = /^(TARGETS|FILES TO TOUCH)\s*:?\s*(.*)$/i.exec(line);
861
+ if (targetsMatch) {
862
+ headerCounts.targets += 1;
863
+ if (headerCounts.targets > 1) {
864
+ duplicateSectionDetected = true;
865
+ section = undefined;
866
+ continue;
867
+ }
868
+ section = "targets";
869
+ if (targetsMatch[2])
870
+ commitItem(targets, targetsMatch[2]);
871
+ continue;
872
+ }
873
+ const createMatch = /^CREATE(?:_FILES?| FILES?)\s*:?\s*(.*)$/i.exec(line);
874
+ if (createMatch) {
875
+ headerCounts.create += 1;
876
+ if (headerCounts.create > 1) {
877
+ duplicateSectionDetected = true;
878
+ section = undefined;
879
+ continue;
880
+ }
881
+ section = "create";
882
+ if (createMatch[1])
883
+ commitItem(createFiles, createMatch[1]);
884
+ continue;
885
+ }
886
+ const riskMatch = /^RISK\s*:?\s*(.*)$/i.exec(line);
887
+ if (riskMatch) {
888
+ headerCounts.risk += 1;
889
+ if (headerCounts.risk > 1) {
890
+ duplicateSectionDetected = true;
891
+ section = undefined;
892
+ continue;
893
+ }
894
+ section = undefined;
895
+ risk = riskMatch[1]?.trim() || risk;
896
+ if (!risk)
897
+ section = "risk";
898
+ continue;
899
+ }
900
+ const verifyMatch = /^(VERIFY|VERIFICATION|VALIDATE|VALIDATION)\s*:?\s*(.*)$/i.exec(line);
901
+ if (verifyMatch) {
902
+ headerCounts.verify += 1;
903
+ if (headerCounts.verify > 1) {
904
+ duplicateSectionDetected = true;
905
+ section = undefined;
906
+ continue;
907
+ }
908
+ section = "verify";
909
+ if (verifyMatch[2])
910
+ commitItem(verification, verifyMatch[2]);
911
+ continue;
912
+ }
913
+ if (section === "required") {
914
+ commitItem(requiredNotes, line);
915
+ continue;
916
+ }
917
+ if (section === "context") {
918
+ commitItem(contextNotes, line);
919
+ continue;
920
+ }
921
+ if (section === "folder") {
922
+ commitItem(folderStructure, line);
923
+ continue;
924
+ }
925
+ if (section === "steps") {
926
+ commitItem(steps, line);
927
+ continue;
928
+ }
929
+ if (section === "targets") {
930
+ commitItem(targets, line);
931
+ continue;
932
+ }
933
+ if (section === "create") {
934
+ commitItem(createFiles, line);
935
+ continue;
936
+ }
937
+ if (section === "verify") {
938
+ commitItem(verification, line);
939
+ continue;
940
+ }
941
+ if (section === "risk") {
942
+ risk = risk ? `${risk} ${line}` : line;
943
+ }
944
+ }
945
+ if (steps.length === 0)
946
+ warnings.push("plan_missing_steps");
947
+ if (targets.length === 0)
948
+ warnings.push("plan_missing_target_files");
949
+ if (!risk)
950
+ warnings.push("plan_missing_risk_assessment");
951
+ if (verification.length === 0)
952
+ warnings.push("plan_missing_verification");
953
+ if (duplicateSectionDetected) {
954
+ warnings.push(ARCHITECT_WARNING_MULTIPLE_SECTION_BLOCKS, ARCHITECT_WARNING_NON_DSL);
955
+ }
956
+ if (steps.length === 0 && targets.length === 0 && !risk) {
957
+ warnings.push(ARCHITECT_WARNING_NON_DSL);
958
+ return { warnings };
959
+ }
960
+ const inferredTargets = targets.length > 0
961
+ ? targets
962
+ : extractPathCandidatesFromLines(folderStructure);
963
+ if (targets.length === 0 && inferredTargets.length > 0) {
964
+ warnings.push("plan_targets_inferred_from_folder_structure");
965
+ }
966
+ const resolvedSteps = steps.length > 0
967
+ ? steps
968
+ : (requiredNotes.length > 0 ? requiredNotes : contextNotes);
969
+ if (steps.length === 0 && requiredNotes.length > 0) {
970
+ warnings.push("plan_steps_inferred_from_requirements");
971
+ }
972
+ else if (steps.length === 0 && requiredNotes.length === 0 && contextNotes.length > 0) {
973
+ warnings.push("plan_steps_inferred_from_current_context");
974
+ }
975
+ const plan = {
976
+ steps: resolvedSteps.length > 0 ? resolvedSteps : fallbackSteps(context),
977
+ target_files: inferredTargets.length > 0 ? inferredTargets : deriveFallbackTargetFiles(context),
978
+ create_files: createFiles.length > 0 ? uniqueStrings(createFiles.map((entry) => normalizePath(entry))) : undefined,
979
+ risk_assessment: risk && risk.length > 0 ? risk : "medium: fallback plan generated from context",
980
+ verification: (() => {
981
+ const resolvedTargets = inferredTargets.length > 0
982
+ ? inferredTargets
983
+ : deriveFallbackTargetFiles(context);
984
+ const explicitCreateTargets = createFiles.length > 0
985
+ ? uniqueStrings(createFiles.map((entry) => normalizePath(entry)))
986
+ : [];
987
+ const verificationCreateTargets = explicitCreateTargets.length > 0
988
+ ? explicitCreateTargets
989
+ : computeCreateTargets(context, resolvedTargets);
990
+ return normalizeVerificationSteps(context, resolvedTargets, verificationCreateTargets, verification);
991
+ })(),
992
+ };
993
+ return { plan, warnings };
994
+ };
995
+ const parsePlanOutput = (content, context) => {
996
+ const classifiedWarnings = classifyArchitectOutput(content);
997
+ const normalizedContent = normalizeArchitectDslCandidate(content);
998
+ const dslResult = parsePlanDsl(normalizedContent, context);
999
+ if (dslResult.plan) {
1000
+ const enriched = enrichPlanWithTargetChangeDetails(context, dslResult.plan);
1001
+ const scoped = applyTargetScopeGuard(context, enriched.plan, uniqueStrings([...classifiedWarnings, ...dslResult.warnings, ...enriched.warnings]));
1002
+ let warnings = scoped.warnings;
1003
+ const hasMissingFields = warnings.some((warning) => REQUIRED_MISSING_PLAN_WARNINGS.has(warning));
1004
+ const hasNonDslWarning = warnings.includes(ARCHITECT_WARNING_NON_DSL);
1005
+ const hasDuplicateSections = warnings.includes(ARCHITECT_WARNING_MULTIPLE_SECTION_BLOCKS);
1006
+ const hasHardProtocolIssue = warnings.includes(ARCHITECT_WARNING_MISSING_REQUIRED_SECTIONS);
1007
+ if (hasMissingFields) {
1008
+ warnings = withRepairWarnings(warnings, "dsl_missing_fields");
1009
+ }
1010
+ else if (hasNonDslWarning) {
1011
+ if (hasHardProtocolIssue) {
1012
+ warnings = withRepairWarnings(warnings, "classifier");
1013
+ }
1014
+ else if (hasDuplicateSections) {
1015
+ warnings = withRepairWarnings(warnings.filter((warning) => warning !== ARCHITECT_WARNING_NON_DSL), "duplicate_sections");
1016
+ }
1017
+ else {
1018
+ warnings = withRepairWarnings(warnings.filter((warning) => warning !== ARCHITECT_WARNING_NON_DSL), "wrapper_noise");
1019
+ }
1020
+ }
1021
+ return {
1022
+ plan: scoped.plan,
1023
+ warnings,
1024
+ };
1025
+ }
1026
+ const parsedResult = parseJsonLoose(normalizedContent);
1027
+ const { plan, warnings } = coercePlan(parsedResult.parsed, context);
1028
+ const enriched = enrichPlanWithTargetChangeDetails(context, plan);
1029
+ const scoped = applyTargetScopeGuard(context, enriched.plan, [...warnings, ...enriched.warnings]);
1030
+ const fallbackDslWarnings = dslResult.warnings.filter((warning) => !REQUIRED_MISSING_PLAN_WARNINGS.has(warning));
1031
+ const usedJsonFallback = parsedResult.parsed !== undefined;
1032
+ const repairedWarnings = withRepairWarnings([
1033
+ ...classifiedWarnings,
1034
+ ...fallbackDslWarnings,
1035
+ ...scoped.warnings,
1036
+ usedJsonFallback
1037
+ ? ARCHITECT_WARNING_USED_JSON_FALLBACK
1038
+ : ARCHITECT_WARNING_USED_PLAINTEXT_FALLBACK,
1039
+ ], usedJsonFallback ? "json_fallback" : "plaintext_fallback");
1040
+ return {
1041
+ plan: scoped.plan,
1042
+ warnings: repairedWarnings,
1043
+ parseError: parsedResult.error,
1044
+ };
1045
+ };
1046
+ const asRecord = (value) => {
1047
+ if (!value || typeof value !== "object" || Array.isArray(value))
1048
+ return undefined;
1049
+ return value;
1050
+ };
1051
+ const extractStringField = (record, keys) => {
1052
+ for (const key of keys) {
1053
+ const value = record[key];
1054
+ if (typeof value === "string" && value.trim().length > 0) {
1055
+ return value.trim();
1056
+ }
1057
+ }
1058
+ return undefined;
1059
+ };
1060
+ const isPlanLikeObject = (record) => {
1061
+ const planCandidate = record.plan;
1062
+ if (planCandidate && typeof planCandidate === "object" && !Array.isArray(planCandidate)) {
1063
+ return true;
1064
+ }
1065
+ return (Object.prototype.hasOwnProperty.call(record, "steps") ||
1066
+ Object.prototype.hasOwnProperty.call(record, "target_files") ||
1067
+ Object.prototype.hasOwnProperty.call(record, "risk_assessment") ||
1068
+ Object.prototype.hasOwnProperty.call(record, "verification"));
1069
+ };
1070
+ const buildRequestId = (prefix) => `${prefix}-${Date.now()}`;
1071
+ const sanitizeRecoveryTokens = (tokens) => tokens
1072
+ .map((token) => token.replace(/^[\/_-]+|[\/_-]+$/g, ""))
1073
+ .filter((token) => token.length >= 3)
1074
+ .filter((token) => !REQUEST_TOKEN_STOPWORDS.has(token))
1075
+ .filter((token) => !PROSE_RECOVERY_STOPWORDS.has(token))
1076
+ .filter((token) => /[a-z]/.test(token));
1077
+ const buildProseRecoveryQuery = (content, request) => {
1078
+ const requestTokens = sanitizeRecoveryTokens(extractRequestTokens(request)).slice(0, 6);
1079
+ const proseTokens = sanitizeRecoveryTokens(extractRequestTokens(content))
1080
+ .filter((token) => !requestTokens.includes(token))
1081
+ .slice(0, 6);
1082
+ const combined = uniqueStrings([...requestTokens, ...proseTokens]).slice(0, 8);
1083
+ if (combined.length > 0)
1084
+ return combined.join(" ");
1085
+ const compactRequest = compactWhitespace(request);
1086
+ if (compactRequest.length > 0)
1087
+ return compactRequest;
1088
+ return "implementation context";
1089
+ };
1090
+ const shouldAdaptProseNonDslOutput = (content) => {
1091
+ const normalized = normalizeArchitectDslCandidate(content);
1092
+ if (!normalized)
1093
+ return false;
1094
+ const headers = detectDslHeaders(normalized);
1095
+ if (headers.plan || headers.targets || headers.risk || headers.verify)
1096
+ return false;
1097
+ const parsed = parseJsonLoose(normalized);
1098
+ if (parsed.parsed !== undefined)
1099
+ return false;
1100
+ if (normalized.length < 24)
1101
+ return false;
1102
+ const sentenceSignals = /[.!?]/.test(normalized) ||
1103
+ /\b(i|we|you|please|should|need|implement|update|add|create|develop|explain)\b/i.test(normalized);
1104
+ return sentenceSignals;
1105
+ };
1106
+ const adaptNonDslPayloadToRequest = (content, context) => {
1107
+ const parsed = parseJsonLoose(content);
1108
+ const record = asRecord(parsed.parsed);
1109
+ if (record && isPlanLikeObject(record))
1110
+ return undefined;
1111
+ if (record) {
1112
+ const query = extractStringField(record, ["query", "search_query", "question", "prompt"]);
1113
+ if (query) {
1114
+ return {
1115
+ source: "query_json",
1116
+ request: {
1117
+ version: "v1",
1118
+ role: "architect",
1119
+ request_id: buildRequestId("architect-adapt-query"),
1120
+ needs: [{ type: "docdex.search", query, limit: 8 }],
1121
+ context: {
1122
+ summary: "Architect returned a query payload instead of a plain-text plan. Fetch focused retrieval context and retry planning.",
1123
+ },
1124
+ },
1125
+ };
1126
+ }
1127
+ const file = extractStringField(record, ["file", "path", "target_file", "target_path"]);
1128
+ if (file) {
1129
+ const normalizedFile = normalizePath(file);
1130
+ const symbol = extractStringField(record, ["symbol", "symbol_id", "symbol_name"]);
1131
+ const needs = [{ type: "file.read", path: normalizedFile }];
1132
+ if (symbol) {
1133
+ needs.push({
1134
+ type: "docdex.search",
1135
+ query: `${normalizedFile} ${symbol}`,
1136
+ limit: 8,
1137
+ });
1138
+ }
1139
+ return {
1140
+ source: "file_json",
1141
+ request: {
1142
+ version: "v1",
1143
+ role: "architect",
1144
+ request_id: buildRequestId("architect-adapt-file"),
1145
+ needs,
1146
+ context: {
1147
+ summary: "Architect returned file/symbol metadata instead of a plain-text plan. Load file context and related symbol evidence before retrying.",
1148
+ },
1149
+ },
1150
+ };
1151
+ }
1152
+ const symbolOnly = extractStringField(record, ["symbol", "symbol_id", "symbol_name"]);
1153
+ if (symbolOnly) {
1154
+ return {
1155
+ source: "symbol_json",
1156
+ request: {
1157
+ version: "v1",
1158
+ role: "architect",
1159
+ request_id: buildRequestId("architect-adapt-symbol"),
1160
+ needs: [{ type: "docdex.search", query: `${context.request} ${symbolOnly}`, limit: 8 }],
1161
+ context: {
1162
+ summary: "Architect returned symbol metadata without a plain-text plan. Retrieve symbol-related context and retry planning.",
1163
+ },
1164
+ },
1165
+ };
1166
+ }
1167
+ }
1168
+ if (shouldAdaptProseNonDslOutput(content)) {
1169
+ const query = buildProseRecoveryQuery(content, context.request ?? "");
1170
+ return {
1171
+ source: "prose",
1172
+ request: {
1173
+ version: "v1",
1174
+ role: "architect",
1175
+ request_id: buildRequestId("architect-adapt-prose"),
1176
+ needs: [
1177
+ { type: "docdex.search", query, limit: 8 },
1178
+ { type: "file.list", root: "src", pattern: "*" },
1179
+ { type: "file.list", root: "docs", pattern: "*" },
1180
+ ],
1181
+ context: {
1182
+ summary: "Architect returned unstructured prose output. Retrieve focused implementation context and retry with strict plain-text sections.",
1183
+ },
1184
+ },
1185
+ };
1186
+ }
1187
+ return undefined;
1188
+ };
1189
+ const hasConcretePlanTargets = (plan) => uniqueStrings(plan.target_files
1190
+ .map((entry) => normalizePath(entry))
1191
+ .filter((entry) => entry.length > 0 && entry !== "unknown")).length > 0;
1192
+ const shouldKeepProseRecoveryRequest = (plan, warnings) => {
1193
+ if (!hasConcretePlanTargets(plan))
1194
+ return true;
1195
+ return warnings.some((warning) => REQUIRED_MISSING_PLAN_WARNINGS.has(warning));
1196
+ };
1197
+ const parsePlanHint = (hint, context) => {
1198
+ const dslResult = parsePlanDsl(hint, context);
1199
+ if (dslResult.plan) {
1200
+ const enriched = enrichPlanWithTargetChangeDetails(context, dslResult.plan);
1201
+ const scoped = applyTargetScopeGuard(context, enriched.plan, [...dslResult.warnings, ...enriched.warnings]);
1202
+ return {
1203
+ plan: scoped.plan,
1204
+ warnings: scoped.warnings,
1205
+ };
1206
+ }
1207
+ const parsedResult = parseJsonLoose(hint);
1208
+ if (parsedResult.parsed && typeof parsedResult.parsed === "object") {
1209
+ const { plan, warnings } = coercePlan(parsedResult.parsed, context);
1210
+ const enriched = enrichPlanWithTargetChangeDetails(context, plan);
1211
+ const scoped = applyTargetScopeGuard(context, enriched.plan, [...warnings, ...enriched.warnings]);
1212
+ return {
1213
+ plan: scoped.plan,
1214
+ warnings: [...dslResult.warnings, ...scoped.warnings],
1215
+ parseError: parsedResult.error,
1216
+ };
1217
+ }
1218
+ return { warnings: [...dslResult.warnings, "plan_hint_not_parseable"], parseError: parsedResult.error };
1219
+ };
1220
+ const parsePlanHintForValidation = (hint, context) => {
1221
+ const parsedResult = parseJsonLoose(hint);
1222
+ if (parsedResult.parsed && typeof parsedResult.parsed === "object") {
1223
+ const { plan, warnings } = coercePlan(parsedResult.parsed, context);
1224
+ const enriched = enrichPlanWithTargetChangeDetails(context, plan);
1225
+ const scopeAssessment = assessPlanTargetScope(context, enriched.plan);
1226
+ const scopeWarnings = scopeAssessment.invalidTargets.length > 0
1227
+ ? [`plan_targets_outside_context:${scopeAssessment.invalidTargets.join(",")}`]
1228
+ : [];
1229
+ return {
1230
+ plan: enriched.plan,
1231
+ warnings: [...warnings, ...enriched.warnings, ...scopeWarnings],
1232
+ parseError: parsedResult.error,
1233
+ };
1234
+ }
1235
+ const dslResult = parsePlanDsl(hint, context);
1236
+ if (dslResult.plan) {
1237
+ const enriched = enrichPlanWithTargetChangeDetails(context, dslResult.plan);
1238
+ const scopeAssessment = assessPlanTargetScope(context, enriched.plan);
1239
+ const scopeWarnings = scopeAssessment.invalidTargets.length > 0
1240
+ ? [`plan_targets_outside_context:${scopeAssessment.invalidTargets.join(",")}`]
1241
+ : [];
1242
+ return {
1243
+ plan: enriched.plan,
1244
+ warnings: [...dslResult.warnings, ...enriched.warnings, ...scopeWarnings],
1245
+ };
1246
+ }
1247
+ return { warnings: [...dslResult.warnings, "plan_hint_not_parseable"], parseError: parsedResult.error };
1248
+ };
1249
+ const PLAN_HINT_BLOCKING_WARNING_PREFIXES = ["plan_missing_steps", "plan_missing_target_files", "plan_missing_risk_assessment"];
1250
+ const PLAN_HINT_PLACEHOLDER_TARGET_PATTERNS = [/^unknown$/i, /^path\/to\//i, /^<.+>$/];
1251
+ const validatePlanHintPlan = (plan, warnings, context) => {
1252
+ const issues = [];
1253
+ const normalizedTargets = uniqueStrings(plan.target_files.map((value) => normalizePath(value)).filter(Boolean));
1254
+ if (plan.steps.length === 0)
1255
+ issues.push("plan_hint_missing_steps");
1256
+ if (normalizedTargets.length === 0)
1257
+ issues.push("plan_hint_missing_targets");
1258
+ if (!plan.risk_assessment.trim())
1259
+ issues.push("plan_hint_missing_risk");
1260
+ const invalidTargets = normalizedTargets.filter((target) => PLAN_HINT_PLACEHOLDER_TARGET_PATTERNS.some((pattern) => pattern.test(target)));
1261
+ if (invalidTargets.length > 0) {
1262
+ issues.push(`plan_hint_invalid_targets:${invalidTargets.join(",")}`);
1263
+ }
1264
+ const scopeAssessment = assessPlanTargetScope(context, plan);
1265
+ if (scopeAssessment.invalidTargets.length > 0) {
1266
+ issues.push(`plan_hint_targets_outside_context:${scopeAssessment.invalidTargets.join(",")}`);
1267
+ }
1268
+ const blockingWarnings = warnings.filter((warning) => PLAN_HINT_BLOCKING_WARNING_PREFIXES.some((prefix) => warning.startsWith(prefix)));
1269
+ if (blockingWarnings.length > 0) {
1270
+ issues.push("plan_hint_missing_required_fields");
1271
+ }
1272
+ return { issues: uniqueStrings(issues), blockingWarnings };
1273
+ };
1274
+ export class PlanHintValidationError extends Error {
1275
+ constructor(input) {
1276
+ super(input.message);
1277
+ this.name = "PlanHintValidationError";
1278
+ this.code = "plan_hint_validation_failed";
1279
+ this.warnings = input.warnings;
1280
+ this.issues = input.issues;
1281
+ this.parseError = input.parseError;
1282
+ }
1283
+ }
1284
+ const parseReviewDsl = (content) => {
1285
+ const warnings = [];
1286
+ const trimmed = content.trim();
1287
+ if (!trimmed)
1288
+ return { warnings: ["architect_review_empty"], feedback: [], reasons: [] };
1289
+ const lines = trimmed.split(/\r?\n/);
1290
+ let status;
1291
+ const feedback = [];
1292
+ const reasons = [];
1293
+ let section;
1294
+ let hasExplicitFeedback = false;
1295
+ for (const rawLine of lines) {
1296
+ const line = rawLine.trim();
1297
+ if (!line)
1298
+ continue;
1299
+ const statusMatch = /^STATUS\s*:\s*(PASS|RETRY)\b/i.exec(line)
1300
+ ?? /^RESULT\s*:\s*(PASS|RETRY)\b/i.exec(line)
1301
+ ?? /^(PASS|RETRY)\b/i.exec(line);
1302
+ if (statusMatch) {
1303
+ status = statusMatch[1].toUpperCase();
1304
+ continue;
1305
+ }
1306
+ if (/^REVIEW\s*:?\s*$/i.test(line)) {
1307
+ continue;
1308
+ }
1309
+ const reasonHeaderMatch = /^(REASONS?|ISSUES?|WHY)\s*:?\s*(.*)$/i.exec(line);
1310
+ if (reasonHeaderMatch) {
1311
+ section = "reasons";
1312
+ if (reasonHeaderMatch[2]) {
1313
+ reasons.push(reasonHeaderMatch[2].trim());
1314
+ }
1315
+ continue;
1316
+ }
1317
+ const feedbackHeaderMatch = /^(FEEDBACK|FIXES?|ACTIONS?|NEXT STEPS?)\s*:?\s*(.*)$/i.exec(line);
1318
+ if (feedbackHeaderMatch) {
1319
+ section = "feedback";
1320
+ if (feedbackHeaderMatch[2]) {
1321
+ feedback.push(feedbackHeaderMatch[2].trim());
1322
+ hasExplicitFeedback = true;
1323
+ }
1324
+ continue;
1325
+ }
1326
+ const cleaned = line.replace(/^\s*[-*•]\s*/, "").trim();
1327
+ if (!cleaned)
1328
+ continue;
1329
+ if (section === "reasons") {
1330
+ reasons.push(cleaned);
1331
+ continue;
1332
+ }
1333
+ if (section === "feedback") {
1334
+ feedback.push(cleaned);
1335
+ hasExplicitFeedback = true;
1336
+ continue;
1337
+ }
1338
+ if (!section && status === "RETRY") {
1339
+ if (/\b(fix|update|change|add|remove|ensure|align|address)\b/i.test(cleaned)) {
1340
+ feedback.push(cleaned);
1341
+ hasExplicitFeedback = true;
1342
+ }
1343
+ else {
1344
+ reasons.push(cleaned);
1345
+ }
1346
+ }
1347
+ }
1348
+ if (!status)
1349
+ warnings.push("architect_review_missing_status");
1350
+ if (status === "PASS" && reasons.length === 0) {
1351
+ reasons.push("Builder output satisfies the request and plan constraints.");
1352
+ }
1353
+ if (status === "RETRY" && !hasExplicitFeedback) {
1354
+ warnings.push("architect_review_retry_missing_feedback");
1355
+ }
1356
+ if (status === "RETRY" && feedback.length === 0 && reasons.length > 0) {
1357
+ for (const reason of reasons.slice(0, 6)) {
1358
+ feedback.push(`Address: ${reason}`);
1359
+ }
1360
+ }
1361
+ if (reasons.length === 0) {
1362
+ warnings.push("architect_review_missing_reasons");
1363
+ }
1364
+ return { status, feedback, reasons, warnings };
1365
+ };
1366
+ const REVIEW_ACTIONABLE_PATTERN = /\b(fix|update|change|add|remove|ensure|align|address|correct)\b/i;
1367
+ const isActionableReviewFeedback = (parsed) => {
1368
+ if (parsed.feedback.length > 0)
1369
+ return true;
1370
+ return parsed.reasons.some((entry) => REVIEW_ACTIONABLE_PATTERN.test(entry));
1371
+ };
1372
+ export class ArchitectPlanner {
1373
+ constructor(provider, options = {}) {
1374
+ this.provider = provider;
1375
+ this.temperature = options.temperature;
1376
+ this.logger = options.logger;
1377
+ this.contextManager = options.contextManager;
1378
+ this.laneId = options.laneId;
1379
+ this.model = options.model;
1380
+ this.responseFormat = options.responseFormat;
1381
+ this.planHint = options.planHint;
1382
+ this.instructionHint = options.instructionHint;
1383
+ this.validatePlanHint = options.validatePlanHint;
1384
+ this.validateOnly = options.validateOnly;
1385
+ this.stream = options.stream;
1386
+ this.onEvent = options.onEvent;
1387
+ }
1388
+ async plan(context, options = {}) {
1389
+ const result = await this.planWithRequest(context, options);
1390
+ return result.plan;
1391
+ }
1392
+ async planWithRequest(context, options = {}) {
1393
+ const hasPlanHintOverride = Object.prototype.hasOwnProperty.call(options, "planHint");
1394
+ const hasInstructionHintOverride = Object.prototype.hasOwnProperty.call(options, "instructionHint");
1395
+ const contextManager = options.contextManager ?? this.contextManager;
1396
+ const laneId = options.laneId ?? this.laneId;
1397
+ const model = options.model ?? this.model;
1398
+ const planHint = hasPlanHintOverride ? options.planHint : this.planHint;
1399
+ const instructionHint = hasInstructionHintOverride
1400
+ ? options.instructionHint
1401
+ : this.instructionHint;
1402
+ const validatePlanHint = options.validatePlanHint ?? this.validatePlanHint;
1403
+ const validateOnly = options.validateOnly ?? this.validateOnly;
1404
+ const stream = options.stream ?? this.stream;
1405
+ const onEvent = options.onEvent ?? this.onEvent;
1406
+ const requestedFormat = options.responseFormat ?? this.responseFormat;
1407
+ const responseFormat = requestedFormat?.type === "gbnf" && !requestedFormat.grammar
1408
+ ? undefined
1409
+ : requestedFormat;
1410
+ if (validateOnly && planHint) {
1411
+ const hintParsed = parsePlanHintForValidation(planHint, context);
1412
+ if (!hintParsed.plan) {
1413
+ throw new PlanHintValidationError({
1414
+ message: "Plan hint validation failed: plan hint is not parseable.",
1415
+ warnings: hintParsed.warnings,
1416
+ issues: ["plan_hint_not_parseable"],
1417
+ parseError: hintParsed.parseError,
1418
+ });
1419
+ }
1420
+ const validation = validatePlanHintPlan(hintParsed.plan, hintParsed.warnings, context);
1421
+ if (validation.issues.length > 0) {
1422
+ throw new PlanHintValidationError({
1423
+ message: "Plan hint validation failed: required fields or targets are invalid.",
1424
+ warnings: hintParsed.warnings,
1425
+ issues: validation.issues,
1426
+ parseError: hintParsed.parseError,
1427
+ });
1428
+ }
1429
+ if (this.logger) {
1430
+ await this.logger.log("architect_plan_hint_validated", {
1431
+ mode: "validate_only",
1432
+ steps: hintParsed.plan.steps.length,
1433
+ target_files: hintParsed.plan.target_files.length,
1434
+ warnings: hintParsed.warnings,
1435
+ });
1436
+ }
1437
+ return {
1438
+ plan: hintParsed.plan,
1439
+ raw: planHint,
1440
+ warnings: [],
1441
+ };
1442
+ }
1443
+ if (planHint) {
1444
+ const hintParsed = parsePlanHint(planHint, context);
1445
+ if (hintParsed.plan) {
1446
+ if (hintParsed.warnings.length && this.logger) {
1447
+ await this.logger.log("architect_plan_hint_normalized", {
1448
+ warnings: hintParsed.warnings,
1449
+ parseError: hintParsed.parseError,
1450
+ });
1451
+ }
1452
+ if (this.logger) {
1453
+ await this.logger.log("architect_plan_hint_used", {
1454
+ hasWarnings: hintParsed.warnings.length > 0,
1455
+ validated: !!validatePlanHint,
1456
+ });
1457
+ }
1458
+ if (validatePlanHint) {
1459
+ return this.validatePlanWithProvider(hintParsed.plan, context, {
1460
+ contextManager,
1461
+ laneId,
1462
+ model,
1463
+ stream,
1464
+ onEvent,
1465
+ });
1466
+ }
1467
+ return {
1468
+ plan: hintParsed.plan,
1469
+ raw: planHint,
1470
+ warnings: hintParsed.warnings,
1471
+ };
1472
+ }
1473
+ }
1474
+ const promptSections = [ARCHITECT_PROMPT];
1475
+ if (instructionHint?.trim()) {
1476
+ promptSections.push("ADDITIONAL ARCHITECT INSTRUCTIONS:");
1477
+ promptSections.push(instructionHint.trim());
1478
+ }
1479
+ if (planHint) {
1480
+ promptSections.push("PLAN HINT (must follow):");
1481
+ promptSections.push(planHint);
1482
+ }
1483
+ const systemPrompt = promptSections.join("\n");
1484
+ const systemMessage = { role: "system", content: systemPrompt };
1485
+ const userMessage = buildUserMessage(context);
1486
+ const history = contextManager && laneId
1487
+ ? await contextManager.prepare(laneId, {
1488
+ systemPrompt: systemMessage.content,
1489
+ bundle: userMessage.content,
1490
+ model,
1491
+ })
1492
+ : [];
1493
+ let response;
1494
+ try {
1495
+ onEvent?.({ type: "status", phase: "thinking", message: "architect" });
1496
+ if (this.logger) {
1497
+ await this.logger.log("provider_request", {
1498
+ provider: this.provider.name,
1499
+ model,
1500
+ messages: [systemMessage, ...history, userMessage],
1501
+ responseFormat,
1502
+ temperature: this.temperature,
1503
+ stream: stream ?? false,
1504
+ });
1505
+ }
1506
+ response = await this.provider.generate({
1507
+ messages: [
1508
+ systemMessage,
1509
+ ...history,
1510
+ userMessage,
1511
+ ],
1512
+ responseFormat,
1513
+ temperature: this.temperature,
1514
+ stream,
1515
+ onEvent,
1516
+ });
1517
+ onEvent?.({ type: "status", phase: "done", message: "architect" });
1518
+ }
1519
+ catch (error) {
1520
+ onEvent?.({
1521
+ type: "error",
1522
+ message: error instanceof Error ? error.message : String(error),
1523
+ });
1524
+ throw error;
1525
+ }
1526
+ if (response.usage && this.logger) {
1527
+ await this.logger.log("phase_usage", { phase: "architect", usage: response.usage });
1528
+ }
1529
+ const content = response.message.content?.trim() ?? "";
1530
+ const parsedPlan = parsePlanOutput(content, context);
1531
+ const { plan } = parsedPlan;
1532
+ const warnings = [...parsedPlan.warnings];
1533
+ if (warnings.length && this.logger) {
1534
+ await this.logger.log("architect_plan_normalized", {
1535
+ warnings,
1536
+ parseError: parsedPlan.parseError,
1537
+ });
1538
+ }
1539
+ if (contextManager && laneId) {
1540
+ await contextManager.append(laneId, userMessage, { role: "architect", model });
1541
+ await contextManager.append(laneId, response.message, {
1542
+ role: "architect",
1543
+ model,
1544
+ tokens: response.usage?.totalTokens,
1545
+ });
1546
+ }
1547
+ let request;
1548
+ try {
1549
+ request = parseAgentRequest(content);
1550
+ }
1551
+ catch {
1552
+ request = undefined;
1553
+ }
1554
+ if (!request) {
1555
+ const adaptedRequest = adaptNonDslPayloadToRequest(content, context);
1556
+ const useAdaptedRequest = adaptedRequest
1557
+ ? adaptedRequest.source !== "prose"
1558
+ || shouldKeepProseRecoveryRequest(plan, warnings)
1559
+ : false;
1560
+ if (adaptedRequest && useAdaptedRequest) {
1561
+ request = adaptedRequest.request;
1562
+ warnings.push("architect_output_adapted_to_request");
1563
+ }
1564
+ else if (adaptedRequest && adaptedRequest.source === "prose") {
1565
+ warnings.push("architect_output_prose_request_suppressed");
1566
+ }
1567
+ }
1568
+ if (request && this.logger) {
1569
+ await this.logger.log("architect_request_detected", {
1570
+ request_id: request.request_id,
1571
+ needs: request.needs.length,
1572
+ });
1573
+ }
1574
+ return { plan, request, raw: content, warnings: uniqueStrings(warnings) };
1575
+ }
1576
+ async reviewBuilderOutput(plan, builderOutput, context, options = {}) {
1577
+ const contextManager = options.contextManager ?? this.contextManager;
1578
+ const laneId = options.laneId ?? this.laneId;
1579
+ const model = options.model ?? this.model;
1580
+ const stream = options.stream ?? this.stream;
1581
+ const onEvent = options.onEvent ?? this.onEvent;
1582
+ const requestedFormat = options.responseFormat ?? this.responseFormat;
1583
+ const responseFormat = requestedFormat?.type === "gbnf" && !requestedFormat.grammar
1584
+ ? undefined
1585
+ : requestedFormat;
1586
+ const systemMessage = { role: "system", content: ARCHITECT_REVIEW_PROMPT };
1587
+ const contextContent = buildContextNarrative(context);
1588
+ const userMessage = {
1589
+ role: "user",
1590
+ content: [
1591
+ "PLAN (read-only):",
1592
+ JSON.stringify(plan, null, 2),
1593
+ "",
1594
+ "BUILDER OUTPUT:",
1595
+ builderOutput,
1596
+ "",
1597
+ "CONTEXT (read-only):",
1598
+ contextContent,
1599
+ ].join("\n"),
1600
+ };
1601
+ const history = contextManager && laneId
1602
+ ? await contextManager.prepare(laneId, {
1603
+ systemPrompt: systemMessage.content,
1604
+ bundle: userMessage.content,
1605
+ model,
1606
+ })
1607
+ : [];
1608
+ onEvent?.({ type: "status", phase: "thinking", message: "architect_review" });
1609
+ if (this.logger) {
1610
+ await this.logger.log("provider_request", {
1611
+ provider: this.provider.name,
1612
+ model,
1613
+ messages: [systemMessage, ...history, userMessage],
1614
+ responseFormat,
1615
+ temperature: this.temperature,
1616
+ stream: stream ?? false,
1617
+ });
1618
+ }
1619
+ const response = await this.provider.generate({
1620
+ messages: [systemMessage, ...history, userMessage],
1621
+ responseFormat,
1622
+ temperature: this.temperature,
1623
+ stream,
1624
+ onEvent,
1625
+ });
1626
+ onEvent?.({ type: "status", phase: "done", message: "architect_review" });
1627
+ if (response.usage && this.logger) {
1628
+ await this.logger.log("phase_usage", { phase: "architect_review", usage: response.usage });
1629
+ }
1630
+ const raw = response.message.content?.trim() ?? "";
1631
+ const parsed = parseReviewDsl(raw);
1632
+ const status = parsed.status ?? (isActionableReviewFeedback(parsed) ? "RETRY" : "PASS");
1633
+ const warnings = parsed.warnings;
1634
+ const reasons = parsed.reasons.length > 0
1635
+ ? parsed.reasons
1636
+ : status === "PASS"
1637
+ ? ["Builder output satisfies the request and plan constraints."]
1638
+ : [];
1639
+ if (warnings.length && this.logger) {
1640
+ await this.logger.log("architect_review_normalized", {
1641
+ warnings,
1642
+ status,
1643
+ });
1644
+ }
1645
+ if (contextManager && laneId) {
1646
+ await contextManager.append(laneId, userMessage, { role: "architect", model });
1647
+ await contextManager.append(laneId, response.message, {
1648
+ role: "architect",
1649
+ model,
1650
+ tokens: response.usage?.totalTokens,
1651
+ });
1652
+ }
1653
+ return { status, feedback: parsed.feedback, reasons, raw, warnings };
1654
+ }
1655
+ async validatePlanWithProvider(plan, context, options = {}) {
1656
+ const contextManager = options.contextManager;
1657
+ const laneId = options.laneId;
1658
+ const model = options.model ?? this.model;
1659
+ const stream = options.stream;
1660
+ const onEvent = options.onEvent;
1661
+ const requestedFormat = this.responseFormat;
1662
+ const responseFormat = requestedFormat?.type === "gbnf" && !requestedFormat.grammar
1663
+ ? undefined
1664
+ : requestedFormat;
1665
+ const systemMessage = { role: "system", content: ARCHITECT_VALIDATE_PROMPT };
1666
+ const contextContent = buildContextNarrative(context);
1667
+ const userMessage = {
1668
+ role: "user",
1669
+ content: [
1670
+ "PROPOSED PLAN:",
1671
+ JSON.stringify(plan, null, 2),
1672
+ "",
1673
+ "CONTEXT:",
1674
+ contextContent,
1675
+ ].join("\n"),
1676
+ };
1677
+ const history = contextManager && laneId
1678
+ ? await contextManager.prepare(laneId, {
1679
+ systemPrompt: systemMessage.content,
1680
+ bundle: userMessage.content,
1681
+ model,
1682
+ })
1683
+ : [];
1684
+ onEvent?.({ type: "status", phase: "thinking", message: "architect_validate" });
1685
+ if (this.logger) {
1686
+ await this.logger.log("provider_request", {
1687
+ provider: this.provider.name,
1688
+ model,
1689
+ messages: [systemMessage, ...history, userMessage],
1690
+ responseFormat,
1691
+ temperature: this.temperature,
1692
+ stream: stream ?? false,
1693
+ });
1694
+ }
1695
+ const response = await this.provider.generate({
1696
+ messages: [systemMessage, ...history, userMessage],
1697
+ responseFormat,
1698
+ temperature: this.temperature,
1699
+ stream,
1700
+ onEvent,
1701
+ });
1702
+ onEvent?.({ type: "status", phase: "done", message: "architect_validate" });
1703
+ if (response.usage && this.logger) {
1704
+ await this.logger.log("phase_usage", { phase: "architect_validate", usage: response.usage });
1705
+ }
1706
+ const content = response.message.content?.trim() ?? "";
1707
+ const parsedPlan = parsePlanOutput(content, context);
1708
+ const { plan: validatedPlan, warnings } = parsedPlan;
1709
+ if (warnings.length && this.logger) {
1710
+ await this.logger.log("architect_plan_normalized", {
1711
+ warnings,
1712
+ parseError: parsedPlan.parseError,
1713
+ source: "validation",
1714
+ });
1715
+ }
1716
+ if (contextManager && laneId) {
1717
+ await contextManager.append(laneId, userMessage, { role: "architect", model });
1718
+ await contextManager.append(laneId, response.message, {
1719
+ role: "architect",
1720
+ model,
1721
+ tokens: response.usage?.totalTokens,
1722
+ });
1723
+ }
1724
+ return { plan: validatedPlan, raw: content, warnings };
1725
+ }
1726
+ }