/**
 * Per-action realize runner — Phase 3 of the L3 redesign.
 *
 * The orchestration glue between the spec walker (controllers + services
 * + their actions/operations) and the per-action emitter. This is the
 * call site that REPLACES the legacy `generateAiBehaviors` step in
 * `realize/index.ts` when the per-action path is enabled.
 *
 * Responsibilities:
 *   1. Walk the spec's controllers + services and collect every custom
 *      action (controller actions + service operations) along with the
 *      owning entity name + step list + spec metadata.
 *   2. For each action, build an ActionSpec + RealizeContext and call
 *      emitActionBody. The matcher (prisma / mongo-native / pg-native)
 *      and conventions array are passed through as injected
 *      dependencies — same shape the legacy ai-behaviors path uses.
 *   3. Group results by owner and emit one `<Owner>.ai.ts` file per
 *      owner, containing one exported async function per action with
 *      the LLM-emitted (or stub-γ-emitted) body inline.
 *
 * Concurrency: the LLM call site (when a real LLM is configured) is
 * sequential per owner — keeps surgical-retry's parse-error feedback
 * clean and bounds the API spend in the worst case. A future Phase
 * could parallelise across owners; not the default in V1.
 *
 * The legacy `<Owner>.ai.ts` emitted by `ai-behaviors-generator.ts`
 * contains per-STEP helpers; the new file contains per-ACTION pure
 * functions. DR2 says keep the filename, repurpose the content shape.
 * Both shapes co-exist when this runner runs alongside the legacy
 * generator (gated by env flag); the new file overwrites the legacy
 * output when both are run for the same owner. The integration test
 * pins the new shape; the env flag governs which runs in realize.
 */
import { type ActionSpec, type LlmEmitFn, type SharedConvention, type SharedStepContext, type MatcherFn } from './per-action-emitter.js';
/**
 * One action collected from the spec walk. Tracks both the owning
 * entity (for grouping into a single `.ai.ts` file) and the spec
 * metadata the per-action emitter needs.
 */
export interface CollectedAction {
    ownerKind: 'controller' | 'service' | 'model';
    /** Controller / service name (the file owner). */
    ownerName: string;
    /** The controller's `model:` reference (or service's "self" name). */
    modelName: string;
    /** Action / operation name as it appears in the spec. */
    actionName: string;
    /** Original ActionSpec — passed straight to emitActionBody. */
    spec: ActionSpec;
}
/** Per-owner emission result — drives the `.ai.ts` file write. */
export interface OwnerEmissionResult {
    ownerName: string;
    /** Map<actionName, body> — the body strings the emitter produced. */
    bodies: Map<string, string>;
    /** Per-action ActionSpec lookup, for shaping the `.ai.ts` signatures. */
    specs: Map<string, ActionSpec>;
}
/** Options for {@link runPerActionEmission}. */
export interface RunPerActionEmissionOptions {
    /** The full spec (already parsed / inferred). */
    spec: any;
    /** Output directory — `.ai.ts` files go under `${outputDir}/backend/src/behaviors/`. */
    outputDir: string;
    /** Convention matcher (matchAgainstConventions, or a test stub). */
    matcher?: MatcherFn;
    /** ORM-specific convention list — prisma / mongo-native / pg-native. */
    conventions?: ReadonlyArray<SharedConvention<SharedStepContext>>;
    /** AI-args expression for the matcher's fallback shape. */
    aiArgsExpr?: (inputs: string[], paramNames: string[]) => string;
    /** LLM emit hook — typically createRealizeActionLlmEmit(). When omitted,
     *  every action falls through to γ-stub mode. */
    llmEmit?: LlmEmitFn;
    /** LLM availability predicate. Default mirrors the per-action emitter. */
    canCallLLM?: () => boolean;
    /** When true, skip writing to disk and return the in-memory result.
     *  Used by tests to assert the produced bodies without touching fs. */
    dryRun?: boolean;
    /** Quiet mode for tests — suppress console.log progress lines. */
    silent?: boolean;
    /**
     * V2 P3 Round 3 follow-up: per-owner availableClients string injected
     * into `RealizeContext.availableClients`, which the LLM emit hook
     * forwards into the realize-action prompt's `availableClients` slot.
     * Keys: ownerName (controller / service name). Values: pre-formatted
     * client list. Built by callers from `spec.components[].import` —
     * see {@link buildAvailableClientsByOwner}.
     */
    availableClientsByOwner?: Map<string, string>;
}
/** Aggregate result from {@link runPerActionEmission}. */
export interface RunPerActionEmissionResult {
    /** One entry per owning entity (controller or service). */
    owners: OwnerEmissionResult[];
    /** Files actually written to disk (empty in dryRun mode). */
    filesWritten: string[];
    /** Aggregate counters for logging / report assembly. */
    totals: {
        actionCount: number;
        ownerCount: number;
        stubCount: number;
        llmCount: number;
        fullyMatchedCount: number;
    };
}
/**
 * Walk the spec, dispatch each custom action through the per-action
 * emitter, and write per-owner `.ai.ts` files.
 *
 * Collection rules:
 *   - Controllers: every entry under `controller.actions` becomes a
 *     CollectedAction. CURVED ops (`controller.cured.{create,...}`)
 *     are skipped — the L1 instance-factory templates handle those
 *     deterministically.
 *   - Services: every entry under `service.operations` becomes a
 *     CollectedAction. Service operations don't have CURVED kinship.
 *   - Model behaviours (`model.behaviors.<name>`) are NOT collected by
 *     this runner — the controller / service is the realized surface;
 *     model behaviours flow through controllers via the inference
 *     pipeline before realize sees them. A future phase could collect
 *     them directly if needed.
 */
