import type { AssetExactResult, AssetInstallMetadata, AssetSearchResult, AssetVersion, GenerateAssetResult, PaginatedList, } from './contract.js' import type { AssetAccess, AssetDependencies, AssetType, GenerateJobStatus } from './schemas.js' export const ASSET_RELEVANCE_SCORE: unique symbol = Symbol('assetRelevanceScore') export interface RankedAssetSearchResult extends AssetSearchResult { [ASSET_RELEVANCE_SCORE]?: number } export interface AssetSearchInput { page: number limit: number type?: AssetType query?: string includeUnapproved: boolean } export interface AssetExactInput { name: string type?: AssetType version?: string includeUnapproved: boolean /** The asset's capability key, an alternative credential to identity for private/unapproved assets. */ key?: string } export interface AssetUploadZipInput { name: string type: AssetType version: string description?: string npmDependencies: Record assetDependencies: AssetDependencies skillDependencies: Record tags: string[] zip: File /** Requested visibility; the worker resolves the effective access (see `AssetAccess`). */ access?: AssetAccess } export interface AssetVersionRef { name: string version: string /** The asset's capability key, an alternative credential to identity for private/unapproved assets. */ key?: string } export interface AssetGenerateInput { description: string type?: AssetType /** Reference images (https URL or data URI) the result should match; see `referenceImagesSchema`. */ referenceImages?: string[] /** Requested visibility of the generated asset; the worker resolves the effective access. */ access?: AssetAccess } export interface AssetOwner { id: string role: string } export interface GenerationReceipt { assetId: string type: AssetType providerJobId: string description: string assetName: string ownerId: string access: AssetAccess } export interface AssetProvider { type: AssetType /** Shown when an agent asks how to search this asset type. */ searchMessage?: string /** Shown to the user after an asset of this type is installed. */ installMessage?: string /** Whether a top-level install should save this asset to package.json assetDependencies. */ saveOnInstall?: boolean /** Whether upload should read package.json assetDependencies from the source zip. */ readAssetDependenciesFromPackageJson?: boolean /** Whether upload should omit unchanged installed dependency files from the source zip. */ omitUnchangedInstalledFilesOnUpload?: boolean /** * Whether generation takes `AssetGenerateInput.referenceImages`. The worker rejects reference * images for providers without this, so they fail loudly instead of being silently ignored. */ acceptsReferenceImages?: boolean search(input: AssetSearchInput): Promise> exact(input: AssetExactInput): Promise uploadZip(input: AssetUploadZipInput, owner: AssetOwner): Promise generate?(input: AssetGenerateInput, owner: AssetOwner): Promise /** * Job-based generation for providers whose upstream work runs for minutes. */ startGeneration?(input: AssetGenerateInput, owner: AssetOwner): Promise generationStatus?(receipt: GenerationReceipt): Promise } export type AssetImplementation = AssetProvider export function assetRelevanceScore(asset: AssetSearchResult): number | undefined { if (!(ASSET_RELEVANCE_SCORE in asset)) return undefined const score = asset[ASSET_RELEVANCE_SCORE] return typeof score === 'number' ? score : undefined } export function installMetadataForProvider(provider: AssetProvider): AssetInstallMetadata { return { searchMessage: provider.searchMessage, installMessage: provider.installMessage, canGenerate: Boolean(provider.generate || provider.startGeneration), acceptsReferenceImages: provider.acceptsReferenceImages ?? false, saveOnInstall: provider.saveOnInstall ?? true, readAssetDependenciesFromPackageJson: provider.readAssetDependenciesFromPackageJson ?? false, omitUnchangedInstalledFilesOnUpload: provider.omitUnchangedInstalledFilesOnUpload ?? false, } } export function withAssetRelevanceScore( asset: AssetSearchResult, score: number, ): RankedAssetSearchResult { const ranked: RankedAssetSearchResult = { ...asset, [ASSET_RELEVANCE_SCORE]: score, } Object.defineProperty(ranked, ASSET_RELEVANCE_SCORE, { value: score, enumerable: false, }) return ranked }