windows-exe-decompiler-mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CODEX_INSTALLATION.md +69 -0
  2. package/COPILOT_INSTALLATION.md +77 -0
  3. package/LICENSE +21 -0
  4. package/README.md +314 -0
  5. package/bin/windows-exe-decompiler-mcp-server.js +3 -0
  6. package/dist/analysis-provenance.d.ts +184 -0
  7. package/dist/analysis-provenance.js +74 -0
  8. package/dist/analysis-task-runner.d.ts +31 -0
  9. package/dist/analysis-task-runner.js +160 -0
  10. package/dist/artifact-inventory.d.ts +23 -0
  11. package/dist/artifact-inventory.js +175 -0
  12. package/dist/cache-manager.d.ts +128 -0
  13. package/dist/cache-manager.js +454 -0
  14. package/dist/confidence-semantics.d.ts +66 -0
  15. package/dist/confidence-semantics.js +122 -0
  16. package/dist/config.d.ts +335 -0
  17. package/dist/config.js +193 -0
  18. package/dist/database.d.ts +227 -0
  19. package/dist/database.js +601 -0
  20. package/dist/decompiler-worker.d.ts +441 -0
  21. package/dist/decompiler-worker.js +1962 -0
  22. package/dist/dynamic-trace.d.ts +95 -0
  23. package/dist/dynamic-trace.js +629 -0
  24. package/dist/env-validator.d.ts +15 -0
  25. package/dist/env-validator.js +249 -0
  26. package/dist/error-handler.d.ts +28 -0
  27. package/dist/error-handler.example.d.ts +22 -0
  28. package/dist/error-handler.example.js +141 -0
  29. package/dist/error-handler.js +139 -0
  30. package/dist/ghidra-analysis-status.d.ts +49 -0
  31. package/dist/ghidra-analysis-status.js +178 -0
  32. package/dist/ghidra-config.d.ts +134 -0
  33. package/dist/ghidra-config.js +464 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.js +200 -0
  36. package/dist/job-queue.d.ts +169 -0
  37. package/dist/job-queue.js +407 -0
  38. package/dist/logger.d.ts +106 -0
  39. package/dist/logger.js +176 -0
  40. package/dist/policy-guard.d.ts +115 -0
  41. package/dist/policy-guard.js +243 -0
  42. package/dist/process-output.d.ts +15 -0
  43. package/dist/process-output.js +90 -0
  44. package/dist/prompts/function-explanation-review.d.ts +5 -0
  45. package/dist/prompts/function-explanation-review.js +64 -0
  46. package/dist/prompts/semantic-name-review.d.ts +5 -0
  47. package/dist/prompts/semantic-name-review.js +63 -0
  48. package/dist/runtime-correlation.d.ts +34 -0
  49. package/dist/runtime-correlation.js +279 -0
  50. package/dist/runtime-paths.d.ts +3 -0
  51. package/dist/runtime-paths.js +11 -0
  52. package/dist/selection-diff.d.ts +667 -0
  53. package/dist/selection-diff.js +53 -0
  54. package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
  55. package/dist/semantic-name-suggestion-artifacts.js +314 -0
  56. package/dist/server.d.ts +129 -0
  57. package/dist/server.js +578 -0
  58. package/dist/tools/artifact-read.d.ts +235 -0
  59. package/dist/tools/artifact-read.js +317 -0
  60. package/dist/tools/artifacts-diff.d.ts +728 -0
  61. package/dist/tools/artifacts-diff.js +304 -0
  62. package/dist/tools/artifacts-list.d.ts +515 -0
  63. package/dist/tools/artifacts-list.js +389 -0
  64. package/dist/tools/attack-map.d.ts +290 -0
  65. package/dist/tools/attack-map.js +519 -0
  66. package/dist/tools/cache-observability.d.ts +4 -0
  67. package/dist/tools/cache-observability.js +36 -0
  68. package/dist/tools/code-function-cfg.d.ts +50 -0
  69. package/dist/tools/code-function-cfg.js +102 -0
  70. package/dist/tools/code-function-decompile.d.ts +55 -0
  71. package/dist/tools/code-function-decompile.js +103 -0
  72. package/dist/tools/code-function-disassemble.d.ts +43 -0
  73. package/dist/tools/code-function-disassemble.js +185 -0
  74. package/dist/tools/code-function-explain-apply.d.ts +255 -0
  75. package/dist/tools/code-function-explain-apply.js +225 -0
  76. package/dist/tools/code-function-explain-prepare.d.ts +535 -0
  77. package/dist/tools/code-function-explain-prepare.js +276 -0
  78. package/dist/tools/code-function-explain-review.d.ts +397 -0
  79. package/dist/tools/code-function-explain-review.js +589 -0
  80. package/dist/tools/code-function-rename-apply.d.ts +248 -0
  81. package/dist/tools/code-function-rename-apply.js +220 -0
  82. package/dist/tools/code-function-rename-prepare.d.ts +506 -0
  83. package/dist/tools/code-function-rename-prepare.js +279 -0
  84. package/dist/tools/code-function-rename-review.d.ts +574 -0
  85. package/dist/tools/code-function-rename-review.js +761 -0
  86. package/dist/tools/code-functions-list.d.ts +37 -0
  87. package/dist/tools/code-functions-list.js +91 -0
  88. package/dist/tools/code-functions-rank.d.ts +34 -0
  89. package/dist/tools/code-functions-rank.js +90 -0
  90. package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
  91. package/dist/tools/code-functions-reconstruct.js +2807 -0
  92. package/dist/tools/code-functions-search.d.ts +39 -0
  93. package/dist/tools/code-functions-search.js +90 -0
  94. package/dist/tools/code-reconstruct-export.d.ts +1212 -0
  95. package/dist/tools/code-reconstruct-export.js +4002 -0
  96. package/dist/tools/code-reconstruct-plan.d.ts +274 -0
  97. package/dist/tools/code-reconstruct-plan.js +342 -0
  98. package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
  99. package/dist/tools/dotnet-metadata-extract.js +355 -0
  100. package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
  101. package/dist/tools/dotnet-reconstruct-export.js +1151 -0
  102. package/dist/tools/dotnet-types-list.d.ts +325 -0
  103. package/dist/tools/dotnet-types-list.js +201 -0
  104. package/dist/tools/dynamic-dependencies.d.ts +115 -0
  105. package/dist/tools/dynamic-dependencies.js +213 -0
  106. package/dist/tools/dynamic-memory-import.d.ts +10 -0
  107. package/dist/tools/dynamic-memory-import.js +567 -0
  108. package/dist/tools/dynamic-trace-import.d.ts +10 -0
  109. package/dist/tools/dynamic-trace-import.js +235 -0
  110. package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
  111. package/dist/tools/entrypoint-fallback-disasm.js +89 -0
  112. package/dist/tools/ghidra-analyze.d.ts +88 -0
  113. package/dist/tools/ghidra-analyze.js +208 -0
  114. package/dist/tools/ghidra-health.d.ts +37 -0
  115. package/dist/tools/ghidra-health.js +212 -0
  116. package/dist/tools/ioc-export.d.ts +209 -0
  117. package/dist/tools/ioc-export.js +542 -0
  118. package/dist/tools/packer-detect.d.ts +165 -0
  119. package/dist/tools/packer-detect.js +284 -0
  120. package/dist/tools/pe-exports-extract.d.ts +175 -0
  121. package/dist/tools/pe-exports-extract.js +253 -0
  122. package/dist/tools/pe-fingerprint.d.ts +234 -0
  123. package/dist/tools/pe-fingerprint.js +269 -0
  124. package/dist/tools/pe-imports-extract.d.ts +105 -0
  125. package/dist/tools/pe-imports-extract.js +245 -0
  126. package/dist/tools/report-generate.d.ts +157 -0
  127. package/dist/tools/report-generate.js +457 -0
  128. package/dist/tools/report-summarize.d.ts +2131 -0
  129. package/dist/tools/report-summarize.js +596 -0
  130. package/dist/tools/runtime-detect.d.ts +135 -0
  131. package/dist/tools/runtime-detect.js +247 -0
  132. package/dist/tools/sample-ingest.d.ts +94 -0
  133. package/dist/tools/sample-ingest.js +327 -0
  134. package/dist/tools/sample-profile-get.d.ts +183 -0
  135. package/dist/tools/sample-profile-get.js +121 -0
  136. package/dist/tools/sandbox-execute.d.ts +441 -0
  137. package/dist/tools/sandbox-execute.js +392 -0
  138. package/dist/tools/strings-extract.d.ts +375 -0
  139. package/dist/tools/strings-extract.js +314 -0
  140. package/dist/tools/strings-floss-decode.d.ts +143 -0
  141. package/dist/tools/strings-floss-decode.js +259 -0
  142. package/dist/tools/system-health.d.ts +434 -0
  143. package/dist/tools/system-health.js +446 -0
  144. package/dist/tools/task-cancel.d.ts +21 -0
  145. package/dist/tools/task-cancel.js +70 -0
  146. package/dist/tools/task-status.d.ts +27 -0
  147. package/dist/tools/task-status.js +106 -0
  148. package/dist/tools/task-sweep.d.ts +22 -0
  149. package/dist/tools/task-sweep.js +77 -0
  150. package/dist/tools/tool-help.d.ts +340 -0
  151. package/dist/tools/tool-help.js +261 -0
  152. package/dist/tools/yara-scan.d.ts +554 -0
  153. package/dist/tools/yara-scan.js +313 -0
  154. package/dist/types.d.ts +266 -0
  155. package/dist/types.js +41 -0
  156. package/dist/worker-pool.d.ts +204 -0
  157. package/dist/worker-pool.js +650 -0
  158. package/dist/workflows/deep-static.d.ts +104 -0
  159. package/dist/workflows/deep-static.js +276 -0
  160. package/dist/workflows/function-explanation-review.d.ts +655 -0
  161. package/dist/workflows/function-explanation-review.js +440 -0
  162. package/dist/workflows/reconstruct.d.ts +2053 -0
  163. package/dist/workflows/reconstruct.js +666 -0
  164. package/dist/workflows/semantic-name-review.d.ts +2418 -0
  165. package/dist/workflows/semantic-name-review.js +521 -0
  166. package/dist/workflows/triage.d.ts +659 -0
  167. package/dist/workflows/triage.js +1374 -0
  168. package/dist/workspace-manager.d.ts +150 -0
  169. package/dist/workspace-manager.js +411 -0
  170. package/ghidra_scripts/DecompileFunction.java +487 -0
  171. package/ghidra_scripts/DecompileFunction.py +150 -0
  172. package/ghidra_scripts/ExtractCFG.java +256 -0
  173. package/ghidra_scripts/ExtractCFG.py +233 -0
  174. package/ghidra_scripts/ExtractFunctions.java +442 -0
  175. package/ghidra_scripts/ExtractFunctions.py +101 -0
  176. package/ghidra_scripts/README.md +125 -0
  177. package/ghidra_scripts/SearchFunctionReferences.java +380 -0
  178. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
  179. package/helpers/DotNetMetadataProbe/Program.cs +566 -0
  180. package/install-to-codex.ps1 +178 -0
  181. package/install-to-copilot.ps1 +303 -0
  182. package/package.json +101 -0
  183. package/requirements.txt +9 -0
  184. package/workers/requirements-dynamic.txt +11 -0
  185. package/workers/requirements.txt +8 -0
  186. package/workers/speakeasy_compat.py +175 -0
  187. package/workers/static_worker.py +5183 -0
  188. package/workers/yara_rules/default.yar +33 -0
  189. package/workers/yara_rules/malware_families.yar +93 -0
  190. package/workers/yara_rules/packers.yar +80 -0