export declare function runPerActionEmission(opts: RunPerActionEmissionOptions): Promise<RunPerActionEmissionResult>;
/**
 * Walk the spec and collect every custom controller action + service
 * operation that has a non-empty `steps:` block. Without steps, the
 * per-action emitter has no work to do — those actions stay as
 * placeholder methods on the controller / service.
 */
export declare function collectActions(spec: any): CollectedAction[];
/**
 * Render the `.ai.ts` content for one owner. Exported so tests can
 * assert on the produced shape without going to disk.
 */
export declare function renderOwnerAiFile(owner: OwnerEmissionResult): string;
/**
 * Build a single component's `availableClients` prompt string from its
 * V2 `import:` block.
 *
 * Output shape (one line per importable operation):
 *   - deps.<clientName>.<operation>(input)  // from <fromName> (<topology>)
 *   - deps.<clientName>  // bare entity export from <fromName> (<topology>)
 *
 * Topology is included when known (caller can pass a resolver-result
 * lookup); when unknown, we omit the parenthetical. The Round-3 wire-up
 * in `realize/index.ts` runs BEFORE the V2 imports loop has resolved
 * factories, so most calls today omit topology — fine. The slot is
 * forward-compatible: a future revision can pass topology by name and
 * we'll surface it.
 *
 * Returns `null` when the component has no imports (caller should not
 * set `availableClients` at all in that case so the LLM hook falls
 * back to its "(none)" default).
 */
export declare function buildAvailableClientsForComponent(component: any, topologyByFrom?: Map<string, string>): string | null;
/**
 * Walk a parsed spec and build the per-owner `availableClients` map the
 * per-action runner threads into `RealizeContext.availableClients`.
 *
 * Resolution: each `spec.components[i]` carries an `import:` block AND
 * a set of contained controllers + services. Every controller / service
 * inside the component sees the same `availableClients` string (V2
 * imports are component-scoped; per-action filtering via `Action.uses`
 * is a future refinement).
 *
 * Components-without-imports do not contribute entries — owners in
 * those components won't have `availableClients` set, and the LLM hook
 * falls back to its "(none)" default. This keeps the prompt clean for
 * specs that don't use V2 imports at all.
 */
export declare function buildAvailableClientsByOwner(spec: any, topologyByFrom?: Map<string, string>): Map<string, string>;
//# sourceMappingURL=per-action-runner.d.ts.map