import type { Scanner } from "../protocol/scanner.js"; import type { ScannerScheduleMode } from "../scanner-definition.js"; import type { ScanResult, ScannerId, StrategyRegistration } from "../types.js"; import { type ExternalScannerIngestRequest, type ExternalScannerIngestResult } from "../external-scanner-receiver.js"; import { type ScannerEngineDependencies } from "./data-providers.js"; import { StrategyRegistry } from "./strategy-registry.js"; import type { ScannerComposition } from "../../runtime/scanner-composition.js"; /** Public scanner registration shape exposed for list operations. */ export interface ScannerRegistrationSummary { address: string; scannerId: ScannerId; enabled: boolean; intervalSeconds: number; } export interface ScannerRegistrationStateSummary extends ScannerRegistrationSummary { scheduleMode: ScannerScheduleMode; initialized: boolean; inFlight: boolean; /** * When the in-flight scan started, or null when none is running. The boolean above cannot tell a * 200ms scan from one wedged for four minutes on a 15s interval; the age of this stamp can. */ inFlightSince: number | null; nextRunAt: number | null; } /** Runtime engine for scanner registration, scheduling, and one-off execution. */ export declare class ScannerEngine { private readonly deps; private readonly composition; private readonly providers; private readonly now; private readonly registry; private started; private readonly inFlight; private identity; private identityResolved; constructor(deps: ScannerEngineDependencies, composition: ScannerComposition, registry?: StrategyRegistry); private resolveIdentity; /** Registers one scanner for one strategy runtime. */ register(strategy: StrategyRegistration, scanner: Scanner): void; /** Enables scanner execution for one strategy + scanner key. */ enable(address: string, scannerId: ScannerId): void; /** Disables scanner execution and clears active timer if present. */ disable(address: string, scannerId: ScannerId): void; /** Starts interval scheduling for all enabled scanner registrations. */ start(): Promise; /** Stops all interval schedules and awaits in-flight runs before resolving. */ stop(): Promise; /** Lists registered scanners, optionally constrained to one strategy address. */ list(address?: string): ScannerRegistrationSummary[]; /** Lists detailed registration state used by health/state inspection surfaces. */ describeRegistrations(address?: string): ScannerRegistrationStateSummary[]; /** Reads scanner runtime config, bootstrapping runtime state when missing. */ getConfig(address: string, scannerId: ScannerId): Promise; /** Writes scanner runtime config after schema validation. */ setConfig(address: string, scannerId: ScannerId, config: unknown): Promise; /** * Executes one scanner run for one (address, scannerId) key. * Flow: overlap-guard -> runtime/config -> beforeScan -> scan -> normalize -> persist. */ runOnce(address: string, scannerId: ScannerId): Promise; /** * Runs the interval-scan body under the scanner.run span. Identity stamping sits inside the * try so the `finally` always clears the in-flight marker, even if the stamp ever throws. * runOnce swallows errors into a failure ScanResult, so withSpan's auto-record never fires — * the span is painted explicitly here. */ private runScan; /** Stamps the scanner.run span attributes from a terminal interval-scan result. */ private stampRunSpan; /** * Accepts one externally supplied signal/context payload for a push-driven * scanner. * * This path intentionally reuses the engine's runtime bootstrap, result * normalization, persistence, retained-context storage, and lifecycle * callbacks so external ingests look like ordinary scanner attempts to the * rest of the runtime. * * The in-flight marker is set before the first awaited store read. That * preserves the same single-run-per-scanner invariant as interval scans even * when two gateway ingests arrive in the same event-loop turn. */ ingestExternalScannerData(address: string, request: ExternalScannerIngestRequest): Promise; /** * Records a scaffold-reported tick failure for a push-driven external scanner * into run telemetry, WITHOUT going through the ingest pipeline. The scaffold * already ran the author's scan() and it threw / timed out / failed to persist * state, so there is no signal to ingest — only a failed run to account for. * * Resolves the registration by (address, scanner NAME) with the SAME rules as * {@link ingestExternalScannerData}, but NON-throwing: an unknown or ineligible * scanner returns `false` and logs a warn rather than throwing, because this is a * fire-and-forget telemetry seam off the untrusted `/errors` boundary. * * On success it emits the standard ERROR run-result through the run-result * lifecycle, so the runtime module's existing `scanner:run:error` → recordRunError * path increments errorCount/consecutiveErrorCount and stamps lastRunStatus="error" * — no dedicated telemetry channel for scaffold errors. * * @returns `true` when the failure was recorded, `false` for an unknown/ineligible scanner. */ recordExternalScannerError(address: string, scannerName: ScannerId, error: { type: string; error_type?: string; message?: string; tick_id?: string; }): boolean; /** * Runs the external-ingest body under the reduced scanner.run span. Identity stamping sits * inside the try so the `finally` always clears the in-flight marker. This path rethrows on * failure, so withSpan's auto-record paints ERROR; outcome=error is set for symmetry. */ private runExternalIngest; /** * Persists a terminal result and hands it to the run-result lifecycle. * * A failed persist must not delete the run: the write is a durability concern, the emit is the * only account that the run happened at all (telemetry's per-run record hangs off it). So the * write is guarded and reported as its own fault, and the result is emitted either way. Results * the persistence policy skips already reach the emit unwritten, so nothing downstream may treat * the emit as proof of a store write. * * @param startedAt This run's own start timestamp, threaded from the caller that began it. */ commitRunResult(address: string, scannerId: ScannerId, result: ScanResult, startedAt?: number): Promise; /** Executes one scanner plus its dependency chain immediately in topological order. */ runChain(address: string, scannerId: ScannerId): Promise; /** Reads one scanner's retained context payload, or null when it is unavailable. */ getScannerContext(address: string, scannerId: ScannerId): Promise; /** * Builds a terminal result for pre-run guard failures such as disabled scanners * or an overlapping in-flight execution for the same strategy/scanner pair. */ private buildEarlyExitResult; /** * Ensures persisted runtime state exists, validates config/state, and runs the * scanner's one-time initialization lifecycle before the first scan. */ private ensureInitializedRuntime; /** * Builds the scan context for one run and applies the optional `beforeScan` * hook, allowing scanners to gate or augment the prepared context. */ private buildPreparedContext; private buildFailureResult; private schedule; private buildInitContext; private buildScanContext; /** * Persists a terminal run result only when the scanner's persistence policy * says the result should be written to disk. */ private persistIfNeeded; /** * Commits retained context for external ingests through the same shared * artifact store used by built-in scanners. * * Built-in scanners write context from inside `scan()` via * `ctx.saveSharedArtifact(...)`. External scanners do not execute scanner * logic, so the engine must preserve that boundary explicitly here. */ private persistExternalContextIfNeeded; /** * Returns the registered strategy/scanner runtime entry or throws when the * requested pair is unknown to the engine. */ private requireRegistration; /** * Resolves the target registration for an external ingest and enforces the * routing invariants that make the ingest path safe. * * Route validation happens here instead of in the runtime/plugin layers so * every caller shares the same rules for unknown scanners, disabled targets, * and non-external registrations. Payload-shape compatibility with * signal-only versus context-producing scanners stays in the ingest validator. */ private requireExternalIngestRegistration; private defaultStateFor; private ensureValidState; private emitRunStart; /** * @param startedAt This run's own start timestamp; omitted when no run started (guard exit or an * out-of-band record), so consumers can tell "unknown" from "never began". */ private emitRunResult; private bootstrapContextDependencies; private buildAvailableScannerDefinitions; private resolveSourceScannerId; } //# sourceMappingURL=engine.d.ts.map