@@ -0,0 +1,589 @@
1
+ import { z } from 'zod';
2
+ import { createCodeFunctionExplainPrepareHandler } from './code-function-explain-prepare.js';
3
+ import { createCodeFunctionExplainApplyHandler } from './code-function-explain-apply.js';
4
+ const TOOL_NAME = 'code.function.explain.review';
5
+ const ReviewExplanationSchema = z
6
+ .object({
7
+ address_or_function: z.string().optional(),
8
+ address: z.string().optional(),
9
+ function: z.string().optional(),
10
+ summary: z.string().min(1).max(1200),
11
+ behavior: z.string().min(1).max(160),
12
+ confidence: z.number().min(0).max(1),
13
+ assumptions: z.array(z.string()).optional().default([]),
14
+ evidence_used: z.array(z.string()).optional().default([]),
15
+ rewrite_guidance: z.union([z.string().min(1), z.array(z.string().min(1))]).optional(),
16
+ })
17
+ .refine((value) => Boolean(value.address_or_function?.trim()) ||
18
+ Boolean(value.address?.trim()) ||
19
+ Boolean(value.function?.trim()), {
20
+ message: 'Each explanation must provide address_or_function, address, or function.',
21
+ });
22
+ const ReviewExplanationPayloadSchema = z.object({
23
+ explanations: z.array(ReviewExplanationSchema).min(1),
24
+ });
25
+ export const codeFunctionExplainReviewInputSchema = z
26
+ .object({
27
+ sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
28
+ address: z.string().optional().describe('Optional specific function address'),
29
+ symbol: z.string().optional().describe('Optional specific function symbol'),
30
+ topk: z
31
+ .number()
32
+ .int()
33
+ .min(1)
34
+ .max(20)
35
+ .default(6)
36
+ .describe('When address/symbol not provided, review up to top-K reconstructed functions'),
37
+ max_functions: z
38
+ .number()
39
+ .int()
40
+ .min(1)
41
+ .max(20)
42
+ .default(6)
43
+ .describe('Maximum number of functions included in the explanation bundle'),
44
+ include_resolved: z
45
+ .boolean()
46
+ .default(true)
47
+ .describe('Include already resolved functions so the external LLM can explain stable and unresolved ones together'),
48
+ analysis_goal: z
49
+ .string()
50
+ .min(1)
51
+ .max(400)
52
+ .default('Explain the prepared functions in plain language and propose evidence-grounded rewrite guidance.')
53
+ .describe('Human-readable analysis goal injected into the MCP prompt and sampling request'),
54
+ session_tag: z
55
+ .string()
56
+ .optional()
57
+ .describe('Optional semantic explanation session tag used for artifact grouping'),
58
+ evidence_scope: z
59
+ .enum(['all', 'latest', 'session'])
60
+ .default('all')
61
+ .describe('Runtime evidence scope forwarded to prepare'),
62
+ evidence_session_tag: z
63
+ .string()
64
+ .optional()
65
+ .describe('Optional runtime evidence session selector used when evidence_scope=session or to narrow all/latest results'),
66
+ persist_artifact: z
67
+ .boolean()
68
+ .default(true)
69
+ .describe('Persist the prepare bundle artifact before requesting external explanation review'),
70
+ auto_apply: z
71
+ .boolean()
72
+ .default(true)
73
+ .describe('Persist accepted explanations automatically via code.function.explain.apply'),
74
+ temperature: z
75
+ .number()
76
+ .min(0)
77
+ .max(1)
78
+ .default(0.2)
79
+ .describe('Sampling temperature passed to the connected MCP client'),
80
+ max_tokens: z
81
+ .number()
82
+ .int()
83
+ .min(200)
84
+ .max(8000)
85
+ .default(2200)
86
+ .describe('Maximum sampling tokens requested from the connected MCP client'),
87
+ include_context: z
88
+ .enum(['none', 'thisServer', 'allServers'])
89
+ .default('none')
90
+ .describe('Requested MCP sampling context scope; clients may ignore this preference'),
91
+ model_hint: z
92
+ .string()
93
+ .min(1)
94
+ .max(120)
95
+ .optional()
96
+ .describe('Optional advisory model-family hint for client-mediated MCP sampling'),
97
+ cost_priority: z
98
+ .number()
99
+ .min(0)
100
+ .max(1)
101
+ .default(0.1)
102
+ .describe('Advisory model selection preference for sampling cost'),
103
+ speed_priority: z
104
+ .number()
105
+ .min(0)
106
+ .max(1)
107
+ .default(0.2)
108
+ .describe('Advisory model selection preference for sampling latency'),
109
+ intelligence_priority: z
110
+ .number()
111
+ .min(0)
112
+ .max(1)
113
+ .default(0.95)
114
+ .describe('Advisory model selection preference for reasoning quality'),
115
+ system_prompt: z
116
+ .string()
117
+ .min(1)
118
+ .max(800)
119
+ .optional()
120
+ .describe('Optional extra system prompt for the client-mediated explanation review'),
121
+ })
122
+ .refine((value) => value.evidence_scope !== 'session' || Boolean(value.evidence_session_tag?.trim()), {
123
+ message: 'evidence_session_tag is required when evidence_scope=session',
124
+ path: ['evidence_session_tag'],
125
+ });
126
+ export const codeFunctionExplainReviewOutputSchema = z.object({
127
+ ok: z.boolean(),
128
+ data: z
129
+ .object({
130
+ sample_id: z.string(),
131
+ review_status: z.enum([
132
+ 'sampled_and_applied',
133
+ 'sampled_only',
134
+ 'prompt_contract_only',
135
+ 'no_targets',
136
+ 'sampling_parse_failed',
137
+ ]),
138
+ prompt_name: z.literal('reverse.function_explanation_review'),
139
+ prompt_arguments: z.object({
140
+ analysis_goal: z.string(),
141
+ prepared_bundle_json: z.string(),
142
+ }),
143
+ task_prompt: z.string(),
144
+ client: z.object({
145
+ name: z.string().nullable(),
146
+ version: z.string().nullable(),
147
+ sampling_available: z.boolean(),
148
+ }),
149
+ prepare: z.object({
150
+ prepared_count: z.number().int().nonnegative(),
151
+ artifact_id: z.string().nullable(),
152
+ }),
153
+ sampling: z.object({
154
+ attempted: z.boolean(),
155
+ model: z.string().nullable(),
156
+ stop_reason: z.string().nullable(),
157
+ response_text: z.string().nullable(),
158
+ parsed_explanation_count: z.number().int().nonnegative(),
159
+ }),
160
+ apply: z.object({
161
+ attempted: z.boolean(),
162
+ accepted_count: z.number().int().nonnegative(),
163
+ rejected_count: z.number().int().nonnegative(),
164
+ artifact_id: z.string().nullable(),
165
+ }),
166
+ confidence_policy: z.object({
167
+ calibrated: z.boolean(),
168
+ explanation_scores_are_heuristic: z.boolean(),
169
+ meaning: z.string(),
170
+ }),
171
+ next_steps: z.array(z.string()),
172
+ })
173
+ .optional(),
174
+ warnings: z.array(z.string()).optional(),
175
+ errors: z.array(z.string()).optional(),
176
+ artifacts: z.array(z.any()).optional(),
177
+ metrics: z
178
+ .object({
179
+ elapsed_ms: z.number(),
180
+ tool: z.string(),
181
+ })
182
+ .optional(),
183
+ });
184
+ export const codeFunctionExplainReviewToolDefinition = {
185
+ name: TOOL_NAME,
186
+ description: 'Use MCP client-mediated sampling to request an external LLM explanation review, then optionally persist the resulting function explanations.',
187
+ inputSchema: codeFunctionExplainReviewInputSchema,
188
+ outputSchema: codeFunctionExplainReviewOutputSchema,
189
+ };
190
+ function extractTextBlocks(result) {
191
+ const blocks = Array.isArray(result.content) ? result.content : [result.content];
192
+ return blocks
193
+ .filter((block) => block?.type === 'text')
194
+ .map((block) => block.text || '')
195
+ .join('\n')
196
+ .trim();
197
+ }
198
+ function extractJsonCandidates(rawText) {
199
+ const candidates = [];
200
+ const trimmed = rawText.trim();
201
+ if (trimmed.length > 0) {
202
+ candidates.push(trimmed);
203
+ }
204
+ const fencedMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
205
+ if (fencedMatch?.[1]) {
206
+ candidates.push(fencedMatch[1].trim());
207
+ }
208
+ const firstBrace = trimmed.indexOf('{');
209
+ const lastBrace = trimmed.lastIndexOf('}');
210
+ if (firstBrace >= 0 && lastBrace > firstBrace) {
211
+ candidates.push(trimmed.slice(firstBrace, lastBrace + 1));
212
+ }
213
+ const firstBracket = trimmed.indexOf('[');
214
+ const lastBracket = trimmed.lastIndexOf(']');
215
+ if (firstBracket >= 0 && lastBracket > firstBracket) {
216
+ candidates.push(trimmed.slice(firstBracket, lastBracket + 1));
217
+ }
218
+ return Array.from(new Set(candidates.filter((item) => item.length > 0)));
219
+ }
220
+ function parseSamplingExplanations(rawText) {
221
+ const candidates = extractJsonCandidates(rawText);
222
+ for (const candidate of candidates) {
223
+ try {
224
+ const parsed = JSON.parse(candidate);
225
+ if (Array.isArray(parsed)) {
226
+ return ReviewExplanationPayloadSchema.parse({ explanations: parsed }).explanations;
227
+ }
228
+ return ReviewExplanationPayloadSchema.parse(parsed).explanations;
229
+ }
230
+ catch {
231
+ continue;
232
+ }
233
+ }
234
+ throw new Error('Sampling response could not be parsed as strict JSON explanations. Return {"explanations":[...]} only.');
235
+ }
236
+ function buildSamplingRequest(input, taskPrompt) {
237
+ return {
238
+ messages: [
239
+ {
240
+ role: 'user',
241
+ content: {
242
+ type: 'text',
243
+ text: taskPrompt,
244
+ },
245
+ },
246
+ ],
247
+ systemPrompt: input.system_prompt ||
248
+ 'You are an evidence-grounded reverse-engineering assistant. Return strict JSON only.',
249
+ includeContext: input.include_context,
250
+ maxTokens: input.max_tokens,
251
+ temperature: input.temperature,
252
+ modelPreferences: {
253
+ hints: input.model_hint ? [{ name: input.model_hint }] : undefined,
254
+ costPriority: input.cost_priority,
255
+ speedPriority: input.speed_priority,
256
+ intelligencePriority: input.intelligence_priority,
257
+ },
258
+ };
259
+ }
260
+ function buildExplanationConfidencePolicy() {
261
+ return {
262
+ calibrated: false,
263
+ explanation_scores_are_heuristic: true,
264
+ meaning: 'Explanation confidence values rank evidence support strength only. They do not prove semantic equivalence or recover original source-level intent with calibrated probability.',
265
+ };
266
+ }
267
+ export function createCodeFunctionExplainReviewHandler(workspaceManager, database, cacheManager, mcpServer, dependencies) {
268
+ const prepareHandler = dependencies?.prepareHandler ||
269
+ createCodeFunctionExplainPrepareHandler(workspaceManager, database, cacheManager);
270
+ const applyHandler = dependencies?.applyHandler ||
271
+ createCodeFunctionExplainApplyHandler(workspaceManager, database);
272
+ const samplingRequester = dependencies?.samplingRequester ||
273
+ (mcpServer ? (params) => mcpServer.createMessage(params) : undefined);
274
+ const clientCapabilitiesProvider = dependencies?.clientCapabilitiesProvider ||
275
+ (mcpServer ? () => mcpServer.getClientCapabilities() : undefined);
276
+ const clientVersionProvider = dependencies?.clientVersionProvider ||
277
+ (mcpServer ? () => mcpServer.getClientVersion() : undefined);
278
+ return async (args) => {
279
+ const startTime = Date.now();
280
+ const warnings = [];
281
+ const artifacts = [];
282
+ try {
283
+ const input = codeFunctionExplainReviewInputSchema.parse(args);
284
+ const prepareResult = await prepareHandler({
285
+ sample_id: input.sample_id,
286
+ address: input.address,
287
+ symbol: input.symbol,
288
+ topk: input.topk,
289
+ max_functions: input.max_functions,
290
+ include_resolved: input.include_resolved,
291
+ analysis_goal: input.analysis_goal,
292
+ persist_artifact: input.persist_artifact,
293
+ session_tag: input.session_tag,
294
+ evidence_scope: input.evidence_scope,
295
+ evidence_session_tag: input.evidence_session_tag,
296
+ });
297
+ warnings.push(...(prepareResult.warnings || []));
298
+ artifacts.push(...(prepareResult.artifacts || []));
299
+ if (!prepareResult.ok) {
300
+ return {
301
+ ok: false,
302
+ errors: prepareResult.errors || ['code.function.explain.prepare failed'],
303
+ warnings: warnings.length > 0 ? warnings : undefined,
304
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
305
+ metrics: {
306
+ elapsed_ms: Date.now() - startTime,
307
+ tool: TOOL_NAME,
308
+ },
309
+ };
310
+ }
311
+ const prepareData = (prepareResult.data || {});
312
+ const preparedCount = Number(prepareData.prepared_count || 0);
313
+ const prepareArtifactId = prepareData.artifact?.id || null;
314
+ const promptArguments = prepareData.prompt_arguments;
315
+ const taskPrompt = prepareData.task_prompt;
316
+ const clientCapabilities = clientCapabilitiesProvider?.();
317
+ const samplingAvailable = Boolean(clientCapabilities?.sampling && samplingRequester);
318
+ const clientVersion = clientVersionProvider?.();
319
+ if (preparedCount === 0) {
320
+ return {
321
+ ok: true,
322
+ data: {
323
+ sample_id: input.sample_id,
324
+ review_status: 'no_targets',
325
+ prompt_name: 'reverse.function_explanation_review',
326
+ prompt_arguments: promptArguments,
327
+ task_prompt: taskPrompt,
328
+ client: {
329
+ name: clientVersion?.name || null,
330
+ version: clientVersion?.version || null,
331
+ sampling_available: samplingAvailable,
332
+ },
333
+ prepare: {
334
+ prepared_count: 0,
335
+ artifact_id: prepareArtifactId,
336
+ },
337
+ sampling: {
338
+ attempted: false,
339
+ model: null,
340
+ stop_reason: null,
341
+ response_text: null,
342
+ parsed_explanation_count: 0,
343
+ },
344
+ apply: {
345
+ attempted: false,
346
+ accepted_count: 0,
347
+ rejected_count: 0,
348
+ artifact_id: null,
349
+ },
350
+ confidence_policy: buildExplanationConfidencePolicy(),
351
+ next_steps: [
352
+ 'increase topk or max_functions',
353
+ 'target a specific address or symbol for explanation review',
354
+ ],
355
+ },
356
+ warnings: warnings.length > 0 ? warnings : undefined,
357
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
358
+ metrics: {
359
+ elapsed_ms: Date.now() - startTime,
360
+ tool: TOOL_NAME,
361
+ },
362
+ };
363
+ }
364
+ if (!samplingAvailable) {
365
+ warnings.push('Connected MCP client did not advertise sampling support; returning prompt contract only.');
366
+ return {
367
+ ok: true,
368
+ data: {
369
+ sample_id: input.sample_id,
370
+ review_status: 'prompt_contract_only',
371
+ prompt_name: 'reverse.function_explanation_review',
372
+ prompt_arguments: promptArguments,
373
+ task_prompt: taskPrompt,
374
+ client: {
375
+ name: clientVersion?.name || null,
376
+ version: clientVersion?.version || null,
377
+ sampling_available: false,
378
+ },
379
+ prepare: {
380
+ prepared_count: preparedCount,
381
+ artifact_id: prepareArtifactId,
382
+ },
383
+ sampling: {
384
+ attempted: false,
385
+ model: null,
386
+ stop_reason: null,
387
+ response_text: null,
388
+ parsed_explanation_count: 0,
389
+ },
390
+ apply: {
391
+ attempted: false,
392
+ accepted_count: 0,
393
+ rejected_count: 0,
394
+ artifact_id: null,
395
+ },
396
+ confidence_policy: buildExplanationConfidencePolicy(),
397
+ next_steps: [
398
+ 'call prompts/get for reverse.function_explanation_review with the returned prompt arguments',
399
+ 'send the prompt to any MCP-capable tool-calling LLM client that supports sampling or manual prompt execution',
400
+ 'pass the JSON result to code.function.explain.apply',
401
+ ],
402
+ },
403
+ warnings: warnings.length > 0 ? warnings : undefined,
404
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
405
+ metrics: {
406
+ elapsed_ms: Date.now() - startTime,
407
+ tool: TOOL_NAME,
408
+ },
409
+ };
410
+ }
411
+ const samplingResult = await samplingRequester(buildSamplingRequest(input, taskPrompt));
412
+ const responseText = extractTextBlocks(samplingResult);
413
+ const samplingModel = samplingResult?.model || null;
414
+ const stopReason = samplingResult?.stopReason || null;
415
+ let parsedExplanations = [];
416
+ try {
417
+ parsedExplanations = parseSamplingExplanations(responseText);
418
+ }
419
+ catch (error) {
420
+ warnings.push(error instanceof Error ? error.message : String(error));
421
+ return {
422
+ ok: true,
423
+ data: {
424
+ sample_id: input.sample_id,
425
+ review_status: 'sampling_parse_failed',
426
+ prompt_name: 'reverse.function_explanation_review',
427
+ prompt_arguments: promptArguments,
428
+ task_prompt: taskPrompt,
429
+ client: {
430
+ name: clientVersion?.name || null,
431
+ version: clientVersion?.version || null,
432
+ sampling_available: true,
433
+ },
434
+ prepare: {
435
+ prepared_count: preparedCount,
436
+ artifact_id: prepareArtifactId,
437
+ },
438
+ sampling: {
439
+ attempted: true,
440
+ model: samplingModel,
441
+ stop_reason: stopReason,
442
+ response_text: responseText || null,
443
+ parsed_explanation_count: 0,
444
+ },
445
+ apply: {
446
+ attempted: false,
447
+ accepted_count: 0,
448
+ rejected_count: 0,
449
+ artifact_id: null,
450
+ },
451
+ confidence_policy: buildExplanationConfidencePolicy(),
452
+ next_steps: [
453
+ 'inspect the sampling response text and ensure the client returned strict JSON',
454
+ 'rerun code.function.explain.review or use code.function.explain.apply manually with corrected JSON',
455
+ ],
456
+ },
457
+ warnings: warnings.length > 0 ? warnings : undefined,
458
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
459
+ metrics: {
460
+ elapsed_ms: Date.now() - startTime,
461
+ tool: TOOL_NAME,
462
+ },
463
+ };
464
+ }
465
+ if (!input.auto_apply) {
466
+ return {
467
+ ok: true,
468
+ data: {
469
+ sample_id: input.sample_id,
470
+ review_status: 'sampled_only',
471
+ prompt_name: 'reverse.function_explanation_review',
472
+ prompt_arguments: promptArguments,
473
+ task_prompt: taskPrompt,
474
+ client: {
475
+ name: clientVersion?.name || null,
476
+ version: clientVersion?.version || null,
477
+ sampling_available: true,
478
+ },
479
+ prepare: {
480
+ prepared_count: preparedCount,
481
+ artifact_id: prepareArtifactId,
482
+ },
483
+ sampling: {
484
+ attempted: true,
485
+ model: samplingModel,
486
+ stop_reason: stopReason,
487
+ response_text: responseText || null,
488
+ parsed_explanation_count: parsedExplanations.length,
489
+ },
490
+ apply: {
491
+ attempted: false,
492
+ accepted_count: 0,
493
+ rejected_count: 0,
494
+ artifact_id: null,
495
+ },
496
+ confidence_policy: buildExplanationConfidencePolicy(),
497
+ next_steps: [
498
+ 'pass the parsed JSON payload to code.function.explain.apply',
499
+ 'rerun code.reconstruct.export after apply to propagate explanation summaries into rewrite output',
500
+ ],
501
+ },
502
+ warnings: warnings.length > 0 ? warnings : undefined,
503
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
504
+ metrics: {
505
+ elapsed_ms: Date.now() - startTime,
506
+ tool: TOOL_NAME,
507
+ },
508
+ };
509
+ }
510
+ const applyResult = await applyHandler({
511
+ sample_id: input.sample_id,
512
+ explanations: parsedExplanations,
513
+ client_name: clientVersion?.name,
514
+ model_name: samplingModel || undefined,
515
+ prepare_artifact_id: prepareArtifactId || undefined,
516
+ session_tag: input.session_tag,
517
+ });
518
+ if (!applyResult.ok) {
519
+ return {
520
+ ok: false,
521
+ errors: applyResult.errors || ['code.function.explain.apply failed'],
522
+ warnings: [...warnings, ...(applyResult.warnings || [])],
523
+ artifacts: [...artifacts, ...(applyResult.artifacts || [])],
524
+ metrics: {
525
+ elapsed_ms: Date.now() - startTime,
526
+ tool: TOOL_NAME,
527
+ },
528
+ };
529
+ }
530
+ warnings.push(...(applyResult.warnings || []));
531
+ artifacts.push(...(applyResult.artifacts || []));
532
+ const applyData = (applyResult.data || {});
533
+ return {
534
+ ok: true,
535
+ data: {
536
+ sample_id: input.sample_id,
537
+ review_status: 'sampled_and_applied',
538
+ prompt_name: 'reverse.function_explanation_review',
539
+ prompt_arguments: promptArguments,
540
+ task_prompt: taskPrompt,
541
+ client: {
542
+ name: clientVersion?.name || null,
543
+ version: clientVersion?.version || null,
544
+ sampling_available: true,
545
+ },
546
+ prepare: {
547
+ prepared_count: preparedCount,
548
+ artifact_id: prepareArtifactId,
549
+ },
550
+ sampling: {
551
+ attempted: true,
552
+ model: samplingModel,
553
+ stop_reason: stopReason,
554
+ response_text: responseText || null,
555
+ parsed_explanation_count: parsedExplanations.length,
556
+ },
557
+ apply: {
558
+ attempted: true,
559
+ accepted_count: Number(applyData.accepted_count || 0),
560
+ rejected_count: Number(applyData.rejected_count || 0),
561
+ artifact_id: applyData.artifact?.id || null,
562
+ },
563
+ confidence_policy: buildExplanationConfidencePolicy(),
564
+ next_steps: [
565
+ 'rerun code.reconstruct.export to propagate explanation summaries into rewrite output',
566
+ 'rerun report.generate or report.summarize if you want explanation artifacts reflected in analyst-facing output',
567
+ ],
568
+ },
569
+ warnings: warnings.length > 0 ? warnings : undefined,
570
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
571
+ metrics: {
572
+ elapsed_ms: Date.now() - startTime,
573
+ tool: TOOL_NAME,
574
+ },
575
+ };
576
+ }
577
+ catch (error) {
578
+ return {
579
+ ok: false,
580
+ errors: [error instanceof Error ? error.message : String(error)],
581
+ metrics: {
582
+ elapsed_ms: Date.now() - startTime,
583
+ tool: TOOL_NAME,
584
+ },
585
+ };
586
+ }
587
+ };
588
+ }
589
+ //# sourceMappingURL=code-function-explain-review.js.map