windows-exe-decompiler-mcp-server 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE_INSTALLATION.md +129 -0
- package/CODEX_INSTALLATION.md +25 -1
- package/COPILOT_INSTALLATION.md +29 -1
- package/LICENSE +21 -21
- package/README.md +232 -110
- package/dist/analysis-provenance.d.ts +50 -2
- package/dist/analysis-provenance.js +1 -0
- package/dist/analysis-task-runner.js +92 -0
- package/dist/config.d.ts +35 -16
- package/dist/config.js +37 -3
- package/dist/database.js +115 -115
- package/dist/decompiler-worker.d.ts +10 -5
- package/dist/decompiler-worker.js +240 -58
- package/dist/dynamic-trace.d.ts +6 -0
- package/dist/dynamic-trace.js +52 -1
- package/dist/ghidra-config.js +17 -1
- package/dist/index.js +38 -8
- package/dist/pe-runtime-functions.d.ts +90 -0
- package/dist/pe-runtime-functions.js +452 -0
- package/dist/prompts/module-reconstruction-review.d.ts +5 -0
- package/dist/prompts/module-reconstruction-review.js +65 -0
- package/dist/runtime-correlation.d.ts +5 -0
- package/dist/runtime-correlation.js +179 -2
- package/dist/sample-workspace.d.ts +28 -0
- package/dist/sample-workspace.js +186 -0
- package/dist/semantic-name-suggestion-artifacts.d.ts +56 -0
- package/dist/semantic-name-suggestion-artifacts.js +87 -0
- package/dist/server.js +1 -1
- package/dist/setup-guidance.d.ts +73 -0
- package/dist/setup-guidance.js +211 -0
- package/dist/tools/artifact-read.d.ts +10 -10
- package/dist/tools/artifacts-diff.d.ts +8 -8
- package/dist/tools/artifacts-list.d.ts +16 -16
- package/dist/tools/attack-map.d.ts +8 -8
- package/dist/tools/binary-role-profile.d.ts +1197 -0
- package/dist/tools/binary-role-profile.js +575 -0
- package/dist/tools/code-function-explain-apply.d.ts +12 -12
- package/dist/tools/code-function-explain-prepare.d.ts +12 -12
- package/dist/tools/code-function-explain-review.d.ts +58 -58
- package/dist/tools/code-function-rename-apply.d.ts +12 -12
- package/dist/tools/code-function-rename-prepare.d.ts +14 -14
- package/dist/tools/code-function-rename-review.d.ts +66 -66
- package/dist/tools/code-functions-define.d.ts +337 -0
- package/dist/tools/code-functions-define.js +337 -0
- package/dist/tools/code-functions-list.js +1 -1
- package/dist/tools/code-functions-rank.js +1 -1
- package/dist/tools/code-functions-reconstruct.d.ts +368 -58
- package/dist/tools/code-functions-reconstruct.js +130 -10
- package/dist/tools/code-functions-smart-recover.d.ts +352 -0
- package/dist/tools/code-functions-smart-recover.js +190 -0
- package/dist/tools/code-module-review-apply.d.ts +233 -0
- package/dist/tools/code-module-review-apply.js +209 -0
- package/dist/tools/code-module-review-prepare.d.ts +756 -0
- package/dist/tools/code-module-review-prepare.js +343 -0
- package/dist/tools/code-module-review.d.ts +474 -0
- package/dist/tools/code-module-review.js +566 -0
- package/dist/tools/code-reconstruct-export.d.ts +193 -21
- package/dist/tools/code-reconstruct-export.js +578 -21
- package/dist/tools/code-reconstruct-plan.d.ts +20 -20
- package/dist/tools/com-role-profile.d.ts +353 -0
- package/dist/tools/com-role-profile.js +186 -0
- package/dist/tools/dll-export-profile.d.ts +625 -0
- package/dist/tools/dll-export-profile.js +230 -0
- package/dist/tools/dotnet-metadata-extract.d.ts +122 -122
- package/dist/tools/dotnet-reconstruct-export.d.ts +49 -49
- package/dist/tools/dotnet-types-list.d.ts +66 -66
- package/dist/tools/dynamic-dependencies.d.ts +144 -8
- package/dist/tools/dynamic-dependencies.js +20 -1
- package/dist/tools/dynamic-memory-import.js +105 -2
- package/dist/tools/ghidra-analyze.d.ts +20 -0
- package/dist/tools/ghidra-analyze.js +10 -2
- package/dist/tools/ghidra-health.d.ts +177 -0
- package/dist/tools/ghidra-health.js +28 -0
- package/dist/tools/ioc-export.d.ts +8 -8
- package/dist/tools/packer-detect.d.ts +14 -14
- package/dist/tools/packer-detect.js +6 -9
- package/dist/tools/pe-exports-extract.d.ts +8 -8
- package/dist/tools/pe-exports-extract.js +6 -9
- package/dist/tools/pe-fingerprint.d.ts +36 -36
- package/dist/tools/pe-imports-extract.d.ts +8 -8
- package/dist/tools/pe-imports-extract.js +6 -9
- package/dist/tools/pe-pdata-extract.d.ts +448 -0
- package/dist/tools/pe-pdata-extract.js +219 -0
- package/dist/tools/pe-symbols-recover.d.ts +227 -0
- package/dist/tools/pe-symbols-recover.js +316 -0
- package/dist/tools/report-generate.d.ts +15 -1
- package/dist/tools/report-generate.js +139 -16
- package/dist/tools/report-summarize.d.ts +2242 -8
- package/dist/tools/report-summarize.js +246 -10
- package/dist/tools/runtime-detect.d.ts +8 -8
- package/dist/tools/runtime-detect.js +2 -12
- package/dist/tools/rust-binary-analyze.d.ts +2047 -0
- package/dist/tools/rust-binary-analyze.js +443 -0
- package/dist/tools/sample-profile-get.d.ts +135 -16
- package/dist/tools/sample-profile-get.js +35 -32
- package/dist/tools/sandbox-execute.d.ts +10 -10
- package/dist/tools/strings-extract.d.ts +8 -8
- package/dist/tools/strings-extract.js +2 -12
- package/dist/tools/strings-floss-decode.d.ts +8 -8
- package/dist/tools/system-health.d.ts +172 -36
- package/dist/tools/system-health.js +14 -0
- package/dist/tools/system-setup-guide.d.ts +179 -0
- package/dist/tools/system-setup-guide.js +62 -0
- package/dist/tools/tool-help.d.ts +16 -16
- package/dist/tools/tool-help.js +172 -0
- package/dist/tools/yara-scan.d.ts +8 -8
- package/dist/types.d.ts +2 -0
- package/dist/workflows/function-explanation-review.d.ts +3034 -76
- package/dist/workflows/function-explanation-review.js +172 -42
- package/dist/workflows/function-index-recover.d.ts +333 -0
- package/dist/workflows/function-index-recover.js +291 -0
- package/dist/workflows/module-reconstruction-review.d.ts +3537 -0
- package/dist/workflows/module-reconstruction-review.js +354 -0
- package/dist/workflows/reconstruct.d.ts +2182 -223
- package/dist/workflows/reconstruct.js +575 -32
- package/dist/workflows/semantic-name-review.d.ts +1436 -85
- package/dist/workflows/semantic-name-review.js +150 -58
- package/dist/workflows/triage.d.ts +8 -8
- package/ghidra_scripts/DecompileFunction.java +487 -487
- package/ghidra_scripts/ExtractCFG.java +256 -256
- package/ghidra_scripts/ExtractFunctions.java +442 -442
- package/ghidra_scripts/README.md +112 -112
- package/ghidra_scripts/SearchFunctionReferences.java +380 -380
- package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -9
- package/helpers/DotNetMetadataProbe/Program.cs +566 -566
- package/install-to-claude.ps1 +432 -0
- package/install-to-codex.ps1 +256 -178
- package/install-to-copilot.ps1 +389 -303
- package/package.json +4 -2
- package/requirements.txt +9 -9
- package/workers/requirements-dynamic.txt +11 -11
- package/workers/requirements.txt +8 -8
- package/workers/speakeasy_compat.py +175 -175
- package/workers/static_worker.py +3474 -3474
|
@@ -109,6 +109,22 @@ export function deriveRuntimeStageCandidates(input) {
|
|
|
109
109
|
semanticCorpus.includes('overlay')) {
|
|
110
110
|
stages.add('scan_pe_layout');
|
|
111
111
|
}
|
|
112
|
+
if (behaviorTags.has('dll_lifecycle') ||
|
|
113
|
+
/\b(dllmain|dllregisterserver|dllunregisterserver|dllinstall|dllcanunloadnow|dll_process_attach|dll_thread_attach|disablethreadlibrarycalls)\b/i.test(semanticCorpus)) {
|
|
114
|
+
stages.add('dll_lifecycle');
|
|
115
|
+
}
|
|
116
|
+
if (behaviorTags.has('com_activation') ||
|
|
117
|
+
/\b(cocreateinstance|queryinterface|registerclassobject|dllgetclassobject|iclassfactory|clsid_|progid|inprocserver32|localserver32)\b/i.test(semanticCorpus)) {
|
|
118
|
+
stages.add('com_activation');
|
|
119
|
+
}
|
|
120
|
+
if (behaviorTags.has('export_dispatch') ||
|
|
121
|
+
/\b(export dispatch|forwarded export|ordinal|dispatch exported|dllcanunloadnow|dllgetclassobject)\b/i.test(semanticCorpus)) {
|
|
122
|
+
stages.add('export_dispatch');
|
|
123
|
+
}
|
|
124
|
+
if (behaviorTags.has('plugin_callback') ||
|
|
125
|
+
/\b(callback|plugin|hook|event sink|notify|host extension|host interaction)\b/i.test(semanticCorpus)) {
|
|
126
|
+
stages.add('callback_surface');
|
|
127
|
+
}
|
|
112
128
|
return Array.from(stages);
|
|
113
129
|
}
|
|
114
130
|
function regionMatchersForStage(stage) {
|
|
@@ -127,10 +143,110 @@ function regionMatchersForStage(stage) {
|
|
|
127
143
|
return [/registry/i, /key/i];
|
|
128
144
|
case 'scan_pe_layout':
|
|
129
145
|
return [/packer/i, /entropy/i, /section/i, /layout/i];
|
|
146
|
+
case 'dll_lifecycle':
|
|
147
|
+
return [/dll/i, /lifecycle/i, /attach/i, /detach/i, /module/i];
|
|
148
|
+
case 'com_activation':
|
|
149
|
+
return [/com/i, /class/i, /factory/i, /activation/i, /registration/i];
|
|
150
|
+
case 'export_dispatch':
|
|
151
|
+
return [/export/i, /dispatch/i, /ordinal/i, /forward/i];
|
|
152
|
+
case 'callback_surface':
|
|
153
|
+
return [/callback/i, /plugin/i, /hook/i, /notify/i, /host/i];
|
|
130
154
|
default:
|
|
131
155
|
return [];
|
|
132
156
|
}
|
|
133
157
|
}
|
|
158
|
+
function protectionMatchersForStage(stage) {
|
|
159
|
+
switch (stage) {
|
|
160
|
+
case 'resolve_dynamic_apis':
|
|
161
|
+
return [/image/i, /r-x/i, /execute/i];
|
|
162
|
+
case 'prepare_remote_process_access':
|
|
163
|
+
return [/read_write/i, /write/i, /execute/i, /rwx/i];
|
|
164
|
+
case 'anti_analysis_checks':
|
|
165
|
+
case 'check_execution_environment':
|
|
166
|
+
return [/read/i, /image/i];
|
|
167
|
+
case 'file_operations':
|
|
168
|
+
return [/file/i, /read_write/i, /container/i];
|
|
169
|
+
case 'registry_operations':
|
|
170
|
+
case 'stage_registry_state':
|
|
171
|
+
return [/read_write/i, /data/i];
|
|
172
|
+
case 'scan_pe_layout':
|
|
173
|
+
return [/image/i, /container/i, /read/i];
|
|
174
|
+
case 'dll_lifecycle':
|
|
175
|
+
case 'com_activation':
|
|
176
|
+
case 'export_dispatch':
|
|
177
|
+
case 'callback_surface':
|
|
178
|
+
return [/image/i, /read/i, /execute/i];
|
|
179
|
+
default:
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function ownerMatchersForStage(stage) {
|
|
184
|
+
switch (stage) {
|
|
185
|
+
case 'resolve_dynamic_apis':
|
|
186
|
+
case 'prepare_remote_process_access':
|
|
187
|
+
return [/kernel32/i, /kernelbase/i, /ntdll/i, /\.exe$/i, /\.dll$/i];
|
|
188
|
+
case 'anti_analysis_checks':
|
|
189
|
+
case 'check_execution_environment':
|
|
190
|
+
return [/kernel32/i, /kernelbase/i, /ntdll/i];
|
|
191
|
+
case 'file_operations':
|
|
192
|
+
return [/kernel32/i, /kernelbase/i, /shlwapi/i];
|
|
193
|
+
case 'registry_operations':
|
|
194
|
+
case 'stage_registry_state':
|
|
195
|
+
return [/advapi32/i];
|
|
196
|
+
case 'scan_pe_layout':
|
|
197
|
+
return [/\.exe$/i, /\.dll$/i];
|
|
198
|
+
case 'dll_lifecycle':
|
|
199
|
+
return [/\.dll$/i, /\.ocx$/i, /\.cpl$/i];
|
|
200
|
+
case 'com_activation':
|
|
201
|
+
return [/ole32/i, /oleaut32/i, /combase/i, /rpcrt4/i, /\.dll$/i];
|
|
202
|
+
case 'export_dispatch':
|
|
203
|
+
return [/\.dll$/i, /\.ocx$/i, /\.cpl$/i];
|
|
204
|
+
case 'callback_surface':
|
|
205
|
+
return [/plugin/i, /host/i, /extension/i, /addin/i, /\.dll$/i];
|
|
206
|
+
default:
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function segmentMatchersForStage(stage) {
|
|
211
|
+
switch (stage) {
|
|
212
|
+
case 'scan_pe_layout':
|
|
213
|
+
return [/\.pdata/i, /\.xdata/i, /\.text/i, /\.rsrc/i];
|
|
214
|
+
case 'dll_lifecycle':
|
|
215
|
+
return [/\.tls/i, /\.crt/i, /\.rdata/i, /init/i];
|
|
216
|
+
case 'com_activation':
|
|
217
|
+
return [/\.idata/i, /\.rdata/i, /class/i, /factory/i];
|
|
218
|
+
case 'export_dispatch':
|
|
219
|
+
return [/\.edata/i, /export/i, /dispatch/i];
|
|
220
|
+
case 'callback_surface':
|
|
221
|
+
return [/callback/i, /hook/i, /event/i, /notify/i];
|
|
222
|
+
case 'prepare_remote_process_access':
|
|
223
|
+
return [/\.text/i, /\.data/i];
|
|
224
|
+
default:
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function collectMatchedMetadataValues(values, candidateStages, input, matchersForStage, fallbackMatcher) {
|
|
229
|
+
const semanticCorpus = buildSemanticCorpus(input);
|
|
230
|
+
const matched = new Set();
|
|
231
|
+
for (const value of values) {
|
|
232
|
+
const lowered = value.toLowerCase();
|
|
233
|
+
for (const stage of candidateStages) {
|
|
234
|
+
if (matchersForStage(stage).some((matcher) => matcher.test(lowered))) {
|
|
235
|
+
matched.add(value);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (fallbackMatcher?.(lowered, semanticCorpus)) {
|
|
239
|
+
matched.add(value);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return Array.from(matched).slice(0, 6);
|
|
243
|
+
}
|
|
244
|
+
function collectMatchedAddressRanges(matchedMemoryRegions, dynamicEvidence) {
|
|
245
|
+
if (matchedMemoryRegions.length === 0) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
return dedupe(dynamicEvidence.address_ranges || []).slice(0, Math.max(1, matchedMemoryRegions.length));
|
|
249
|
+
}
|
|
134
250
|
function collectMatchedMemoryRegions(candidateStages, input, dynamicEvidence) {
|
|
135
251
|
const semanticCorpus = buildSemanticCorpus(input);
|
|
136
252
|
const matched = new Set();
|
|
@@ -168,6 +284,11 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
|
|
|
168
284
|
const observedStages = new Set((dynamicEvidence.stages || []).map((item) => item.toLowerCase()));
|
|
169
285
|
const matchedStages = candidateStages.filter((item) => observedStages.has(item.toLowerCase()));
|
|
170
286
|
const matchedMemoryRegions = collectMatchedMemoryRegions(candidateStages, input, dynamicEvidence);
|
|
287
|
+
const matchedProtections = collectMatchedMetadataValues(dynamicEvidence.protections || [], candidateStages, input, protectionMatchersForStage, (lowered, semanticCorpus) => semanticCorpus.includes('remote') && /(read_write|write|execute|rwx)/i.test(lowered));
|
|
288
|
+
const matchedRegionOwners = collectMatchedMetadataValues(dynamicEvidence.region_owners || [], candidateStages, input, ownerMatchersForStage, (lowered, semanticCorpus) => semanticCorpus.includes('com') && /(ole32|oleaut32|combase|rpcrt4)/i.test(lowered));
|
|
289
|
+
const matchedObservedModules = collectMatchedMetadataValues(dynamicEvidence.observed_modules || [], candidateStages, input, ownerMatchersForStage, (lowered, semanticCorpus) => semanticCorpus.includes('com') && /(ole32|oleaut32|combase|rpcrt4)/i.test(lowered));
|
|
290
|
+
const matchedSegmentNames = collectMatchedMetadataValues(dynamicEvidence.segment_names || [], candidateStages, input, segmentMatchersForStage);
|
|
291
|
+
const matchedAddressRanges = collectMatchedAddressRanges(matchedMemoryRegions, dynamicEvidence);
|
|
171
292
|
const evidenceSources = summarizeEvidenceSources(dynamicEvidence);
|
|
172
293
|
const sourceNames = summarizeSourceNames(dynamicEvidence);
|
|
173
294
|
const provenanceLayers = summarizeProvenanceLayers(dynamicEvidence);
|
|
@@ -178,7 +299,13 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
|
|
|
178
299
|
(input.stringHints || []).length > 0 ? 'string_hint' : '',
|
|
179
300
|
input.semanticSummary ? 'semantic_summary' : '',
|
|
180
301
|
]);
|
|
181
|
-
if (matchedApis.length === 0 &&
|
|
302
|
+
if (matchedApis.length === 0 &&
|
|
303
|
+
matchedStages.length === 0 &&
|
|
304
|
+
matchedMemoryRegions.length === 0 &&
|
|
305
|
+
matchedProtections.length === 0 &&
|
|
306
|
+
matchedRegionOwners.length === 0 &&
|
|
307
|
+
matchedObservedModules.length === 0 &&
|
|
308
|
+
matchedSegmentNames.length === 0) {
|
|
182
309
|
return undefined;
|
|
183
310
|
}
|
|
184
311
|
const highSignalMatches = matchedApis.filter((item) => highSignalSet.has(normalizeRuntimeApiName(item).toLowerCase()));
|
|
@@ -192,6 +319,21 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
|
|
|
192
319
|
if (matchedMemoryRegions.length > 0) {
|
|
193
320
|
notes.push(`Runtime memory regions align with this function: ${matchedMemoryRegions.slice(0, 4).join(', ')}`);
|
|
194
321
|
}
|
|
322
|
+
if (matchedProtections.length > 0) {
|
|
323
|
+
notes.push(`Runtime protections align with this function: ${matchedProtections.slice(0, 4).join(', ')}`);
|
|
324
|
+
}
|
|
325
|
+
if (matchedRegionOwners.length > 0) {
|
|
326
|
+
notes.push(`Runtime region owners align with this function: ${matchedRegionOwners.slice(0, 4).join(', ')}`);
|
|
327
|
+
}
|
|
328
|
+
if (matchedObservedModules.length > 0) {
|
|
329
|
+
notes.push(`Runtime modules align with this function: ${matchedObservedModules.slice(0, 4).join(', ')}`);
|
|
330
|
+
}
|
|
331
|
+
if (matchedSegmentNames.length > 0) {
|
|
332
|
+
notes.push(`Runtime segments align with this function: ${matchedSegmentNames.slice(0, 4).join(', ')}`);
|
|
333
|
+
}
|
|
334
|
+
if (matchedAddressRanges.length > 0) {
|
|
335
|
+
notes.push(`Runtime address ranges associated with this function: ${matchedAddressRanges.slice(0, 3).join(', ')}`);
|
|
336
|
+
}
|
|
195
337
|
if (dynamicEvidence.executed) {
|
|
196
338
|
notes.push('Correlation includes executed runtime evidence, not just static or memory-only hints.');
|
|
197
339
|
}
|
|
@@ -225,6 +367,22 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
|
|
|
225
367
|
...(matchedMemoryRegions.some((item) => /network|socket|http|pipe|ipc/i.test(item))
|
|
226
368
|
? ['network_ops']
|
|
227
369
|
: []),
|
|
370
|
+
...(matchedRegionOwners.some((item) => /ole32|oleaut32|combase|rpcrt4/i.test(item)) ||
|
|
371
|
+
matchedObservedModules.some((item) => /ole32|oleaut32|combase|rpcrt4/i.test(item))
|
|
372
|
+
? ['com_activation']
|
|
373
|
+
: []),
|
|
374
|
+
...(matchedSegmentNames.some((item) => /\.edata|export|dispatch/i.test(item)) ||
|
|
375
|
+
matchedStages.some((item) => item.toLowerCase() === 'export_dispatch')
|
|
376
|
+
? ['export_dispatch']
|
|
377
|
+
: []),
|
|
378
|
+
...(matchedSegmentNames.some((item) => /\.tls|\.crt|init/i.test(item)) ||
|
|
379
|
+
matchedStages.some((item) => item.toLowerCase() === 'dll_lifecycle')
|
|
380
|
+
? ['dll_lifecycle']
|
|
381
|
+
: []),
|
|
382
|
+
...(matchedStages.some((item) => item.toLowerCase() === 'callback_surface') ||
|
|
383
|
+
matchedObservedModules.some((item) => /plugin|host|extension|addin/i.test(item))
|
|
384
|
+
? ['callback_surface']
|
|
385
|
+
: []),
|
|
228
386
|
]);
|
|
229
387
|
if (suggestedModules.length > 0) {
|
|
230
388
|
notes.push(`Suggested semantic modules: ${suggestedModules.slice(0, 4).join(', ')}`);
|
|
@@ -233,7 +391,9 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
|
|
|
233
391
|
Math.min(0.24, dedupe(matchedApis).length * 0.08) +
|
|
234
392
|
Math.min(0.16, dedupe(matchedStages).length * 0.06) +
|
|
235
393
|
Math.min(0.12, highSignalMatches.length * 0.05) +
|
|
236
|
-
Math.min(0.1, matchedMemoryRegions.length * 0.04)
|
|
394
|
+
Math.min(0.1, matchedMemoryRegions.length * 0.04) +
|
|
395
|
+
Math.min(0.07, matchedProtections.length * 0.02) +
|
|
396
|
+
Math.min(0.07, (matchedRegionOwners.length + matchedObservedModules.length + matchedSegmentNames.length) * 0.015), 0.45, 0.97);
|
|
237
397
|
return {
|
|
238
398
|
corroborated_apis: dedupe(matchedApis).slice(0, 8),
|
|
239
399
|
corroborated_stages: dedupe(matchedStages).slice(0, 6),
|
|
@@ -245,6 +405,11 @@ export function correlateFunctionWithRuntimeEvidence(input, dynamicEvidence) {
|
|
|
245
405
|
artifact_count: dynamicEvidence.artifact_count,
|
|
246
406
|
executed_artifact_count: dynamicEvidence.executed_artifact_count || 0,
|
|
247
407
|
matched_memory_regions: matchedMemoryRegions,
|
|
408
|
+
matched_protections: matchedProtections,
|
|
409
|
+
matched_address_ranges: matchedAddressRanges,
|
|
410
|
+
matched_region_owners: matchedRegionOwners,
|
|
411
|
+
matched_observed_modules: matchedObservedModules,
|
|
412
|
+
matched_segment_names: matchedSegmentNames,
|
|
248
413
|
suggested_modules: suggestedModules,
|
|
249
414
|
matched_by: matchedBy,
|
|
250
415
|
provenance_layers: provenanceLayers,
|
|
@@ -273,6 +438,18 @@ export function modulesSuggestedByRuntimeStages(stages = []) {
|
|
|
273
438
|
else if (stage === 'resolve_dynamic_apis') {
|
|
274
439
|
modules.add('process_ops');
|
|
275
440
|
}
|
|
441
|
+
else if (stage === 'dll_lifecycle') {
|
|
442
|
+
modules.add('dll_lifecycle');
|
|
443
|
+
}
|
|
444
|
+
else if (stage === 'com_activation') {
|
|
445
|
+
modules.add('com_activation');
|
|
446
|
+
}
|
|
447
|
+
else if (stage === 'export_dispatch') {
|
|
448
|
+
modules.add('export_dispatch');
|
|
449
|
+
}
|
|
450
|
+
else if (stage === 'callback_surface') {
|
|
451
|
+
modules.add('callback_surface');
|
|
452
|
+
}
|
|
276
453
|
}
|
|
277
454
|
return Array.from(modules);
|
|
278
455
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { WorkspaceManager } from './workspace-manager.js';
|
|
2
|
+
export type SampleWorkspaceStatus = 'ready' | 'workspace_missing' | 'original_dir_missing' | 'original_file_missing';
|
|
3
|
+
export interface SampleWorkspaceIntegrity {
|
|
4
|
+
status: SampleWorkspaceStatus;
|
|
5
|
+
workspace_root: string | null;
|
|
6
|
+
original_dir: string | null;
|
|
7
|
+
reports_dir: string | null;
|
|
8
|
+
ghidra_dir: string | null;
|
|
9
|
+
workspace_exists: boolean;
|
|
10
|
+
original_dir_exists: boolean;
|
|
11
|
+
reports_dir_exists: boolean;
|
|
12
|
+
ghidra_dir_exists: boolean;
|
|
13
|
+
original_present: boolean;
|
|
14
|
+
original_file_count: number;
|
|
15
|
+
original_files: string[];
|
|
16
|
+
alternate_workspace_root: string | null;
|
|
17
|
+
alternate_original_dir: string | null;
|
|
18
|
+
alternate_original_present: boolean;
|
|
19
|
+
alternate_original_files: string[];
|
|
20
|
+
remediation: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function inspectSampleWorkspace(workspaceManager: WorkspaceManager, sampleId: string): Promise<SampleWorkspaceIntegrity>;
|
|
23
|
+
export declare function formatMissingOriginalError(sampleId: string, integrity: SampleWorkspaceIntegrity): string;
|
|
24
|
+
export declare function resolvePrimarySamplePath(workspaceManager: WorkspaceManager, sampleId: string): Promise<{
|
|
25
|
+
samplePath: string;
|
|
26
|
+
integrity: SampleWorkspaceIntegrity;
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=sample-workspace.d.ts.map
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
function buildRemediation(status, sampleId) {
|
|
4
|
+
const shared = [
|
|
5
|
+
`Re-ingest the sample with sample.ingest(path=<absolute path>) for ${sampleId}.`,
|
|
6
|
+
'Or restore the original binary into workspace/original before running PE/Ghidra recovery tools.',
|
|
7
|
+
];
|
|
8
|
+
if (status === 'workspace_missing') {
|
|
9
|
+
return [
|
|
10
|
+
`Workspace directory is missing for ${sampleId}.`,
|
|
11
|
+
...shared,
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
if (status === 'original_dir_missing') {
|
|
15
|
+
return [
|
|
16
|
+
'workspace/original is missing even though the sample record still exists.',
|
|
17
|
+
...shared,
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
if (status === 'original_file_missing') {
|
|
21
|
+
return [
|
|
22
|
+
'workspace/original exists but contains no sample file.',
|
|
23
|
+
...shared,
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
function inferStatus(options) {
|
|
29
|
+
if (!options.workspaceExists) {
|
|
30
|
+
return 'workspace_missing';
|
|
31
|
+
}
|
|
32
|
+
if (!options.originalDirExists) {
|
|
33
|
+
return 'original_dir_missing';
|
|
34
|
+
}
|
|
35
|
+
if (options.originalFileCount <= 0) {
|
|
36
|
+
return 'original_file_missing';
|
|
37
|
+
}
|
|
38
|
+
return 'ready';
|
|
39
|
+
}
|
|
40
|
+
export async function inspectSampleWorkspace(workspaceManager, sampleId) {
|
|
41
|
+
const workspaceRootBase = workspaceManager.getWorkspaceRoot();
|
|
42
|
+
let workspaceRoot = null;
|
|
43
|
+
let originalDir = null;
|
|
44
|
+
let reportsDir = null;
|
|
45
|
+
let ghidraDir = null;
|
|
46
|
+
let workspaceExists = false;
|
|
47
|
+
let originalDirExists = false;
|
|
48
|
+
let reportsDirExists = false;
|
|
49
|
+
let ghidraDirExists = false;
|
|
50
|
+
let originalFiles = [];
|
|
51
|
+
let alternateWorkspaceRoot = null;
|
|
52
|
+
let alternateOriginalDir = null;
|
|
53
|
+
let alternateOriginalFiles = [];
|
|
54
|
+
try {
|
|
55
|
+
const workspace = await workspaceManager.getWorkspace(sampleId);
|
|
56
|
+
workspaceRoot = workspace.root;
|
|
57
|
+
originalDir = workspace.original;
|
|
58
|
+
reportsDir = workspace.reports;
|
|
59
|
+
ghidraDir = workspace.ghidra;
|
|
60
|
+
try {
|
|
61
|
+
await fs.access(workspace.root);
|
|
62
|
+
workspaceExists = true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
workspaceExists = false;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
await fs.access(workspace.original);
|
|
69
|
+
originalDirExists = true;
|
|
70
|
+
const entries = await fs.readdir(workspace.original, { withFileTypes: true });
|
|
71
|
+
originalFiles = entries
|
|
72
|
+
.filter((entry) => entry.isFile())
|
|
73
|
+
.map((entry) => entry.name)
|
|
74
|
+
.sort();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
originalDirExists = false;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
await fs.access(workspace.reports);
|
|
81
|
+
reportsDirExists = true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
reportsDirExists = false;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
await fs.access(workspace.ghidra);
|
|
88
|
+
ghidraDirExists = true;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
ghidraDirExists = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
workspaceExists = false;
|
|
96
|
+
}
|
|
97
|
+
const status = inferStatus({
|
|
98
|
+
workspaceExists,
|
|
99
|
+
originalDirExists,
|
|
100
|
+
originalFileCount: originalFiles.length,
|
|
101
|
+
});
|
|
102
|
+
if (status !== 'ready') {
|
|
103
|
+
const workspaceParent = path.dirname(path.dirname(workspaceRootBase));
|
|
104
|
+
const candidateRoots = Array.from(new Set([
|
|
105
|
+
path.join(workspaceParent, 'workspaces'),
|
|
106
|
+
])).filter((candidate) => path.resolve(candidate) !== path.resolve(workspaceRootBase));
|
|
107
|
+
const sampleHash = sampleId.startsWith('sha256:') ? sampleId.slice(7).toLowerCase() : null;
|
|
108
|
+
if (sampleHash && /^[a-f0-9]{64}$/.test(sampleHash)) {
|
|
109
|
+
for (const candidateRoot of candidateRoots) {
|
|
110
|
+
const candidateOriginalDir = path.join(candidateRoot, sampleHash.slice(0, 2), sampleHash.slice(2, 4), sampleHash, 'original');
|
|
111
|
+
try {
|
|
112
|
+
const entries = await fs.readdir(candidateOriginalDir, { withFileTypes: true });
|
|
113
|
+
const files = entries.filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
|
|
114
|
+
if (files.length > 0) {
|
|
115
|
+
alternateWorkspaceRoot = candidateRoot;
|
|
116
|
+
alternateOriginalDir = candidateOriginalDir;
|
|
117
|
+
alternateOriginalFiles = files;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// ignore missing legacy workspace candidates
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
status,
|
|
129
|
+
workspace_root: workspaceRoot,
|
|
130
|
+
original_dir: originalDir,
|
|
131
|
+
reports_dir: reportsDir,
|
|
132
|
+
ghidra_dir: ghidraDir,
|
|
133
|
+
workspace_exists: workspaceExists,
|
|
134
|
+
original_dir_exists: originalDirExists,
|
|
135
|
+
reports_dir_exists: reportsDirExists,
|
|
136
|
+
ghidra_dir_exists: ghidraDirExists,
|
|
137
|
+
original_present: originalFiles.length > 0,
|
|
138
|
+
original_file_count: originalFiles.length,
|
|
139
|
+
original_files: originalFiles,
|
|
140
|
+
alternate_workspace_root: alternateWorkspaceRoot,
|
|
141
|
+
alternate_original_dir: alternateOriginalDir,
|
|
142
|
+
alternate_original_present: alternateOriginalFiles.length > 0,
|
|
143
|
+
alternate_original_files: alternateOriginalFiles,
|
|
144
|
+
remediation: buildRemediation(status, sampleId),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export function formatMissingOriginalError(sampleId, integrity) {
|
|
148
|
+
const parts = [
|
|
149
|
+
`Sample original file is unavailable for ${sampleId}.`,
|
|
150
|
+
`status=${integrity.status}`,
|
|
151
|
+
];
|
|
152
|
+
if (integrity.workspace_root) {
|
|
153
|
+
parts.push(`workspace_root=${integrity.workspace_root}`);
|
|
154
|
+
}
|
|
155
|
+
if (integrity.original_dir) {
|
|
156
|
+
parts.push(`original_dir=${integrity.original_dir}`);
|
|
157
|
+
}
|
|
158
|
+
if (integrity.alternate_original_present && integrity.alternate_original_dir) {
|
|
159
|
+
parts.push(`alternate_original_dir=${integrity.alternate_original_dir}`);
|
|
160
|
+
parts.push('alternate_workspace_fallback=available');
|
|
161
|
+
}
|
|
162
|
+
if (integrity.remediation.length > 0) {
|
|
163
|
+
parts.push(`remediation=${integrity.remediation.join(' ')}`);
|
|
164
|
+
}
|
|
165
|
+
return parts.join(' ');
|
|
166
|
+
}
|
|
167
|
+
export async function resolvePrimarySamplePath(workspaceManager, sampleId) {
|
|
168
|
+
const integrity = await inspectSampleWorkspace(workspaceManager, sampleId);
|
|
169
|
+
if (integrity.original_present && integrity.original_dir) {
|
|
170
|
+
return {
|
|
171
|
+
samplePath: path.join(integrity.original_dir, integrity.original_files[0]),
|
|
172
|
+
integrity,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (integrity.alternate_original_present && integrity.alternate_original_dir) {
|
|
176
|
+
return {
|
|
177
|
+
samplePath: path.join(integrity.alternate_original_dir, integrity.alternate_original_files[0]),
|
|
178
|
+
integrity,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (!integrity.original_present || !integrity.original_dir) {
|
|
182
|
+
throw new Error(formatMissingOriginalError(sampleId, integrity));
|
|
183
|
+
}
|
|
184
|
+
throw new Error(formatMissingOriginalError(sampleId, integrity));
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=sample-workspace.js.map
|
|
@@ -5,6 +5,8 @@ export declare const SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE = "semantic_name_su
|
|
|
5
5
|
export declare const SEMANTIC_NAME_PREPARE_BUNDLE_ARTIFACT_TYPE = "semantic_name_prepare_bundle";
|
|
6
6
|
export declare const SEMANTIC_EXPLANATION_PREPARE_BUNDLE_ARTIFACT_TYPE = "semantic_explanation_prepare_bundle";
|
|
7
7
|
export declare const SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE = "semantic_function_explanations";
|
|
8
|
+
export declare const SEMANTIC_MODULE_REVIEW_PREPARE_BUNDLE_ARTIFACT_TYPE = "semantic_module_review_prepare_bundle";
|
|
9
|
+
export declare const SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE = "semantic_module_reviews";
|
|
8
10
|
export interface SemanticNameSuggestionEntry {
|
|
9
11
|
address?: string | null;
|
|
10
12
|
function?: string | null;
|
|
@@ -102,15 +104,69 @@ export interface SemanticFunctionExplanationIndex {
|
|
|
102
104
|
latest_created_at: string | null;
|
|
103
105
|
scope_note: string;
|
|
104
106
|
}
|
|
107
|
+
export interface SemanticModuleReviewEntry {
|
|
108
|
+
module_name: string;
|
|
109
|
+
refined_name?: string | null;
|
|
110
|
+
summary: string;
|
|
111
|
+
role_hint?: string | null;
|
|
112
|
+
confidence: number;
|
|
113
|
+
assumptions?: string[];
|
|
114
|
+
evidence_used?: string[];
|
|
115
|
+
rewrite_guidance?: string[];
|
|
116
|
+
focus_areas?: string[];
|
|
117
|
+
priority_functions?: string[];
|
|
118
|
+
}
|
|
119
|
+
export interface SemanticModuleReviewArtifactPayload {
|
|
120
|
+
schema_version: 1;
|
|
121
|
+
sample_id: string;
|
|
122
|
+
created_at: string;
|
|
123
|
+
session_tag?: string | null;
|
|
124
|
+
client_name?: string | null;
|
|
125
|
+
model_name?: string | null;
|
|
126
|
+
prepare_artifact_id?: string | null;
|
|
127
|
+
reviews: SemanticModuleReviewEntry[];
|
|
128
|
+
}
|
|
129
|
+
export interface LoadedSemanticModuleReview {
|
|
130
|
+
module_name: string;
|
|
131
|
+
refined_name: string | null;
|
|
132
|
+
summary: string;
|
|
133
|
+
role_hint: string | null;
|
|
134
|
+
confidence: number;
|
|
135
|
+
assumptions: string[];
|
|
136
|
+
evidence_used: string[];
|
|
137
|
+
rewrite_guidance: string[];
|
|
138
|
+
focus_areas: string[];
|
|
139
|
+
priority_functions: string[];
|
|
140
|
+
artifact_id: string;
|
|
141
|
+
created_at: string;
|
|
142
|
+
client_name: string | null;
|
|
143
|
+
model_name: string | null;
|
|
144
|
+
session_tag: string | null;
|
|
145
|
+
prepare_artifact_id: string | null;
|
|
146
|
+
}
|
|
147
|
+
export interface SemanticModuleReviewIndex {
|
|
148
|
+
byModule: Map<string, LoadedSemanticModuleReview>;
|
|
149
|
+
marker: string;
|
|
150
|
+
artifact_ids: string[];
|
|
151
|
+
session_tags: string[];
|
|
152
|
+
earliest_created_at: string | null;
|
|
153
|
+
latest_created_at: string | null;
|
|
154
|
+
scope_note: string;
|
|
155
|
+
}
|
|
105
156
|
export declare function normalizeFunctionKey(value: string | null | undefined): string | null;
|
|
106
157
|
export declare function normalizeAddressKey(value: string | null | undefined): string | null;
|
|
107
158
|
export declare function sanitizeSemanticName(value: string | null | undefined): string | null;
|
|
159
|
+
export declare function normalizeModuleKey(value: string | null | undefined): string | null;
|
|
108
160
|
export declare function persistSemanticNamePrepareBundleArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, payload: unknown, sessionTag?: string | null): Promise<ArtifactRef>;
|
|
109
161
|
export declare function persistSemanticExplanationPrepareBundleArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, payload: unknown, sessionTag?: string | null): Promise<ArtifactRef>;
|
|
110
162
|
export declare function persistSemanticFunctionExplanationsArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, payload: SemanticFunctionExplanationArtifactPayload): Promise<ArtifactRef>;
|
|
163
|
+
export declare function persistSemanticModuleReviewPrepareBundleArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, payload: unknown, sessionTag?: string | null): Promise<ArtifactRef>;
|
|
164
|
+
export declare function persistSemanticModuleReviewsArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, payload: SemanticModuleReviewArtifactPayload): Promise<ArtifactRef>;
|
|
111
165
|
export declare function persistSemanticNameSuggestionsArtifact(workspaceManager: WorkspaceManager, database: DatabaseManager, payload: SemanticNameSuggestionArtifactPayload): Promise<ArtifactRef>;
|
|
112
166
|
export declare function loadSemanticNameSuggestionIndex(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, options?: LoadSemanticArtifactOptions): Promise<SemanticNameSuggestionIndex>;
|
|
113
167
|
export declare function loadSemanticFunctionExplanationIndex(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, options?: LoadSemanticArtifactOptions): Promise<SemanticFunctionExplanationIndex>;
|
|
168
|
+
export declare function loadSemanticModuleReviewIndex(workspaceManager: WorkspaceManager, database: DatabaseManager, sampleId: string, options?: LoadSemanticArtifactOptions): Promise<SemanticModuleReviewIndex>;
|
|
114
169
|
export declare function findSemanticNameSuggestion(index: SemanticNameSuggestionIndex | null | undefined, address: string | null | undefined, funcName: string | null | undefined): LoadedSemanticNameSuggestion | null;
|
|
115
170
|
export declare function findSemanticFunctionExplanation(index: SemanticFunctionExplanationIndex | null | undefined, address: string | null | undefined, funcName: string | null | undefined): LoadedSemanticFunctionExplanation | null;
|
|
171
|
+
export declare function findSemanticModuleReview(index: SemanticModuleReviewIndex | null | undefined, moduleName: string | null | undefined): LoadedSemanticModuleReview | null;
|
|
116
172
|
//# sourceMappingURL=semantic-name-suggestion-artifacts.d.ts.map
|
|
@@ -6,6 +6,8 @@ export const SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE = 'semantic_name_suggestion
|
|
|
6
6
|
export const SEMANTIC_NAME_PREPARE_BUNDLE_ARTIFACT_TYPE = 'semantic_name_prepare_bundle';
|
|
7
7
|
export const SEMANTIC_EXPLANATION_PREPARE_BUNDLE_ARTIFACT_TYPE = 'semantic_explanation_prepare_bundle';
|
|
8
8
|
export const SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE = 'semantic_function_explanations';
|
|
9
|
+
export const SEMANTIC_MODULE_REVIEW_PREPARE_BUNDLE_ARTIFACT_TYPE = 'semantic_module_review_prepare_bundle';
|
|
10
|
+
export const SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE = 'semantic_module_reviews';
|
|
9
11
|
const LATEST_SEMANTIC_ARTIFACT_WINDOW_MS = 10 * 1000;
|
|
10
12
|
function sanitizePathSegment(value, fallback) {
|
|
11
13
|
const normalized = (value || fallback)
|
|
@@ -46,6 +48,10 @@ export function sanitizeSemanticName(value) {
|
|
|
46
48
|
}
|
|
47
49
|
return collapsed;
|
|
48
50
|
}
|
|
51
|
+
export function normalizeModuleKey(value) {
|
|
52
|
+
const normalized = sanitizeSemanticName(value);
|
|
53
|
+
return normalized && normalized.length > 0 ? normalized : null;
|
|
54
|
+
}
|
|
49
55
|
async function persistSemanticNamingJsonArtifact(workspaceManager, database, sampleId, artifactType, filePrefix, payload, sessionTag) {
|
|
50
56
|
const workspace = await workspaceManager.createWorkspace(sampleId);
|
|
51
57
|
const sessionSegment = sanitizePathSegment(sessionTag || undefined, 'default');
|
|
@@ -85,6 +91,12 @@ export async function persistSemanticExplanationPrepareBundleArtifact(workspaceM
|
|
|
85
91
|
export async function persistSemanticFunctionExplanationsArtifact(workspaceManager, database, payload) {
|
|
86
92
|
return persistSemanticNamingJsonArtifact(workspaceManager, database, payload.sample_id, SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE, 'semantic_function_explanations', payload, payload.session_tag);
|
|
87
93
|
}
|
|
94
|
+
export async function persistSemanticModuleReviewPrepareBundleArtifact(workspaceManager, database, sampleId, payload, sessionTag) {
|
|
95
|
+
return persistSemanticNamingJsonArtifact(workspaceManager, database, sampleId, SEMANTIC_MODULE_REVIEW_PREPARE_BUNDLE_ARTIFACT_TYPE, 'module_review_bundle', payload, sessionTag);
|
|
96
|
+
}
|
|
97
|
+
export async function persistSemanticModuleReviewsArtifact(workspaceManager, database, payload) {
|
|
98
|
+
return persistSemanticNamingJsonArtifact(workspaceManager, database, payload.sample_id, SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE, 'semantic_module_reviews', payload, payload.session_tag);
|
|
99
|
+
}
|
|
88
100
|
export async function persistSemanticNameSuggestionsArtifact(workspaceManager, database, payload) {
|
|
89
101
|
return persistSemanticNamingJsonArtifact(workspaceManager, database, payload.sample_id, SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE, 'semantic_name_suggestions', payload, payload.session_tag);
|
|
90
102
|
}
|
|
@@ -283,6 +295,71 @@ export async function loadSemanticFunctionExplanationIndex(workspaceManager, dat
|
|
|
283
295
|
: 'Semantic explanation artifacts reflect the selected artifact scope.',
|
|
284
296
|
};
|
|
285
297
|
}
|
|
298
|
+
export async function loadSemanticModuleReviewIndex(workspaceManager, database, sampleId, options = {}) {
|
|
299
|
+
const byModule = new Map();
|
|
300
|
+
const artifacts = database.findArtifactsByType(sampleId, SEMANTIC_MODULE_REVIEWS_ARTIFACT_TYPE);
|
|
301
|
+
const loadedArtifacts = [];
|
|
302
|
+
for (const artifact of artifacts) {
|
|
303
|
+
const payload = (await readArtifactJson(workspaceManager, database, sampleId, artifact.id));
|
|
304
|
+
loadedArtifacts.push({ artifact, payload });
|
|
305
|
+
}
|
|
306
|
+
const filteredArtifacts = filterSemanticArtifactsByScope(loadedArtifacts, options);
|
|
307
|
+
for (const { artifact, payload } of filteredArtifacts) {
|
|
308
|
+
if (!payload || !Array.isArray(payload.reviews)) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
for (const review of payload.reviews) {
|
|
312
|
+
const moduleKey = normalizeModuleKey(review.module_name);
|
|
313
|
+
if (!moduleKey || byModule.has(moduleKey)) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
byModule.set(moduleKey, {
|
|
317
|
+
module_name: review.module_name,
|
|
318
|
+
refined_name: review.refined_name || null,
|
|
319
|
+
summary: review.summary,
|
|
320
|
+
role_hint: review.role_hint || null,
|
|
321
|
+
confidence: review.confidence,
|
|
322
|
+
assumptions: review.assumptions || [],
|
|
323
|
+
evidence_used: review.evidence_used || [],
|
|
324
|
+
rewrite_guidance: review.rewrite_guidance || [],
|
|
325
|
+
focus_areas: review.focus_areas || [],
|
|
326
|
+
priority_functions: review.priority_functions || [],
|
|
327
|
+
artifact_id: artifact.id,
|
|
328
|
+
created_at: artifact.created_at,
|
|
329
|
+
client_name: payload.client_name || null,
|
|
330
|
+
model_name: payload.model_name || null,
|
|
331
|
+
session_tag: payload.session_tag || null,
|
|
332
|
+
prepare_artifact_id: payload.prepare_artifact_id || null,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
byModule,
|
|
338
|
+
marker: filteredArtifacts.length > 0
|
|
339
|
+
? filteredArtifacts.map(({ artifact }) => `${artifact.id}:${artifact.sha256}`).join('|')
|
|
340
|
+
: 'none',
|
|
341
|
+
artifact_ids: filteredArtifacts.map(({ artifact }) => artifact.id),
|
|
342
|
+
session_tags: Array.from(new Set(filteredArtifacts.flatMap(({ artifact, payload }) => collectSemanticArtifactSessionTags(artifact.path, payload?.session_tag)))),
|
|
343
|
+
earliest_created_at: filteredArtifacts.length > 0
|
|
344
|
+
? filteredArtifacts
|
|
345
|
+
.map(({ artifact }) => artifact.created_at)
|
|
346
|
+
.filter((item) => typeof item === 'string' && item.length > 0)
|
|
347
|
+
.sort()[0] || null
|
|
348
|
+
: null,
|
|
349
|
+
latest_created_at: filteredArtifacts.length > 0
|
|
350
|
+
? filteredArtifacts
|
|
351
|
+
.map(({ artifact }) => artifact.created_at)
|
|
352
|
+
.filter((item) => typeof item === 'string' && item.length > 0)
|
|
353
|
+
.sort()
|
|
354
|
+
.slice(-1)[0] || null
|
|
355
|
+
: null,
|
|
356
|
+
scope_note: options.scope === 'session' && options.sessionTag?.trim()
|
|
357
|
+
? `Semantic module review artifacts are limited to session selector "${options.sessionTag.trim()}".`
|
|
358
|
+
: options.scope === 'latest'
|
|
359
|
+
? 'Semantic module review artifacts are limited to the latest artifact window.'
|
|
360
|
+
: 'Semantic module review artifacts reflect the selected artifact scope.',
|
|
361
|
+
};
|
|
362
|
+
}
|
|
286
363
|
export function findSemanticNameSuggestion(index, address, funcName) {
|
|
287
364
|
if (!index) {
|
|
288
365
|
return null;
|
|
@@ -311,4 +388,14 @@ export function findSemanticFunctionExplanation(index, address, funcName) {
|
|
|
311
388
|
}
|
|
312
389
|
return null;
|
|
313
390
|
}
|
|
391
|
+
export function findSemanticModuleReview(index, moduleName) {
|
|
392
|
+
if (!index) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
const moduleKey = normalizeModuleKey(moduleName);
|
|
396
|
+
if (moduleKey && index.byModule.has(moduleKey)) {
|
|
397
|
+
return index.byModule.get(moduleKey) || null;
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
314
401
|
//# sourceMappingURL=semantic-name-suggestion-artifacts.js.map
|