From 4e67bc399951768737eeaf539f7bc9fa8373b244 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:21:35 +0800 Subject: [PATCH] refactor(analysis): consolidate selection ownership --- docs/architecture.mermaid | 2 +- src/adb/AdbConfiguration.ts | 2 +- src/apktool/ApktoolConfiguration.ts | 2 +- src/application/AnalysisProvider.ts | 2 +- .../ClientRegistrationEnvironment.ts | 2 +- src/application/DirectAnalysis.ts | 18 +++++++----------- src/application/SetupHost.ts | 2 +- .../binary/AnalysisProviderRegistry.ts | 2 +- src/application/binary/BinarySessionOpen.ts | 2 +- src/application/binary/BinarySessionPort.ts | 2 +- .../binary/SessionProviderRouter.ts | 2 +- src/cli/options.ts | 4 ++-- src/config/environment.ts | 2 +- src/config/types.ts | 2 +- src/contracts/sessionLifecycleInputs.ts | 2 +- src/contracts/toolOutputSchemaPrimitives.ts | 2 +- src/{contracts => domain}/providerSelection.ts | 0 src/ghidra/GhidraInstallation.ts | 2 +- src/kuna/KunaProviderCapabilities.ts | 2 +- src/mcpStartupPolicy.ts | 2 +- src/pcap/PcapConfiguration.ts | 2 +- .../mcp/callRelationshipAvailability.test.ts | 2 +- .../mcp/nativeDispatchAvailability.test.ts | 2 +- 23 files changed, 29 insertions(+), 33 deletions(-) rename src/{contracts => domain}/providerSelection.ts (100%) diff --git a/docs/architecture.mermaid b/docs/architecture.mermaid index 91603c944e..4f36c28aac 100644 --- a/docs/architecture.mermaid +++ b/docs/architecture.mermaid @@ -75,7 +75,7 @@ graph TD end subgraph "Domain Layer" - DOMAIN["src/domain/
Pure side-effect-free modules
format declarations, Evidence, comparisons, application graphs"] + DOMAIN["src/domain/
Pure side-effect-free modules
format declarations, provider selection, Evidence, comparisons, application graphs"] end subgraph "Configuration" diff --git a/src/adb/AdbConfiguration.ts b/src/adb/AdbConfiguration.ts index 2661dfde27..122e244998 100644 --- a/src/adb/AdbConfiguration.ts +++ b/src/adb/AdbConfiguration.ts @@ -13,7 +13,7 @@ import { execFileOutputFailure, } from "../process/ExecFileOutput.js"; import type { ProviderAvailability } from "../application/AnalysisProvider.js"; -import type { ProviderRejectionCode } from "../contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../domain/providerSelection.js"; import { fixedAllowanceOptions } from "../domain/fixedAllowanceRemediation.js"; import type { JsonValue } from "../domain/jsonValue.js"; diff --git a/src/apktool/ApktoolConfiguration.ts b/src/apktool/ApktoolConfiguration.ts index c50e3af7b1..f6187c3443 100644 --- a/src/apktool/ApktoolConfiguration.ts +++ b/src/apktool/ApktoolConfiguration.ts @@ -24,7 +24,7 @@ import { prepareOwnedCommand, } from "../process/OwnedCommand.js"; import type { ProviderAvailability } from "../application/AnalysisProvider.js"; -import type { ProviderRejectionCode } from "../contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../domain/providerSelection.js"; import type { JsonValue } from "../domain/jsonValue.js"; import { parse } from "semver"; import { diff --git a/src/application/AnalysisProvider.ts b/src/application/AnalysisProvider.ts index 5a06c5c79b..caafeae4b0 100644 --- a/src/application/AnalysisProvider.ts +++ b/src/application/AnalysisProvider.ts @@ -17,7 +17,7 @@ import type { } from "../domain/evidence.js"; import { jsonValueSchema } from "../domain/jsonValue.js"; import type { ProgressReporter } from "./ProgressReporter.js"; -import type { ProviderRejectionCode } from "../contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../domain/providerSelection.js"; import type { ProcessLineageObservation } from "../process/ProcessOwnership.js"; import type { ProviderOperationHealth } from "../domain/providerOperationHealth.js"; import { diff --git a/src/application/ClientRegistrationEnvironment.ts b/src/application/ClientRegistrationEnvironment.ts index 80498a029a..8a1b84afa7 100644 --- a/src/application/ClientRegistrationEnvironment.ts +++ b/src/application/ClientRegistrationEnvironment.ts @@ -18,7 +18,7 @@ import { } from "../process/snapshotEnvironment.js"; import { dshDocument } from "./DshCordisPatch.js"; import type { McpToolTimeoutInputs } from "../mcpStartupPolicy.js"; -import type { AnalysisProviderSelector } from "../contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "../domain/providerSelection.js"; export type ProviderRequestBudgetResolver = ( analysisProvider: AnalysisProviderSelector, diff --git a/src/application/DirectAnalysis.ts b/src/application/DirectAnalysis.ts index c25d83f95d..521675a124 100644 --- a/src/application/DirectAnalysis.ts +++ b/src/application/DirectAnalysis.ts @@ -20,6 +20,7 @@ import { createImmutableEvidenceBundle } from "../domain/evidenceBundle.js"; import { retainAnalysisResultSources } from "./investigation/recordEvidenceSources.js"; import type { Evidence } from "../domain/evidence.js"; import type { NativeToolName } from "../contracts/native/nativeToolContracts.js"; +import type { EnhancedToolName } from "../contracts/enhancedInputs.js"; import { ARTIFACT_ANALYSIS_OPERATIONS, type ArtifactAnalysisOperation, @@ -61,7 +62,7 @@ import { workflowSnapshotRecord, recordWorkflowUnknowns, } from "./WorkflowEvidence.js"; -import type { AnalysisProviderSelector } from "../contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "../domain/providerSelection.js"; import { resolveXrefsAddress } from "./XrefsAddressResolution.js"; import { AnalysisCancelledError, @@ -598,15 +599,7 @@ const executeAnalysisTool = async (input: { ); return result.ok ? ok({ evidence: result.value }) : result; } - if ( - tool === "binary_overview" || - tool === "inspect_native_api" || - tool === "inspect_native_dispatch_metadata" || - tool === "trace_feature" || - tool === "trace_native_values" || - tool === "trace_native_ui_action" || - tool === "decompile_all" - ) { + if (isWorkflowEvidenceTool(tool)) { const { EnhancedTools } = await import("./EnhancedTools.js"); let result = await executeComposedWorkflow( session, @@ -812,7 +805,10 @@ const isWorkflowEvidenceTool = ( | ArtifactAnalysisOperation | ManagedToolName | DirectAnalysisTool, -): boolean => +): tool is Exclude< + Extract, + "analyze_function" +> => tool === "binary_overview" || tool === "inspect_native_api" || tool === "inspect_native_dispatch_metadata" || diff --git a/src/application/SetupHost.ts b/src/application/SetupHost.ts index 569a0b3d16..60402a7cf4 100644 --- a/src/application/SetupHost.ts +++ b/src/application/SetupHost.ts @@ -81,7 +81,7 @@ import { } from "../artifacts/AnalysisCleanupLease.js"; import { bindRegularFileCleanup } from "./RegularFileRead.js"; import { CURRENT_ENTRYPOINT_PATH } from "../process/CurrentEntrypointPath.js"; -import { analysisProviderSelectorSchema } from "../contracts/providerSelection.js"; +import { analysisProviderSelectorSchema } from "../domain/providerSelection.js"; /** Resolve the executable and arguments used in a managed MCP registration. */ export const setupRegistrationCommand = ( diff --git a/src/application/binary/AnalysisProviderRegistry.ts b/src/application/binary/AnalysisProviderRegistry.ts index 3af1805679..ba7df2051f 100644 --- a/src/application/binary/AnalysisProviderRegistry.ts +++ b/src/application/binary/AnalysisProviderRegistry.ts @@ -11,7 +11,7 @@ import { analysisProviderIdSchema, analysisProviderSelectorSchema, type AnalysisProviderSelector, -} from "../../contracts/providerSelection.js"; +} from "../../domain/providerSelection.js"; import type { AnalysisProfileCommitment } from "../../domain/analysisProfile.js"; import type { BinaryTarget } from "../../domain/binaryTargetTypes.js"; import { diff --git a/src/application/binary/BinarySessionOpen.ts b/src/application/binary/BinarySessionOpen.ts index 75af7bcbc6..1b370b7d6d 100644 --- a/src/application/binary/BinarySessionOpen.ts +++ b/src/application/binary/BinarySessionOpen.ts @@ -4,7 +4,7 @@ import type { BinaryTarget, } from "../../domain/binaryTargetTypes.js"; import { artifactTargetSelectedPath } from "../../domain/binaryTargetTypes.js"; -import type { AnalysisProviderSelector } from "../../contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "../../domain/providerSelection.js"; import type { AnalysisProfileCommitment } from "../../domain/analysisProfile.js"; import { snapshotMatchesBinding, diff --git a/src/application/binary/BinarySessionPort.ts b/src/application/binary/BinarySessionPort.ts index b6fe5cdeb3..12e46f26a6 100644 --- a/src/application/binary/BinarySessionPort.ts +++ b/src/application/binary/BinarySessionPort.ts @@ -7,7 +7,7 @@ import type { import type { JsonValue } from "../../domain/jsonValue.js"; import type { AnalysisSnapshot } from "../../domain/analysisSnapshot.js"; import type { AnalysisProfileCommitment } from "../../domain/analysisProfile.js"; -import type { AnalysisProviderSelector } from "../../contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "../../domain/providerSelection.js"; import type { BinarySessionDetail } from "../../contracts/sessionStatusContract.js"; import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import type { Result } from "../../domain/result.js"; diff --git a/src/application/binary/SessionProviderRouter.ts b/src/application/binary/SessionProviderRouter.ts index 0989460b4f..5e454bd54c 100644 --- a/src/application/binary/SessionProviderRouter.ts +++ b/src/application/binary/SessionProviderRouter.ts @@ -1,4 +1,4 @@ -import type { AnalysisProviderSelector } from "../../contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "../../domain/providerSelection.js"; import type { AnalysisProfileCommitment } from "../../domain/analysisProfile.js"; import type { BinaryTarget } from "../../domain/binaryTargetTypes.js"; import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js"; diff --git a/src/cli/options.ts b/src/cli/options.ts index 0ebef6c0fe..2de0d6b1e7 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -9,8 +9,8 @@ import { binaryArchitectureSchema, rawBinaryEntryPointSchema, } from "../domain/rawBinary.js"; -import { analysisProviderSelectorSchema } from "../contracts/providerSelection.js"; -import type { AnalysisProviderSelector } from "../contracts/providerSelection.js"; +import { analysisProviderSelectorSchema } from "../domain/providerSelection.js"; +import type { AnalysisProviderSelector } from "../domain/providerSelection.js"; import type { Logger } from "pino"; /** Parse decimal integer tokens before Number coercion can erase their syntax. */ diff --git a/src/config/environment.ts b/src/config/environment.ts index ba19d64982..fbc771dbea 100644 --- a/src/config/environment.ts +++ b/src/config/environment.ts @@ -7,7 +7,7 @@ import { isAbsolute } from "node:path"; import { ConfigurationError } from "../domain/configurationErrors.js"; import { err, ok, type Result } from "../domain/result.js"; -import { analysisProviderSelectorSchema } from "../contracts/providerSelection.js"; +import { analysisProviderSelectorSchema } from "../domain/providerSelection.js"; import { ghidraCompilerSpecIdSchema, ghidraLanguageIdSchema, diff --git a/src/config/types.ts b/src/config/types.ts index e9c85a5703..880b03674b 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -2,7 +2,7 @@ import type { GhidraProjectSelection } from "./ghidraExistingProject.js"; import type { McpToolFamilies } from "./mcpToolFamilies.js"; import type { LogLevel } from "./processStart.js"; import type { ReferenceSourcePolicy } from "../domain/referenceSourcePolicy.js"; -import type { AnalysisProviderSelector } from "../contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "../domain/providerSelection.js"; import type { GhidraLanguageOverride } from "./ghidraLanguageOverride.js"; import type { McpInputSchemaProfile } from "./mcpInputSchemaProfile.js"; diff --git a/src/contracts/sessionLifecycleInputs.ts b/src/contracts/sessionLifecycleInputs.ts index 48a1182770..b0bb246e80 100644 --- a/src/contracts/sessionLifecycleInputs.ts +++ b/src/contracts/sessionLifecycleInputs.ts @@ -6,7 +6,7 @@ import { rawBinarySelectionError, } from "../domain/rawBinary.js"; import { z } from "zod"; -import { analysisProviderSelectorSchema } from "./providerSelection.js"; +import { analysisProviderSelectorSchema } from "../domain/providerSelection.js"; /** Caller-supplied analysis snapshot path; must name its destination absolutely. */ const snapshotPathSchema = absoluteLocalPathStringSchema.describe( diff --git a/src/contracts/toolOutputSchemaPrimitives.ts b/src/contracts/toolOutputSchemaPrimitives.ts index 3aaa48332a..7c91104297 100644 --- a/src/contracts/toolOutputSchemaPrimitives.ts +++ b/src/contracts/toolOutputSchemaPrimitives.ts @@ -12,7 +12,7 @@ import { jsonObjectSchema } from "../domain/jsonValue.js"; import { PROVIDER_REJECTION_CODES, type ProviderRejectionCode, -} from "./providerSelection.js"; +} from "../domain/providerSelection.js"; import { PROVIDER_FAILURE_STAGES, PROVIDER_OPERATION_STATES, diff --git a/src/contracts/providerSelection.ts b/src/domain/providerSelection.ts similarity index 100% rename from src/contracts/providerSelection.ts rename to src/domain/providerSelection.ts diff --git a/src/ghidra/GhidraInstallation.ts b/src/ghidra/GhidraInstallation.ts index 46d0c8f092..7f30a7f6a0 100644 --- a/src/ghidra/GhidraInstallation.ts +++ b/src/ghidra/GhidraInstallation.ts @@ -22,7 +22,7 @@ import { import { constants, accessSync, realpathSync, statSync } from "node:fs"; import { delimiter as pathDelimiter, dirname, posix, win32 } from "node:path"; -import type { ProviderRejectionCode } from "../contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../domain/providerSelection.js"; import { parseExecutableHeader, readFatArchitectureFamilies, diff --git a/src/kuna/KunaProviderCapabilities.ts b/src/kuna/KunaProviderCapabilities.ts index 3ef7882934..425b5fdf6a 100644 --- a/src/kuna/KunaProviderCapabilities.ts +++ b/src/kuna/KunaProviderCapabilities.ts @@ -2,7 +2,7 @@ import type { AnalysisOperation, CapabilityDescriptor, } from "../application/AnalysisProvider.js"; -import type { ProviderRejectionCode } from "../contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../domain/providerSelection.js"; import { KUNA_PROVIDER_ID } from "./KunaRelease.js"; import { DEFAULT_REGEX_MATCHING_DEADLINE, diff --git a/src/mcpStartupPolicy.ts b/src/mcpStartupPolicy.ts index d9c5f3a9bf..a1a914ce59 100644 --- a/src/mcpStartupPolicy.ts +++ b/src/mcpStartupPolicy.ts @@ -1,4 +1,4 @@ -import type { AnalysisProviderSelector } from "./contracts/providerSelection.js"; +import type { AnalysisProviderSelector } from "./domain/providerSelection.js"; import { mcpPhaseBudgetRecovery, type McpPhaseBudget, diff --git a/src/pcap/PcapConfiguration.ts b/src/pcap/PcapConfiguration.ts index 463cca8e51..5eb33217ac 100644 --- a/src/pcap/PcapConfiguration.ts +++ b/src/pcap/PcapConfiguration.ts @@ -4,7 +4,7 @@ import { delimiter, isAbsolute } from "node:path"; import { appendCallerPath } from "../filesystem/callerPath.js"; import type { ProviderAvailability } from "../application/AnalysisProvider.js"; -import type { ProviderRejectionCode } from "../contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../domain/providerSelection.js"; import type { AnalysisErrorOptions } from "../domain/analysisErrorBase.js"; import { AnalysisCapabilityUnavailableError, diff --git a/tests/boundary/mcp/callRelationshipAvailability.test.ts b/tests/boundary/mcp/callRelationshipAvailability.test.ts index d3c3621a36..97a2ede20d 100644 --- a/tests/boundary/mcp/callRelationshipAvailability.test.ts +++ b/tests/boundary/mcp/callRelationshipAvailability.test.ts @@ -11,7 +11,7 @@ import type { } from "../../../src/application/AnalysisProvider.js"; import { createAnalysisExecution } from "../../../src/application/AnalysisProvider.js"; import { ok } from "../../../src/domain/result.js"; -import type { ProviderRejectionCode } from "../../../src/contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../../../src/domain/providerSelection.js"; import { toolContract } from "../../../src/contracts/toolContracts.js"; import { expandToolAvailability } from "../../../src/contracts/toolOutputSchemaPrimitives.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; diff --git a/tests/boundary/mcp/nativeDispatchAvailability.test.ts b/tests/boundary/mcp/nativeDispatchAvailability.test.ts index 6ba0abae62..5d071237a8 100644 --- a/tests/boundary/mcp/nativeDispatchAvailability.test.ts +++ b/tests/boundary/mcp/nativeDispatchAvailability.test.ts @@ -13,7 +13,7 @@ import { createAnalysisExecution } from "../../../src/application/AnalysisProvid import { AnalysisOutputError } from "../../../src/domain/analysisErrorCore.js"; import { inspectNativeDispatchMetadata } from "../../../src/domain/native/objcSwiftMetadata.js"; import { err, ok } from "../../../src/domain/result.js"; -import type { ProviderRejectionCode } from "../../../src/contracts/providerSelection.js"; +import type { ProviderRejectionCode } from "../../../src/domain/providerSelection.js"; import { toolContract } from "../../../src/contracts/toolContracts.js"; import { expandToolAvailability } from "../../../src/contracts/toolOutputSchemaPrimitives.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js";