294 lines
11 KiB
TypeScript
294 lines
11 KiB
TypeScript
import type { BinaryTarget } from "../domain/binaryTargetTypes.js";
|
|
import type { OfficialToolName } from "../contracts/officialToolContracts.js";
|
|
import type { EnhancedToolName } from "../contracts/enhancedInputs.js";
|
|
import type { NativeToolName } from "../contracts/native/nativeToolContracts.js";
|
|
import type { ArtifactAnalysisOperation } from "../contracts/artifactToolContracts.js";
|
|
import type { ManagedToolName } from "../contracts/managed/managedToolContracts.js";
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import type { AnalysisProfileCommitment } from "../domain/analysisProfile.js";
|
|
import type { JsonValue } from "../domain/jsonValue.js";
|
|
import type { Result } from "../domain/result.js";
|
|
import type { EvidenceLocation } from "../domain/evidence.js";
|
|
import type { EvidenceSubjectTarget } 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 { ProcessLineageObservation } from "../process/ProcessOwnership.js";
|
|
import type { ProviderOperationHealth } from "../domain/providerOperationHealth.js";
|
|
import {
|
|
relationshipCoverageSchema,
|
|
type RelationshipCoverage,
|
|
} from "../domain/relationshipCoverage.js";
|
|
|
|
export interface ExecutionOptions {
|
|
readonly signal?: AbortSignal;
|
|
readonly progress?: ProgressReporter;
|
|
/**
|
|
* Set when a procedure selector came from provider output, such as a
|
|
* revisited callee, rather than from the caller. A provider-derived
|
|
* selector is never the caller's input mistake. Absent means the caller
|
|
* supplied it.
|
|
*/
|
|
readonly selectorOrigin?: "provider";
|
|
}
|
|
|
|
/** Closed operations accepted by provider-backed analysis sessions. */
|
|
export type AnalysisOperation =
|
|
| OfficialToolName
|
|
| EnhancedToolName
|
|
| NativeToolName
|
|
| ArtifactAnalysisOperation
|
|
| ManagedToolName
|
|
| "health";
|
|
|
|
/** Atomic successful provider observation with exact producing provenance. */
|
|
export interface AnalysisExecution {
|
|
readonly result: JsonValue;
|
|
readonly rawResult: JsonValue | null;
|
|
readonly provider: ProviderIdentity;
|
|
readonly analysisProfile?: AnalysisProfileCommitment;
|
|
readonly limitations: readonly string[];
|
|
/** Provider-owned completeness assessment for caller/callee observations. */
|
|
readonly relationshipCoverage?: RelationshipCoverage;
|
|
/**
|
|
* Whether the provider capped an `xrefs` read: true when it reported a
|
|
* truncation, false when it explicitly reported none. Absent when it reports
|
|
* nothing, which is not a completeness claim. Never part of Evidence or tool
|
|
* output.
|
|
*/
|
|
readonly referencesTruncated?: boolean;
|
|
readonly locations: readonly EvidenceLocation[];
|
|
readonly subject: EvidenceSubjectTarget | null;
|
|
}
|
|
|
|
interface CreateAnalysisExecutionOptions {
|
|
readonly rawResult?: unknown;
|
|
readonly analysisProfile?: AnalysisProfileCommitment;
|
|
readonly limitations?: readonly string[];
|
|
readonly relationshipCoverage?: RelationshipCoverage;
|
|
readonly referencesTruncated?: boolean;
|
|
readonly locations?: readonly EvidenceLocation[];
|
|
readonly subject?: EvidenceSubjectTarget;
|
|
}
|
|
|
|
/**
|
|
* Build a validated atomic provider observation at an adapter boundary.
|
|
*
|
|
* Pass `rawResult` only for a provider representation distinct from
|
|
* `result`. Omitting it records `null` rather than a second complete copy of
|
|
* the normalized result in every Evidence record and tool response.
|
|
*/
|
|
export function createAnalysisExecution<Subject extends EvidenceSubjectTarget>(
|
|
result: unknown,
|
|
provider: ProviderIdentity,
|
|
options: CreateAnalysisExecutionOptions & { readonly subject: Subject },
|
|
): Omit<AnalysisExecution, "subject"> & { readonly subject: Subject };
|
|
export function createAnalysisExecution(
|
|
result: unknown,
|
|
provider: ProviderIdentity,
|
|
options?: CreateAnalysisExecutionOptions,
|
|
): AnalysisExecution;
|
|
export function createAnalysisExecution(
|
|
result: unknown,
|
|
provider: ProviderIdentity,
|
|
options: CreateAnalysisExecutionOptions = {},
|
|
): AnalysisExecution {
|
|
return {
|
|
result: jsonValueSchema.parse(result),
|
|
rawResult:
|
|
options.rawResult === undefined
|
|
? null
|
|
: jsonValueSchema.parse(options.rawResult),
|
|
provider,
|
|
...(options.analysisProfile === undefined
|
|
? {}
|
|
: { analysisProfile: structuredClone(options.analysisProfile) }),
|
|
limitations: [...(options.limitations ?? [])],
|
|
...(options.relationshipCoverage === undefined
|
|
? {}
|
|
: {
|
|
relationshipCoverage: relationshipCoverageSchema.parse(
|
|
options.relationshipCoverage,
|
|
),
|
|
}),
|
|
...(options.referencesTruncated === undefined
|
|
? {}
|
|
: { referencesTruncated: options.referencesTruncated }),
|
|
locations: [...(options.locations ?? [])],
|
|
subject: options.subject ?? null,
|
|
};
|
|
}
|
|
|
|
/** Provider-neutral application capability for one analysis operation. */
|
|
export interface AnalysisOperationPort {
|
|
execute(
|
|
operation: AnalysisOperation,
|
|
parameters: Readonly<Record<string, JsonValue>>,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<AnalysisExecution, AnalysisError>>;
|
|
}
|
|
|
|
/** Retained provider-process observation attributed to its producing provider. */
|
|
export interface ProviderRuntimeLineageSnapshot {
|
|
readonly provider: ProviderIdentity;
|
|
readonly observation: ProcessLineageObservation;
|
|
}
|
|
|
|
/** One provider's current serial-request activity, including detached callers. */
|
|
export interface ProviderRequestActivitySnapshot {
|
|
readonly provider: ProviderIdentity;
|
|
readonly active: {
|
|
readonly requestId: number;
|
|
readonly operation: string;
|
|
readonly elapsedMs: number;
|
|
readonly callerState: "waiting" | "cancelled";
|
|
} | null;
|
|
readonly queuedRequests: number;
|
|
}
|
|
|
|
/** Provider session bound to exactly one parsed artifact. */
|
|
export interface AnalysisClient extends AnalysisOperationPort {
|
|
/** Execute only if this operation can run without loading or starting a provider. */
|
|
executeIfReady?(
|
|
operation: AnalysisOperation,
|
|
parameters: Readonly<Record<string, JsonValue>>,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<AnalysisExecution, AnalysisError> | undefined>;
|
|
/** Retained token-verified process snapshots for dynamic providers this client started. */
|
|
runtimeLineageSnapshots?(): readonly ProviderRuntimeLineageSnapshot[];
|
|
/** Current provider work that may outlive a timed-out or cancelled caller. */
|
|
requestActivitySnapshots?(): readonly ProviderRequestActivitySnapshot[];
|
|
/** Current operational health for the active provider bridge. */
|
|
operationHealthSnapshot?(): ProviderOperationHealth | undefined;
|
|
/** Close owned resources and retain failed cleanup for a later retry. */
|
|
close(
|
|
options?: Pick<ExecutionOptions, "progress"> & {
|
|
readonly retainDocument?: boolean;
|
|
},
|
|
): Promise<Result<null, AnalysisError>>;
|
|
}
|
|
|
|
/** Per-client identity allocated before any provider side effect starts. */
|
|
export interface AnalysisClientContext {
|
|
readonly runId: string;
|
|
}
|
|
|
|
export interface ProviderIdentity {
|
|
readonly id: string;
|
|
readonly name: string;
|
|
readonly version: string | null;
|
|
}
|
|
|
|
/** Typed analysis profile resolved before provider startup; null means unresolved. */
|
|
export interface AnalysisProfileResolution {
|
|
readonly profile: AnalysisProfileCommitment | null;
|
|
}
|
|
|
|
/** Cancellation context for side-effect-free provider profile resolution. */
|
|
export interface AnalysisProfileResolutionOptions {
|
|
readonly signal?: AbortSignal;
|
|
}
|
|
|
|
interface CapabilityEffects {
|
|
readonly mutatesArtifact: boolean;
|
|
readonly launchesProcess: boolean;
|
|
readonly mayShowUi: boolean;
|
|
readonly mayAccessNetwork: boolean;
|
|
readonly mayWriteFilesystem: boolean;
|
|
readonly changesPermissions: boolean;
|
|
readonly requiresRoot: boolean;
|
|
}
|
|
|
|
type CapabilityAvailability =
|
|
| {
|
|
readonly available: true;
|
|
readonly reason: null;
|
|
readonly availabilityCode?: null;
|
|
}
|
|
| {
|
|
readonly available: false;
|
|
readonly reason: string;
|
|
readonly availabilityCode?: ProviderRejectionCode;
|
|
};
|
|
|
|
export type CapabilityDescriptor = CapabilityAvailability & {
|
|
/**
|
|
* Snapshot opts immutable, profile-bound reads into replay despite incidental
|
|
* private filesystem writes. Live state must always be re-observed. Omitting
|
|
* this policy retains conservative effect checks; mutation and UI-state
|
|
* exclusions apply to every policy.
|
|
*/
|
|
readonly cachePolicy?: "snapshot" | "live";
|
|
readonly provider: ProviderIdentity;
|
|
readonly operation: Exclude<AnalysisOperation, "health">;
|
|
readonly effects: CapabilityEffects;
|
|
readonly limitations: readonly string[];
|
|
};
|
|
|
|
/** Factory and capability declaration for an analysis implementation. */
|
|
export interface AnalysisProvider {
|
|
identity(): ProviderIdentity;
|
|
capabilities(): readonly CapabilityDescriptor[];
|
|
resolveAnalysisProfile?(
|
|
target: BinaryTarget,
|
|
options?: AnalysisProfileResolutionOptions,
|
|
): Promise<Result<AnalysisProfileResolution, AnalysisError>>;
|
|
createClient(
|
|
target: BinaryTarget,
|
|
profile?: AnalysisProfileCommitment,
|
|
context?: AnalysisClientContext,
|
|
): AnalysisClient;
|
|
}
|
|
|
|
/** Bounded host observation made without starting an analysis process. */
|
|
export type ProviderAvailability =
|
|
| {
|
|
readonly status: "available";
|
|
readonly code: null;
|
|
readonly reason: null;
|
|
readonly diagnostics: Readonly<Record<string, JsonValue>>;
|
|
}
|
|
| {
|
|
readonly status: "unavailable";
|
|
readonly code: ProviderRejectionCode;
|
|
readonly reason: string;
|
|
readonly diagnostics: Readonly<Record<string, JsonValue>>;
|
|
};
|
|
|
|
/** Provider-owned target support observation before profile resolution. */
|
|
export type ProviderTargetSupport =
|
|
| {
|
|
readonly status: "supported";
|
|
readonly code: null;
|
|
readonly reason: null;
|
|
readonly diagnostics: Readonly<Record<string, JsonValue>>;
|
|
}
|
|
| {
|
|
readonly status: "unsupported";
|
|
readonly code:
|
|
| "target_kind_unsupported"
|
|
| "target_format_unsupported"
|
|
| "architecture_unsupported"
|
|
| "target_role_unsupported"
|
|
| "managed_target_unsupported";
|
|
readonly reason: string;
|
|
readonly diagnostics: Readonly<Record<string, JsonValue>>;
|
|
};
|
|
|
|
/** One owner-observed availability/capability snapshot for provider discovery. */
|
|
export interface AnalysisProviderReadinessSnapshot {
|
|
readonly availability: ProviderAvailability;
|
|
readonly capabilities: readonly CapabilityDescriptor[];
|
|
}
|
|
|
|
/** Deep provider candidate discoverable before one target-bound route is chosen. */
|
|
export interface AnalysisProviderCandidate extends AnalysisProvider {
|
|
resolveAnalysisProfile(
|
|
target: BinaryTarget,
|
|
options?: AnalysisProfileResolutionOptions,
|
|
): Promise<Result<AnalysisProfileResolution, AnalysisError>>;
|
|
inspectAvailability(): ProviderAvailability;
|
|
inspectReadinessSnapshot?(): AnalysisProviderReadinessSnapshot;
|
|
inspectTargetSupport(target: BinaryTarget): ProviderTargetSupport;
|
|
}
|