/** * admit-tool-package — the tool admission pipeline as ONE reusable callable * (ADR-0041: one validator, four consumers). * * The sequence every whole-tool plugin travels — `loadToolManifest` → * `admitTool` (compatibility gate) → dynamic runtime import → `isValidTool` * shape gate → `assertManifestMatchesTool` drift guard — previously lived * inline in `register-tools.ts` with per-source failure POLICY (bundled fails * closed, installed skips-with-diagnostic) woven through it. This module * factors the SEQUENCE out as a report producer and leaves policy at the * callers: * * - `registerFirstPartyTools` (bundled bootstrap) converts a failed report * into the same fail-closed `PluginIncompatibleError`s it always threw. * - `tools validate` renders the report's sections to the user. * - `tools install` gates activation on `report.ok`. * - The bundled-tool conformance tests run it against fitness/sim/graph. * * EXECUTES UNTRUSTED CODE: the runtime sections dynamic-import the package's * module. Callers that need isolation (e.g. `tools validate` probing a * not-yet-trusted package) pass `staticOnly: true` here and run the runtime * sections in a child-process probe instead. * * ADR-0054 M4-G (capstone): external tool runtimes NEVER import in the host * process. The capstone invariant is mechanized at the type level: a HOST import * policy ({@link ToolRuntimeImportPolicy}) is `{ source: 'bundled' }` ONLY — * `hostRuntimeImportPolicyFor` accepts only `'bundled'`, so a non-bundled host * import is a COMPILE error, not a runtime guard. The forked dispatch worker (the * isolation boundary) imports the untrusted external runtime via the distinct * {@link workerRuntimeImportPolicyFor} (`{ source, inDispatchWorker: true }`), * named for what it is. The host registers a manifest-derived synthetic Tool for * external provenance (see `synthesize-external-tool.ts`) and never loads its * runtime; the worker imports it when a command dispatches. */ import { type RawToolPluginManifest, type Tool, type ToolPluginManifest, type ToolProvenance, type ToolSource } from '@opensip-cli/core'; /** * The outcome of importing a tool package's runtime module. A discriminated * result (never throws) so each caller maps it to its own policy — bundled * fails closed, installed skips-with-diagnostic. (Relocated verbatim from * `register-tools.ts`; the authored/installed discovery legs import it from * here.) */ export type ToolRuntimeLoad = { readonly ok: true; readonly tool: Tool; } | { readonly ok: false; readonly reason: 'no-entry' | 'invalid-shape' | 'import-failed'; readonly detail?: string; }; /** * The HOST import policy (ADR-0054 M4-G capstone). A host-process tool runtime * import is `{ source: 'bundled' }` ONLY — bundled tools are the trusted * computing base. External provenance can NOT produce a host policy: the type * makes the external host-import unrepresentable (a compile error), not merely a * runtime guard. External runtimes load only behind the worker boundary (see * {@link WorkerRuntimeImportPolicy}). */ export interface ToolRuntimeImportPolicy { readonly source: 'bundled'; } /** * The WORKER import policy (ADR-0054 M4-G). Inside the forked dispatch worker — * the isolation boundary — importing the untrusted external runtime IS the goal. * A worker import is either the bundled host policy (the worker re-runs the same * bootstrap, which imports bundled tools too) or the named external worker policy * (`{ source, inDispatchWorker: true }`). It is constructed ONLY by * {@link workerRuntimeImportPolicyFor} on the worker-owned discovery path; the * fitness check confines its use to the worker plane. */ export type WorkerRuntimeImportPolicy = ToolRuntimeImportPolicy | { readonly source: Exclude; readonly inDispatchWorker: true; }; /** * The bundled-only HOST import policy constructor. Accepts ONLY `'bundled'` — a * `hostRuntimeImportPolicyFor('installed')` is a COMPILE error (the capstone * invariant, type-enforced). External provenance never reaches a host import. */ export declare function hostRuntimeImportPolicyFor(source: 'bundled'): ToolRuntimeImportPolicy; /** * The WORKER import policy constructor (ADR-0054 M4-G). Used ONLY on the * worker-owned discovery path (inside the forked `__tool-command-worker`, gated * on `OPENSIP_CLI_IN_TOOL_WORKER`). A bundled source produces the plain host * policy; an external source produces the named `inDispatchWorker` policy — the * legitimate place untrusted external runtime loads. */ export declare function workerRuntimeImportPolicyFor(source: ToolSource): WorkerRuntimeImportPolicy; /** * Resolve a tool package's entry, DYNAMIC-IMPORT it, and validate the exported * `tool` shape. This is the ONE runtime-load path every installation source * travels (1.0.0 launch, north-star Figure 7): no static `import` of a tool runtime * survives in the host — a bundled tool is imported by its resolved entry path * exactly as an installed one is. Import is by `pathToFileURL(meta.mainEntry)`, * not the bare package name, so a tool living in a host dir off the CLI's own * module-resolution path still loads. A third-party tool is an untrusted * boundary, so `isValidTool` gates the exported symbol before it is touched. * * ADR-0054 M4-G: the `policy` is `{ source: 'bundled' }` for a HOST import or the * `inDispatchWorker` worker policy for an external import inside the dispatch * worker. A bare external source can no longer be expressed (the type forbids it); * the runtime check is defense-in-depth. * * Never throws: returns a discriminated result the caller acts on. */ export declare function importToolRuntime(dir: string, policy: WorkerRuntimeImportPolicy): Promise; /** The named conformance sections of the admission pipeline, in run order. */ export type AdmissionSection = 'manifest' | 'compatibility' | 'runtime-load' | 'tool-shape' | 'manifest-runtime-coherence'; /** One section's verdict. `diagnostic` is present on failure (and only then). */ export interface AdmissionSectionResult { readonly section: AdmissionSection; readonly ok: boolean; readonly diagnostic?: string; } /** * The full admission verdict for one package dir. `ok` ⇔ every executed * section passed. Sections later than the first failure are not executed * (each depends on its predecessor's artifact); with `staticOnly` the three * runtime sections are not executed either — absent from `sections`, so a * renderer can show them as skipped. */ export interface AdmissionReport { readonly ok: boolean; readonly sections: readonly AdmissionSectionResult[]; /** * The raw `loadToolManifest` result — present once the manifest section * passes, even when the compatibility gate later rejects (callers render * the candidate's id from it). */ readonly rawManifest?: RawToolPluginManifest; /** The ADMITTED manifest — present iff the compatibility section passed. */ readonly manifest?: ToolPluginManifest; /** Present iff the compatibility section passed. */ readonly provenance?: ToolProvenance; /** Present iff every runtime section passed (never with `staticOnly`). */ readonly tool?: Tool; /** The raw gate decision when the compatibility section ran. */ readonly compatibilityDecision?: 'admit' | 'skip' | 'fail-closed'; /** Failure detail from the runtime-load/tool-shape sections, when they ran. */ readonly runtimeLoadReason?: 'no-entry' | 'invalid-shape' | 'import-failed'; readonly runtimeLoadDetail?: string; /** * The ORIGINAL error thrown by `assertManifestMatchesTool` when the * coherence section fails — preserved so a fail-closed caller (the bundled * bootstrap) can rethrow it unchanged. */ readonly coherenceError?: unknown; } /** Input to {@link admitToolPackage}. */ export interface AdmitToolPackageOptions { /** The package directory whose `package.json#opensipTools` is the manifest. */ readonly dir: string; readonly source: ToolSource; readonly packageName?: string; /** Threaded to the compatibility gate (skip-vs-fail posture lives there). */ readonly explicitlyRequested: boolean; /** * Stop after the static (no-code-execution) sections: manifest + * compatibility. The runtime sections (`runtime-load`, `tool-shape`, * `manifest-runtime-coherence`) execute the package's module — callers * validating untrusted candidates run those in a child-process probe. */ readonly staticOnly?: boolean; } /** * Run the admission pipeline over one package dir and report per-section * verdicts. Pure sequencing — no logging, no throwing, no registration; * policy (fail-closed vs skip vs render) belongs to the caller. */ export declare function admitToolPackage(opts: AdmitToolPackageOptions): Promise; //# sourceMappingURL=admit-tool-package.d.ts.map