@securecode-ai/mcp 0.5.5 → 0.5.7

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 (186) hide show
  1. package/README.md +52 -12
  2. package/dist/api/types.d.ts +7 -0
  3. package/dist/approval/auditLog.d.ts +1 -1
  4. package/dist/approval/auditLog.js +11 -3
  5. package/dist/approval/auditLog.js.map +1 -1
  6. package/dist/approval/broker.d.ts +7 -2
  7. package/dist/approval/broker.js +239 -110
  8. package/dist/approval/broker.js.map +1 -1
  9. package/dist/approval/policy.d.ts +10 -0
  10. package/dist/approval/policy.js +104 -0
  11. package/dist/approval/policy.js.map +1 -0
  12. package/dist/approval/types.d.ts +11 -2
  13. package/dist/approval/types.js +10 -1
  14. package/dist/approval/types.js.map +1 -1
  15. package/dist/attack/agentScanExecutor.d.ts +35 -1
  16. package/dist/attack/agentScanExecutor.js +405 -76
  17. package/dist/attack/agentScanExecutor.js.map +1 -1
  18. package/dist/attack/agentScanLoop.d.ts +20 -0
  19. package/dist/attack/agentScanLoop.js +1264 -60
  20. package/dist/attack/agentScanLoop.js.map +1 -1
  21. package/dist/attack/agentScanProtocol.d.ts +382 -6
  22. package/dist/attack/agentScanProtocol.js +110 -4
  23. package/dist/attack/agentScanProtocol.js.map +1 -1
  24. package/dist/attack/agentTrace.d.ts +73 -0
  25. package/dist/attack/agentTrace.js +228 -0
  26. package/dist/attack/agentTrace.js.map +1 -0
  27. package/dist/attack/architectureScoutExecutor.d.ts +18 -0
  28. package/dist/attack/architectureScoutExecutor.js +211 -0
  29. package/dist/attack/architectureScoutExecutor.js.map +1 -0
  30. package/dist/attack/architectureScoutLoop.d.ts +36 -0
  31. package/dist/attack/architectureScoutLoop.js +403 -0
  32. package/dist/attack/architectureScoutLoop.js.map +1 -0
  33. package/dist/attack/architectureScoutProtocol.d.ts +209 -0
  34. package/dist/attack/architectureScoutProtocol.js +47 -0
  35. package/dist/attack/architectureScoutProtocol.js.map +1 -0
  36. package/dist/attack/candidateStore.d.ts +95 -0
  37. package/dist/attack/candidateStore.js +231 -0
  38. package/dist/attack/candidateStore.js.map +1 -0
  39. package/dist/attack/evidenceLedger.d.ts +67 -0
  40. package/dist/attack/evidenceLedger.js +192 -0
  41. package/dist/attack/evidenceLedger.js.map +1 -0
  42. package/dist/attack/finishGate.d.ts +62 -0
  43. package/dist/attack/finishGate.js +209 -0
  44. package/dist/attack/finishGate.js.map +1 -0
  45. package/dist/attack/fixCodeMerge.d.ts +27 -0
  46. package/dist/attack/fixCodeMerge.js +42 -0
  47. package/dist/attack/fixCodeMerge.js.map +1 -0
  48. package/dist/attack/fixVerifyLoop.d.ts +55 -0
  49. package/dist/attack/fixVerifyLoop.js +187 -0
  50. package/dist/attack/fixVerifyLoop.js.map +1 -0
  51. package/dist/attack/investigationProfiles.d.ts +36 -0
  52. package/dist/attack/investigationProfiles.js +144 -0
  53. package/dist/attack/investigationProfiles.js.map +1 -0
  54. package/dist/attack/investigationState.d.ts +227 -0
  55. package/dist/attack/investigationState.js +666 -0
  56. package/dist/attack/investigationState.js.map +1 -0
  57. package/dist/attack/mutationOperators.d.ts +22 -0
  58. package/dist/attack/mutationOperators.js +170 -0
  59. package/dist/attack/mutationOperators.js.map +1 -0
  60. package/dist/attack/mutationTest.d.ts +33 -0
  61. package/dist/attack/mutationTest.js +110 -0
  62. package/dist/attack/mutationTest.js.map +1 -0
  63. package/dist/attack/proofGate.d.ts +20 -0
  64. package/dist/attack/proofGate.js +104 -0
  65. package/dist/attack/proofGate.js.map +1 -0
  66. package/dist/attack/proofTypes.d.ts +57 -0
  67. package/dist/attack/proofTypes.js +32 -0
  68. package/dist/attack/proofTypes.js.map +1 -0
  69. package/dist/attack/protocolValidator.d.ts +50 -0
  70. package/dist/attack/protocolValidator.js +427 -0
  71. package/dist/attack/protocolValidator.js.map +1 -0
  72. package/dist/attack/qualityMetrics.d.ts +133 -0
  73. package/dist/attack/qualityMetrics.js +226 -0
  74. package/dist/attack/qualityMetrics.js.map +1 -0
  75. package/dist/attack/scanScheduler.d.ts +52 -0
  76. package/dist/attack/scanScheduler.js +265 -0
  77. package/dist/attack/scanScheduler.js.map +1 -0
  78. package/dist/attack/scanState.d.ts +58 -0
  79. package/dist/attack/scanState.js +102 -0
  80. package/dist/attack/scanState.js.map +1 -0
  81. package/dist/attack/searchIntent.d.ts +16 -0
  82. package/dist/attack/searchIntent.js +57 -0
  83. package/dist/attack/searchIntent.js.map +1 -0
  84. package/dist/attack/verifyLoop.d.ts +43 -0
  85. package/dist/attack/verifyLoop.js +314 -14
  86. package/dist/attack/verifyLoop.js.map +1 -1
  87. package/dist/attack/workItem.d.ts +57 -0
  88. package/dist/attack/workItem.js +225 -0
  89. package/dist/attack/workItem.js.map +1 -0
  90. package/dist/audit/findingReviewQueue.d.ts +109 -0
  91. package/dist/audit/findingReviewQueue.js +335 -0
  92. package/dist/audit/findingReviewQueue.js.map +1 -0
  93. package/dist/audit/scanAuditLog.d.ts +160 -0
  94. package/dist/audit/scanAuditLog.js +404 -0
  95. package/dist/audit/scanAuditLog.js.map +1 -0
  96. package/dist/dependency/dependencyChecker.js +35 -9
  97. package/dist/dependency/dependencyChecker.js.map +1 -1
  98. package/dist/dependency/exploitPriority.d.ts +33 -0
  99. package/dist/dependency/exploitPriority.js +78 -0
  100. package/dist/dependency/exploitPriority.js.map +1 -0
  101. package/dist/dependency/finding.d.ts +16 -0
  102. package/dist/dependency/osvClient.js +2 -0
  103. package/dist/dependency/osvClient.js.map +1 -1
  104. package/dist/dependency/types.d.ts +12 -1
  105. package/dist/mcp/server.js +13 -2
  106. package/dist/mcp/server.js.map +1 -1
  107. package/dist/mcp/tools.d.ts +1 -0
  108. package/dist/mcp/tools.js +127 -6
  109. package/dist/mcp/tools.js.map +1 -1
  110. package/dist/project-map/agentMemory.d.ts +83 -1
  111. package/dist/project-map/agentMemory.js +197 -7
  112. package/dist/project-map/agentMemory.js.map +1 -1
  113. package/dist/project-map/architectureContext.d.ts +202 -0
  114. package/dist/project-map/architectureContext.js +421 -0
  115. package/dist/project-map/architectureContext.js.map +1 -0
  116. package/dist/project-map/blastRadius.d.ts +54 -0
  117. package/dist/project-map/blastRadius.js +196 -0
  118. package/dist/project-map/blastRadius.js.map +1 -0
  119. package/dist/project-map/callGraphExtractor.d.ts +13 -0
  120. package/dist/project-map/callGraphExtractor.js +222 -0
  121. package/dist/project-map/callGraphExtractor.js.map +1 -0
  122. package/dist/project-map/capabilityRegistry.d.ts +46 -0
  123. package/dist/project-map/capabilityRegistry.js +91 -0
  124. package/dist/project-map/capabilityRegistry.js.map +1 -0
  125. package/dist/project-map/findTests.d.ts +29 -0
  126. package/dist/project-map/findTests.js +236 -0
  127. package/dist/project-map/findTests.js.map +1 -0
  128. package/dist/project-map/handlerInventory.d.ts +112 -0
  129. package/dist/project-map/handlerInventory.js +184 -0
  130. package/dist/project-map/handlerInventory.js.map +1 -0
  131. package/dist/project-map/implementationResolver.d.ts +43 -0
  132. package/dist/project-map/implementationResolver.js +217 -0
  133. package/dist/project-map/implementationResolver.js.map +1 -0
  134. package/dist/project-map/mapContext.d.ts +6 -1
  135. package/dist/project-map/mapContext.js +1 -0
  136. package/dist/project-map/mapContext.js.map +1 -1
  137. package/dist/project-map/scanCache.d.ts +57 -3
  138. package/dist/project-map/scanCache.js +61 -2
  139. package/dist/project-map/scanCache.js.map +1 -1
  140. package/dist/project-map/symbolIndex.d.ts +39 -0
  141. package/dist/project-map/symbolIndex.js +385 -0
  142. package/dist/project-map/symbolIndex.js.map +1 -0
  143. package/dist/tooling/agentEvalScoring.d.ts +77 -0
  144. package/dist/tooling/agentEvalScoring.js +140 -0
  145. package/dist/tooling/agentEvalScoring.js.map +1 -0
  146. package/dist/tooling/agentRegression.d.ts +53 -0
  147. package/dist/tooling/agentRegression.js +99 -0
  148. package/dist/tooling/agentRegression.js.map +1 -0
  149. package/dist/tools/agentScan.d.ts +69 -0
  150. package/dist/tools/agentScan.js +702 -43
  151. package/dist/tools/agentScan.js.map +1 -1
  152. package/dist/tools/findingReviewTools.d.ts +22 -0
  153. package/dist/tools/findingReviewTools.js +121 -0
  154. package/dist/tools/findingReviewTools.js.map +1 -0
  155. package/dist/tools/fix.js +1 -1
  156. package/dist/tools/fix.js.map +1 -1
  157. package/dist/tools/map.js +191 -1
  158. package/dist/tools/map.js.map +1 -1
  159. package/dist/tools/runTests.d.ts +2 -0
  160. package/dist/tools/runTests.js +29 -0
  161. package/dist/tools/runTests.js.map +1 -0
  162. package/dist/utils/effectMock.d.ts +1 -0
  163. package/dist/utils/effectMock.js +162 -0
  164. package/dist/utils/effectMock.js.map +1 -0
  165. package/dist/utils/gitContext.d.ts +26 -0
  166. package/dist/utils/gitContext.js +317 -0
  167. package/dist/utils/gitContext.js.map +1 -0
  168. package/dist/utils/localTestRunner.d.ts +57 -2
  169. package/dist/utils/localTestRunner.js +122 -115
  170. package/dist/utils/localTestRunner.js.map +1 -1
  171. package/dist/utils/securityConfig.d.ts +17 -0
  172. package/dist/utils/securityConfig.js +192 -0
  173. package/dist/utils/securityConfig.js.map +1 -0
  174. package/dist/utils/testCommandPolicy.d.ts +36 -0
  175. package/dist/utils/testCommandPolicy.js +252 -0
  176. package/dist/utils/testCommandPolicy.js.map +1 -0
  177. package/dist/utils/testRunner.d.ts +56 -0
  178. package/dist/utils/testRunner.js +246 -0
  179. package/dist/utils/testRunner.js.map +1 -0
  180. package/dist/utils/testSafety.d.ts +40 -2
  181. package/dist/utils/testSafety.js +205 -26
  182. package/dist/utils/testSafety.js.map +1 -1
  183. package/dist/utils/verificationSandbox.d.ts +113 -0
  184. package/dist/utils/verificationSandbox.js +656 -0
  185. package/dist/utils/verificationSandbox.js.map +1 -0
  186. package/package.json +1 -1
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * Architecture scout executor — dispatches each scout action to the right
4
+ * tool and returns a redacted, truncated observation string.
5
+ *
6
+ * This is a SUBSET of the agent-scan executor: only the
7
+ * architecture-relevant tools (read_file, search_code, list_files,
8
+ * list_imports, get_endpoints, call_graph, read_config, check_dependencies,
9
+ * find_definition, find_references). Vulnerability-confirmation tools
10
+ * (trace_flow, check_guard, check_policy, git_*, find_tests, run_tests)
11
+ * are NOT available — the scout surveys architecture, it does not confirm
12
+ * vulnerabilities.
13
+ *
14
+ * All tools run locally (no API round-trip — the scout has no equivalent
15
+ * of the agent-scan check_policy that calls back to the API).
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.executeScoutAction = executeScoutAction;
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const files_1 = require("../utils/files");
55
+ const searchCode_1 = require("../utils/searchCode");
56
+ const listImports_1 = require("../utils/listImports");
57
+ const listFiles_1 = require("../utils/listFiles");
58
+ const endpointDiscovery_1 = require("../project-map/endpointDiscovery");
59
+ const callGraphExtractor_1 = require("../project-map/callGraphExtractor");
60
+ const dependencyChecker_1 = require("../dependency/dependencyChecker");
61
+ const depCache_1 = require("../dependency/depCache");
62
+ const securityConfig_1 = require("../utils/securityConfig");
63
+ const symbolIndex_1 = require("../project-map/symbolIndex");
64
+ const agentScanExecutor_1 = require("./agentScanExecutor");
65
+ async function executeScoutAction(action, ctx) {
66
+ switch (action.type) {
67
+ case 'read_file': {
68
+ try {
69
+ const absPath = (0, files_1.resolveWorkspacePath)(ctx.workspaceRoot, action.path);
70
+ const content = fs.readFileSync(absPath, 'utf8');
71
+ const rel = path.relative(ctx.workspaceRoot, absPath).replace(/\\/g, '/');
72
+ const allLines = content.split('\n');
73
+ const totalLines = allLines.length;
74
+ if (action.startLine || action.endLine) {
75
+ const start = Math.max(1, action.startLine || 1);
76
+ const end = Math.min(totalLines, action.endLine || totalLines);
77
+ const section = allLines.slice(start - 1, end)
78
+ .map((line, i) => `${start + i}: ${line}`)
79
+ .join('\n');
80
+ return (0, agentScanExecutor_1.redact)(`File: ${rel} (lines ${start}-${end} of ${totalLines})\n\n${section}`);
81
+ }
82
+ if (totalLines > agentScanExecutor_1.LARGE_FILE_THRESHOLD) {
83
+ const funcMap = await (0, agentScanExecutor_1.extractFunctionMap)(content, rel);
84
+ if (funcMap) {
85
+ return (0, agentScanExecutor_1.redact)(`File: ${rel} (${totalLines} lines — LARGE FILE)\n\nThis file is too large to show in full. Here is a function map. Use read_file with startLine/endLine to read specific sections.\n\n${funcMap}`);
86
+ }
87
+ }
88
+ const numbered = allLines
89
+ .map((line, i) => `${i + 1}: ${line}`)
90
+ .join('\n');
91
+ return (0, agentScanExecutor_1.redact)(`File: ${rel} (${totalLines} lines)\n\n${numbered}`);
92
+ }
93
+ catch (e) {
94
+ return `Error reading file "${action.path}": ${e.message || e}`;
95
+ }
96
+ }
97
+ case 'search_code': {
98
+ try {
99
+ const result = await (0, searchCode_1.searchCode)(ctx.workspaceRoot, action.pattern, action.glob);
100
+ return (0, agentScanExecutor_1.redact)((0, searchCode_1.formatSearchResult)(result));
101
+ }
102
+ catch (e) {
103
+ return `Error searching for "${action.pattern}": ${e.message || e}`;
104
+ }
105
+ }
106
+ case 'list_files': {
107
+ try {
108
+ const files = (0, listFiles_1.listFiles)(ctx.workspaceRoot, {
109
+ dir: action.path,
110
+ glob: action.glob,
111
+ });
112
+ return (0, agentScanExecutor_1.truncate)((0, listFiles_1.formatFileList)(files));
113
+ }
114
+ catch (e) {
115
+ return `Error listing files: ${e.message || e}`;
116
+ }
117
+ }
118
+ case 'list_imports': {
119
+ try {
120
+ const imports = await (0, listImports_1.listImports)(ctx.workspaceRoot, action.filePath);
121
+ return (0, agentScanExecutor_1.truncate)((0, listImports_1.formatImports)(imports, action.filePath));
122
+ }
123
+ catch (e) {
124
+ return `Error listing imports for "${action.filePath}": ${e.message || e}`;
125
+ }
126
+ }
127
+ case 'get_endpoints': {
128
+ try {
129
+ const endpoints = await (0, endpointDiscovery_1.discoverEndpoints)(ctx.workspaceRoot, action.glob);
130
+ return (0, agentScanExecutor_1.truncate)((0, endpointDiscovery_1.formatEndpoints)(endpoints));
131
+ }
132
+ catch (e) {
133
+ return `Error discovering endpoints: ${e.message || e}`;
134
+ }
135
+ }
136
+ case 'call_graph': {
137
+ try {
138
+ const result = await (0, callGraphExtractor_1.getCallGraph)(ctx.workspaceRoot, action.filePath, action.functionName);
139
+ return (0, agentScanExecutor_1.redact)(result);
140
+ }
141
+ catch (e) {
142
+ return `Error extracting call graph for "${action.filePath}": ${e.message || e}`;
143
+ }
144
+ }
145
+ case 'read_config': {
146
+ try {
147
+ const result = await (0, securityConfig_1.readSecurityConfig)(ctx.workspaceRoot, action.configKind);
148
+ return (0, agentScanExecutor_1.truncate)((0, agentScanExecutor_1.redact)(result));
149
+ }
150
+ catch (e) {
151
+ return `Error reading ${action.configKind} config: ${e.message || e}`;
152
+ }
153
+ }
154
+ case 'check_dependencies': {
155
+ try {
156
+ const cache = new depCache_1.FileCacheStore(ctx.workspaceRoot);
157
+ const result = await (0, dependencyChecker_1.scanDependencies)({
158
+ workspaceRoot: ctx.workspaceRoot,
159
+ state: cache,
160
+ licensePolicy: 'warn',
161
+ useGhsa: true,
162
+ });
163
+ if (result.findings.length === 0) {
164
+ return `No vulnerable dependencies found. Scanned ${result.packageCount} package(s) across ${result.lockfiles.length} lockfile(s).`;
165
+ }
166
+ const vulnFindings = result.findings.filter(f => !f.check_id.startsWith('dep.license.') && !f.check_id.startsWith('dep.unresolved.'));
167
+ const lines = [
168
+ `Dependency scan: ${vulnFindings.length} vulnerable package(s) across ${result.packageCount} package(s) (${result.lockfiles.length} lockfile(s)):`,
169
+ '',
170
+ ];
171
+ for (const f of vulnFindings.slice(0, 30)) {
172
+ const dep = f.dependency;
173
+ const sev = f.severity === 'ERROR' ? 'CRITICAL' : 'HIGH';
174
+ lines.push(` [${sev}] ${dep?.name || 'unknown'}@${dep?.installedVersion || '?'} — ${f.message}`);
175
+ if (dep?.fixedVersion)
176
+ lines.push(` Fix: upgrade to ${dep.fixedVersion}`);
177
+ }
178
+ if (vulnFindings.length > 30) {
179
+ lines.push(` ... and ${vulnFindings.length - 30} more`);
180
+ }
181
+ return (0, agentScanExecutor_1.truncate)(lines.join('\n'));
182
+ }
183
+ catch (e) {
184
+ return `Error checking dependencies: ${e.message || e}`;
185
+ }
186
+ }
187
+ case 'find_definition': {
188
+ try {
189
+ const result = await (0, symbolIndex_1.findDefinition)(ctx.workspaceRoot, action.filePath, action.symbol, action.line);
190
+ return (0, agentScanExecutor_1.truncate)(result);
191
+ }
192
+ catch (e) {
193
+ return `Error finding definition of "${action.symbol}": ${e.message || e}`;
194
+ }
195
+ }
196
+ case 'find_references': {
197
+ try {
198
+ const result = await (0, symbolIndex_1.findReferences)(ctx.workspaceRoot, action.filePath, action.symbol, action.line);
199
+ return (0, agentScanExecutor_1.truncate)(result);
200
+ }
201
+ catch (e) {
202
+ return `Error finding references to "${action.symbol}": ${e.message || e}`;
203
+ }
204
+ }
205
+ case 'finish': {
206
+ return '';
207
+ }
208
+ }
209
+ return '';
210
+ }
211
+ //# sourceMappingURL=architectureScoutExecutor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architectureScoutExecutor.js","sourceRoot":"","sources":["../../src/attack/architectureScoutExecutor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBH,gDAyJC;AA9KD,uCAAyB;AACzB,2CAA6B;AAE7B,0CAAsD;AACtD,oDAAqE;AACrE,sDAAkE;AAClE,kDAA+D;AAC/D,wEAAsF;AACtF,0EAAiE;AACjE,uEAAmE;AACnE,qDAAwD;AACxD,4DAA6D;AAC7D,4DAA4E;AAC5E,2DAK6B;AAGtB,KAAK,UAAU,kBAAkB,CACpC,MAA+B,EAC/B,GAAkB;IAElB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,WAAW,CAAC,CAAC,CAAC;YACf,IAAI,CAAC;gBACD,MAAM,OAAO,GAAG,IAAA,4BAAoB,EAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBACrE,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACjD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACrC,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAEnC,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;oBACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,IAAI,UAAU,CAAC,CAAC;oBAC/D,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC;yBACzC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;yBACzC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAChB,OAAO,IAAA,0BAAM,EAAC,SAAS,GAAG,WAAW,KAAK,IAAI,GAAG,OAAO,UAAU,QAAQ,OAAO,EAAE,CAAC,CAAC;gBACzF,CAAC;gBAED,IAAI,UAAU,GAAG,wCAAoB,EAAE,CAAC;oBACpC,MAAM,OAAO,GAAG,MAAM,IAAA,sCAAkB,EAAC,OAAO,EAAE,GAAG,CAAC,CAAC;oBACvD,IAAI,OAAO,EAAE,CAAC;wBACV,OAAO,IAAA,0BAAM,EAAC,SAAS,GAAG,KAAK,UAAU,8JAA8J,OAAO,EAAE,CAAC,CAAC;oBACtN,CAAC;gBACL,CAAC;gBAED,MAAM,QAAQ,GAAG,QAAQ;qBACpB,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;qBACrC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAChB,OAAO,IAAA,0BAAM,EAAC,SAAS,GAAG,KAAK,UAAU,cAAc,QAAQ,EAAE,CAAC,CAAC;YACvE,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,uBAAuB,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YACpE,CAAC;QACL,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACjB,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAA,uBAAU,EAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBAChF,OAAO,IAAA,0BAAM,EAAC,IAAA,+BAAkB,EAAC,MAAM,CAAC,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,wBAAwB,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YACxE,CAAC;QACL,CAAC;QAED,KAAK,YAAY,CAAC,CAAC,CAAC;YAChB,IAAI,CAAC;gBACD,MAAM,KAAK,GAAG,IAAA,qBAAS,EAAC,GAAG,CAAC,aAAa,EAAE;oBACvC,GAAG,EAAG,MAAc,CAAC,IAAI;oBACzB,IAAI,EAAG,MAAc,CAAC,IAAI;iBAC7B,CAAC,CAAC;gBACH,OAAO,IAAA,4BAAQ,EAAC,IAAA,0BAAc,EAAC,KAAK,CAAC,CAAC,CAAC;YAC3C,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,wBAAwB,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YACpD,CAAC;QACL,CAAC;QAED,KAAK,cAAc,CAAC,CAAC,CAAC;YAClB,IAAI,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,IAAA,yBAAW,EAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACtE,OAAO,IAAA,4BAAQ,EAAC,IAAA,2BAAa,EAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC7D,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,8BAA8B,MAAM,CAAC,QAAQ,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YAC/E,CAAC;QACL,CAAC;QAED,KAAK,eAAe,CAAC,CAAC,CAAC;YACnB,IAAI,CAAC;gBACD,MAAM,SAAS,GAAG,MAAM,IAAA,qCAAiB,EAAC,GAAG,CAAC,aAAa,EAAG,MAAc,CAAC,IAAI,CAAC,CAAC;gBACnF,OAAO,IAAA,4BAAQ,EAAC,IAAA,mCAAe,EAAC,SAAS,CAAC,CAAC,CAAC;YAChD,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,gCAAgC,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YAC5D,CAAC;QACL,CAAC;QAED,KAAK,YAAY,CAAC,CAAC,CAAC;YAChB,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAA,iCAAY,EAC7B,GAAG,CAAC,aAAa,EACjB,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,YAAY,CACtB,CAAC;gBACF,OAAO,IAAA,0BAAM,EAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,oCAAoC,MAAM,CAAC,QAAQ,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YACrF,CAAC;QACL,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACjB,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAA,mCAAkB,EAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;gBAC9E,OAAO,IAAA,4BAAQ,EAAC,IAAA,0BAAM,EAAC,MAAM,CAAC,CAAC,CAAC;YACpC,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,iBAAiB,MAAM,CAAC,UAAU,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YAC1E,CAAC;QACL,CAAC;QACD,KAAK,oBAAoB,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC;gBACD,MAAM,KAAK,GAAG,IAAI,yBAAc,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBACpD,MAAM,MAAM,GAAG,MAAM,IAAA,oCAAgB,EAAC;oBAClC,aAAa,EAAE,GAAG,CAAC,aAAa;oBAChC,KAAK,EAAE,KAAK;oBACZ,aAAa,EAAE,MAAM;oBACrB,OAAO,EAAE,IAAI;iBAChB,CAAC,CAAC;gBACH,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC/B,OAAO,6CAA6C,MAAM,CAAC,YAAY,sBAAsB,MAAM,CAAC,SAAS,CAAC,MAAM,eAAe,CAAC;gBACxI,CAAC;gBACD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC;gBACtI,MAAM,KAAK,GAAa;oBACpB,oBAAoB,YAAY,CAAC,MAAM,iCAAiC,MAAM,CAAC,YAAY,gBAAgB,MAAM,CAAC,SAAS,CAAC,MAAM,gBAAgB;oBAClJ,EAAE;iBACL,CAAC;gBACF,KAAK,MAAM,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;oBACxC,MAAM,GAAG,GAAI,CAAS,CAAC,UAAU,CAAC;oBAClC,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;oBACzD,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,GAAG,EAAE,IAAI,IAAI,SAAS,IAAI,GAAG,EAAE,gBAAgB,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;oBAClG,IAAI,GAAG,EAAE,YAAY;wBAAE,KAAK,CAAC,IAAI,CAAC,uBAAuB,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;gBACjF,CAAC;gBACD,IAAI,YAAY,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;oBAC3B,KAAK,CAAC,IAAI,CAAC,aAAa,YAAY,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;gBAC7D,CAAC;gBACD,OAAO,IAAA,4BAAQ,EAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACtC,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,gCAAgC,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YAC5D,CAAC;QACL,CAAC;QAED,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACrB,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAc,EAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBACpG,OAAO,IAAA,4BAAQ,EAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,gCAAgC,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YAC/E,CAAC;QACL,CAAC;QAED,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACrB,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAc,EAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBACpG,OAAO,IAAA,4BAAQ,EAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,OAAO,gCAAgC,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YAC/E,CAAC;QACL,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACZ,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Architecture scout loop driver — the MCP-side loop that drives the
3
+ * architecture-scout subagent.
4
+ *
5
+ * Mirrors agentScanLoop.ts but with architecture-scout tools and a finish
6
+ * action that returns an ArchitectureContext instead of vulnerability
7
+ * findings.
8
+ *
9
+ * Flow:
10
+ * 1. POST /agent/architecture/start → { runId, budget }
11
+ * 2. while (true):
12
+ * POST /agent/architecture/step { runId, target, transcript, budget }
13
+ * ← { next: action | null }
14
+ * if null → done (capped or completed)
15
+ * if finish → done with ArchitectureContext
16
+ * execute action locally → observation
17
+ * push { action, observation } into transcript
18
+ * 3. return result
19
+ *
20
+ * The loop inherits the system_event + protocolValidator hardening from
21
+ * agentScanLoop: malformed API responses are rejected, errors are surfaced
22
+ * as system_event(error) instead of fake read_file('__ERROR__').
23
+ */
24
+ import type { ServerContext } from '../mcp/types';
25
+ import { ARCHITECTURE_SCOUT_DEFAULTS, type ArchitectureDepth, type ArchitectureScoutResult, type ArchitectureScoutTarget } from './architectureScoutProtocol';
26
+ export interface ArchitectureScoutOptions {
27
+ depth?: ArchitectureDepth;
28
+ signal?: AbortSignal;
29
+ onProgress?: (stepsTaken: number, maxSteps: number, message: string) => void;
30
+ /** Project map builtAt — stamped on the result context for cache invalidation. */
31
+ projectMapBuiltAt?: number;
32
+ /** Project map schema version — stamped on the result context. */
33
+ projectMapVersion?: number;
34
+ }
35
+ export declare function runArchitectureScout(ctx: ServerContext, target: ArchitectureScoutTarget, options?: ArchitectureScoutOptions): Promise<ArchitectureScoutResult>;
36
+ export { ARCHITECTURE_SCOUT_DEFAULTS };
@@ -0,0 +1,403 @@
1
+ "use strict";
2
+ /**
3
+ * Architecture scout loop driver — the MCP-side loop that drives the
4
+ * architecture-scout subagent.
5
+ *
6
+ * Mirrors agentScanLoop.ts but with architecture-scout tools and a finish
7
+ * action that returns an ArchitectureContext instead of vulnerability
8
+ * findings.
9
+ *
10
+ * Flow:
11
+ * 1. POST /agent/architecture/start → { runId, budget }
12
+ * 2. while (true):
13
+ * POST /agent/architecture/step { runId, target, transcript, budget }
14
+ * ← { next: action | null }
15
+ * if null → done (capped or completed)
16
+ * if finish → done with ArchitectureContext
17
+ * execute action locally → observation
18
+ * push { action, observation } into transcript
19
+ * 3. return result
20
+ *
21
+ * The loop inherits the system_event + protocolValidator hardening from
22
+ * agentScanLoop: malformed API responses are rejected, errors are surfaced
23
+ * as system_event(error) instead of fake read_file('__ERROR__').
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.ARCHITECTURE_SCOUT_DEFAULTS = void 0;
27
+ exports.runArchitectureScout = runArchitectureScout;
28
+ const client_1 = require("../api/client");
29
+ const architectureScoutExecutor_1 = require("./architectureScoutExecutor");
30
+ const architectureScoutProtocol_1 = require("./architectureScoutProtocol");
31
+ Object.defineProperty(exports, "ARCHITECTURE_SCOUT_DEFAULTS", { enumerable: true, get: function () { return architectureScoutProtocol_1.ARCHITECTURE_SCOUT_DEFAULTS; } });
32
+ const architectureContext_1 = require("../project-map/architectureContext");
33
+ // Re-use the agent-scan response validator shape. The architecture scout
34
+ // step response has the same fields as AgentScanStepResponse (minus
35
+ // systemEvent, which the scout doesn't use in v1). We validate inline
36
+ // rather than importing the agent-scan validator (which checks agent-scan
37
+ // action types the scout doesn't have).
38
+ const protocolValidator_1 = require("./protocolValidator");
39
+ // ── Inline validators (scout-specific) ──────────────────────────────────────
40
+ //
41
+ // The scout action types are a subset of agent-scan's, but the validator in
42
+ // protocolValidator.ts checks the full agent-scan action union (including
43
+ // trace_flow, check_guard, etc.). Rather than weaken that validator, we
44
+ // validate the scout actions inline here. The start-response validator is
45
+ // shape-compatible, so we reuse it.
46
+ const VALID_SCOUT_ACTION_TYPES = new Set([
47
+ 'read_file', 'search_code', 'list_files', 'list_imports',
48
+ 'get_endpoints', 'call_graph', 'read_config', 'check_dependencies',
49
+ 'find_definition', 'find_references', 'finish',
50
+ ]);
51
+ function validateScoutAction(raw) {
52
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
53
+ return { ok: false, error: 'action is not an object' };
54
+ }
55
+ const a = raw;
56
+ const type = a.type;
57
+ if (typeof type !== 'string' || !VALID_SCOUT_ACTION_TYPES.has(type)) {
58
+ return { ok: false, error: `action.type is missing or unknown: ${String(type)}` };
59
+ }
60
+ // Light field checks for the primary tool args. The API controller does
61
+ // deeper validation; here we just block obviously malformed actions
62
+ // from reaching the executor.
63
+ if (type === 'read_file' && (typeof a.path !== 'string' || a.path.length === 0)) {
64
+ return { ok: false, error: 'read_file requires non-empty "path"' };
65
+ }
66
+ if (type === 'search_code' && (typeof a.pattern !== 'string' || a.pattern.length === 0)) {
67
+ return { ok: false, error: 'search_code requires non-empty "pattern"' };
68
+ }
69
+ if (type === 'list_imports' && (typeof a.filePath !== 'string' || a.filePath.length === 0)) {
70
+ return { ok: false, error: 'list_imports requires non-empty "filePath"' };
71
+ }
72
+ if (type === 'call_graph' && (typeof a.filePath !== 'string' || a.filePath.length === 0)) {
73
+ return { ok: false, error: 'call_graph requires non-empty "filePath"' };
74
+ }
75
+ if (type === 'read_config') {
76
+ const validKinds = new Set(['auth', 'cors', 'rate_limit', 'headers', 'env', 'all']);
77
+ if (typeof a.configKind !== 'string' || !validKinds.has(a.configKind)) {
78
+ return { ok: false, error: 'read_config requires valid "configKind"' };
79
+ }
80
+ }
81
+ if (type === 'find_definition' || type === 'find_references') {
82
+ if (typeof a.filePath !== 'string' || a.filePath.length === 0) {
83
+ return { ok: false, error: `${type} requires non-empty "filePath"` };
84
+ }
85
+ if (typeof a.symbol !== 'string' || a.symbol.length === 0) {
86
+ return { ok: false, error: `${type} requires non-empty "symbol"` };
87
+ }
88
+ }
89
+ if (type === 'finish') {
90
+ if (typeof a.summary !== 'string' || a.summary.length === 0) {
91
+ return { ok: false, error: 'finish requires "summary"' };
92
+ }
93
+ if (typeof a.selfCritique !== 'string' || a.selfCritique.length === 0) {
94
+ return { ok: false, error: 'finish requires "selfCritique"' };
95
+ }
96
+ if (typeof a.architecture !== 'object' || a.architecture === null) {
97
+ return { ok: false, error: 'finish requires "architecture" object' };
98
+ }
99
+ }
100
+ return { ok: true, value: raw };
101
+ }
102
+ function validateScoutStepResponse(raw) {
103
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
104
+ return { ok: false, error: 'step response is not an object' };
105
+ }
106
+ const r = raw;
107
+ if (r.next !== null && r.next !== undefined) {
108
+ const actionResult = validateScoutAction(r.next);
109
+ if (!actionResult.ok)
110
+ return { ok: false, error: `step response next: ${actionResult.error}` };
111
+ }
112
+ if (typeof r.costUsd !== 'number')
113
+ return { ok: false, error: 'costUsd must be a number' };
114
+ if (typeof r.tokens !== 'number')
115
+ return { ok: false, error: 'tokens must be a number' };
116
+ if (typeof r.degraded !== 'boolean')
117
+ return { ok: false, error: 'degraded must be a boolean' };
118
+ if (typeof r.costCapped !== 'boolean')
119
+ return { ok: false, error: 'costCapped must be a boolean' };
120
+ if (typeof r.stepsRemaining !== 'number')
121
+ return { ok: false, error: 'stepsRemaining must be a number' };
122
+ return { ok: true, value: raw };
123
+ }
124
+ // ── Main loop ───────────────────────────────────────────────────────────────
125
+ async function runArchitectureScout(ctx, target, options = {}) {
126
+ const client = new client_1.ApiClient({ baseUrl: ctx.apiUrl, token: ctx.apiToken });
127
+ const depth = options.depth ?? 'standard';
128
+ const defaults = (0, architectureScoutProtocol_1.scoutDefaultsForDepth)(depth);
129
+ const budget = {
130
+ stepsRemaining: defaults.maxSteps,
131
+ costSpentUsd: 0,
132
+ costCapUsd: defaults.costCapUsd,
133
+ };
134
+ const startTime = Date.now();
135
+ const wallClockMs = defaults.wallClockMs;
136
+ let stepsTaken = 0;
137
+ let costSpentUsd = 0;
138
+ try {
139
+ const startRespRaw = await client.postJson('/agent/architecture/start', { depth }, options.signal);
140
+ // Re-use the agent-scan start response validator — the shape is
141
+ // identical (runId, budget, scanCredits, refundId).
142
+ const startValidation = (0, protocolValidator_1.validateStartResponse)(startRespRaw);
143
+ if (!startValidation.ok) {
144
+ return {
145
+ status: 'spawn_failed',
146
+ architecture: null,
147
+ transcript: [],
148
+ stepsUsed: 0,
149
+ costSpentUsd: 0,
150
+ error: `API returned an invalid start response: ${startValidation.error}`,
151
+ };
152
+ }
153
+ const startResp = startValidation.value;
154
+ const transcript = [];
155
+ const readFiles = new Set();
156
+ const readFileCounts = new Map();
157
+ const toolCallCounts = new Map();
158
+ const MAX_SAME_TOOL_CALL = 2;
159
+ let consecutiveErrors = 0;
160
+ while (true) {
161
+ if (Date.now() - startTime > wallClockMs) {
162
+ return {
163
+ status: 'capped',
164
+ architecture: null,
165
+ transcript,
166
+ stepsUsed: stepsTaken,
167
+ costSpentUsd,
168
+ summary: `Wall clock limit (${wallClockMs}ms) exceeded.`,
169
+ };
170
+ }
171
+ if (options.signal?.aborted) {
172
+ return {
173
+ status: 'cancelled',
174
+ architecture: null,
175
+ transcript,
176
+ stepsUsed: stepsTaken,
177
+ costSpentUsd,
178
+ summary: 'Cancelled by user.',
179
+ };
180
+ }
181
+ const stepReq = {
182
+ runId: startResp.runId,
183
+ target,
184
+ transcript,
185
+ budget: {
186
+ stepsRemaining: budget.stepsRemaining,
187
+ costSpentUsd,
188
+ costCapUsd: budget.costCapUsd,
189
+ },
190
+ };
191
+ let stepResp;
192
+ try {
193
+ const stepRespRaw = await client.postJson('/agent/architecture/step', stepReq, options.signal);
194
+ const stepValidation = validateScoutStepResponse(stepRespRaw);
195
+ if (!stepValidation.ok) {
196
+ const vErr = stepValidation.error;
197
+ console.warn(`[Architecture Scout] Step ${stepsTaken + 1} returned a malformed response: ${vErr}`);
198
+ consecutiveErrors++;
199
+ stepsTaken++;
200
+ budget.stepsRemaining--;
201
+ const errMsg = `API returned a malformed step response: ${vErr}`;
202
+ transcript.push({
203
+ action: { type: 'finish', architecture: {}, summary: '', selfCritique: errMsg },
204
+ observation: errMsg,
205
+ });
206
+ if (consecutiveErrors >= 3 || budget.stepsRemaining <= 0) {
207
+ return {
208
+ status: 'capped',
209
+ architecture: null,
210
+ transcript,
211
+ stepsUsed: stepsTaken,
212
+ costSpentUsd,
213
+ summary: `Step budget exhausted after ${consecutiveErrors} consecutive malformed API responses. Last error: ${vErr}`,
214
+ };
215
+ }
216
+ continue;
217
+ }
218
+ stepResp = stepValidation.value;
219
+ }
220
+ catch (stepErr) {
221
+ const errMsg = stepErr?.message || String(stepErr);
222
+ const apiCode = stepErr?.apiCode || '';
223
+ if (apiCode === 'AGENT_RUN_NOT_FOUND' || /AGENT_RUN_NOT_FOUND|Invalid or expired agent run/i.test(errMsg)) {
224
+ return {
225
+ status: 'spawn_failed',
226
+ architecture: null,
227
+ transcript,
228
+ stepsUsed: stepsTaken,
229
+ costSpentUsd,
230
+ error: 'API server restarted mid-scout — the run was lost. Please retry.',
231
+ };
232
+ }
233
+ if (options.signal?.aborted || /aborted/i.test(errMsg)) {
234
+ return {
235
+ status: 'cancelled',
236
+ architecture: null,
237
+ transcript,
238
+ stepsUsed: stepsTaken,
239
+ costSpentUsd,
240
+ summary: 'Cancelled by user.',
241
+ };
242
+ }
243
+ console.warn(`[Architecture Scout] Step ${stepsTaken + 1} error: ${errMsg}`);
244
+ consecutiveErrors++;
245
+ stepsTaken++;
246
+ budget.stepsRemaining--;
247
+ const errorMsg = `ERROR: Your previous action was rejected: ${errMsg}. Please try a DIFFERENT action with ALL required fields.`;
248
+ transcript.push({
249
+ action: { type: 'finish', architecture: {}, summary: '', selfCritique: errorMsg },
250
+ observation: errorMsg,
251
+ });
252
+ if (consecutiveErrors >= 3 || budget.stepsRemaining <= 0) {
253
+ return {
254
+ status: 'capped',
255
+ architecture: null,
256
+ transcript,
257
+ stepsUsed: stepsTaken,
258
+ costSpentUsd,
259
+ summary: `Step budget exhausted after ${consecutiveErrors} consecutive API errors. Last error: ${errMsg}`,
260
+ };
261
+ }
262
+ continue;
263
+ }
264
+ costSpentUsd += stepResp.costUsd || 0;
265
+ consecutiveErrors = 0;
266
+ if (!stepResp.next) {
267
+ const status = stepResp.costCapped
268
+ ? 'capped'
269
+ : (stepResp.degraded ? 'degraded' : 'completed');
270
+ return {
271
+ status,
272
+ architecture: null,
273
+ transcript,
274
+ stepsUsed: stepsTaken,
275
+ costSpentUsd,
276
+ summary: stepResp.costCapped
277
+ ? `Cost cap ($${budget.costCapUsd.toFixed(2)}) reached.`
278
+ : 'Scout completed without explicit finish.',
279
+ };
280
+ }
281
+ const action = stepResp.next;
282
+ stepsTaken++;
283
+ budget.stepsRemaining = stepResp.stepsRemaining;
284
+ if (options.onProgress) {
285
+ options.onProgress(stepsTaken, defaults.maxSteps, `Step ${stepsTaken}: ${describeScoutAction(action)}`);
286
+ }
287
+ if (action.type === 'finish') {
288
+ if (!(0, architectureScoutProtocol_1.isScoutFinishAction)(action)) {
289
+ // Validator should have caught this, but double-check.
290
+ return {
291
+ status: 'spawn_failed',
292
+ architecture: null,
293
+ transcript,
294
+ stepsUsed: stepsTaken,
295
+ costSpentUsd,
296
+ error: 'Finish action did not pass type guard.',
297
+ };
298
+ }
299
+ // Stamp the context with cache/derivation metadata before
300
+ // returning — the LLM doesn't know these fields.
301
+ const ctx2 = action.architecture;
302
+ ctx2.version = architectureContext_1.ARCHITECTURE_CONTEXT_VERSION;
303
+ ctx2.depth = depth;
304
+ ctx2.derivedAt = Date.now();
305
+ ctx2.projectMapBuiltAt = options.projectMapBuiltAt ?? 0;
306
+ ctx2.projectMapVersion = options.projectMapVersion ?? 0;
307
+ if (!ctx2.completeness)
308
+ ctx2.completeness = 'partial';
309
+ // Cap importantFiles at maxImportantFiles — the LLM may
310
+ // exceed the cap despite the prompt instruction.
311
+ if (ctx2.importantFiles && ctx2.importantFiles.length > target.maxImportantFiles) {
312
+ ctx2.importantFiles = ctx2.importantFiles
313
+ .sort((a, b) => b.importance - a.importance)
314
+ .slice(0, target.maxImportantFiles);
315
+ }
316
+ return {
317
+ status: 'completed',
318
+ architecture: ctx2,
319
+ transcript,
320
+ stepsUsed: stepsTaken,
321
+ costSpentUsd,
322
+ summary: action.summary,
323
+ };
324
+ }
325
+ // Dedup: prevent the scout from re-reading the same file range
326
+ // or calling the same tool with identical args repeatedly.
327
+ let observation;
328
+ let wasBlocked = false;
329
+ if (action.type === 'read_file') {
330
+ const normalizedPath = action.path.replace(/\\/g, '/').toLowerCase();
331
+ const rangeKey = `${normalizedPath}:${action.startLine || 0}:${action.endLine || 0}`;
332
+ if (readFiles.has(rangeKey)) {
333
+ observation = `File "${action.path}" (lines ${action.startLine || 'all'}-${action.endLine || 'all'}) was already read. The content is in the transcript above. Use a DIFFERENT line range or a different tool.`;
334
+ wasBlocked = true;
335
+ }
336
+ else {
337
+ const count = (readFileCounts.get(normalizedPath) || 0) + 1;
338
+ readFileCounts.set(normalizedPath, count);
339
+ readFiles.add(rangeKey);
340
+ observation = await (0, architectureScoutExecutor_1.executeScoutAction)(action, ctx);
341
+ if (count >= 5) {
342
+ observation += `\n\nNOTE: You have read "${action.path}" ${count} times. Consider using search_code, call_graph, find_references, or finish to proceed.`;
343
+ }
344
+ }
345
+ }
346
+ else {
347
+ let toolKey = '';
348
+ if (action.type === 'search_code')
349
+ toolKey = `search_code:${action.pattern}:${action.glob || ''}`;
350
+ else if (action.type === 'call_graph')
351
+ toolKey = `call_graph:${action.filePath}:${action.functionName || ''}`;
352
+ else if (action.type === 'list_imports')
353
+ toolKey = `list_imports:${action.filePath}`;
354
+ else if (action.type === 'find_definition')
355
+ toolKey = `find_definition:${action.filePath}:${action.symbol}`;
356
+ else if (action.type === 'find_references')
357
+ toolKey = `find_references:${action.filePath}:${action.symbol}`;
358
+ if (toolKey) {
359
+ const count = (toolCallCounts.get(toolKey) || 0) + 1;
360
+ toolCallCounts.set(toolKey, count);
361
+ if (count > MAX_SAME_TOOL_CALL) {
362
+ observation = `You have already called this exact tool with the same arguments ${MAX_SAME_TOOL_CALL} times. Use a DIFFERENT tool, different arguments, or call finish to report your architecture context.`;
363
+ wasBlocked = true;
364
+ }
365
+ else {
366
+ observation = await (0, architectureScoutExecutor_1.executeScoutAction)(action, ctx);
367
+ }
368
+ }
369
+ else {
370
+ observation = await (0, architectureScoutExecutor_1.executeScoutAction)(action, ctx);
371
+ }
372
+ }
373
+ transcript.push({ action, observation });
374
+ }
375
+ }
376
+ catch (err) {
377
+ return {
378
+ status: 'spawn_failed',
379
+ architecture: null,
380
+ transcript: [],
381
+ stepsUsed: stepsTaken,
382
+ costSpentUsd,
383
+ error: err.message || String(err),
384
+ };
385
+ }
386
+ }
387
+ function describeScoutAction(action) {
388
+ switch (action.type) {
389
+ case 'read_file': return `read_file(${action.path})`;
390
+ case 'search_code': return `search_code("${action.pattern}")`;
391
+ case 'list_files': return `list_files(${action.path || 'root'})`;
392
+ case 'list_imports': return `list_imports(${action.filePath})`;
393
+ case 'get_endpoints': return `get_endpoints(${action.glob || 'all'})`;
394
+ case 'call_graph': return `call_graph(${action.filePath}${action.functionName ? `, fn=${action.functionName}` : ''})`;
395
+ case 'read_config': return `read_config(${action.configKind})`;
396
+ case 'check_dependencies': return 'check_dependencies';
397
+ case 'find_definition': return `find_definition(${action.symbol})`;
398
+ case 'find_references': return `find_references(${action.symbol})`;
399
+ case 'finish': return 'finish';
400
+ }
401
+ return 'unknown';
402
+ }
403
+ //# sourceMappingURL=architectureScoutLoop.js.map