/** * MVI (Minimum Viable Information) record projection. * * Strips verbose record fields down to the essentials agents need for control * flow: id, title, status, and a handful of routing keys. The full record is * available again via the `--verbose` / `--human` / `--full` opt-out flag, which * the CLI translates into `_projection: 'full'` on the dispatch request. * * This is distinct from the LAFS envelope-level projection in * `@cleocode/lafs/mviProjection` (which strips envelope chrome like `$schema` * / `_meta`), from the tier-based domain-access gate in * `packages/cleo/src/dispatch/middleware/projection.ts` (which gates which * domains are reachable), and from the JSON Pointer extractor in * `./projection.ts` (T9929 — `--field` flag). This module trims the DATA * payload itself per record kind so `cleo show T###` returns ~150 bytes * instead of ~1.6 KB. * * @packageDocumentation * @module @cleocode/core/dispatch/mvi-projection * * @epic T9855 (Saga) * @task T9922 (E8.3) * @task T11351 (Epic T11285 EP-MVI-PRIMITIVE) — generalized budget-aware projector */ /** * Mode of projection applied to a single record. * * - `'mvi'`: only the fields essential for agent control flow are kept. * - `'full'`: the record is returned unchanged. */ export type ProjectionMode = 'mvi' | 'full'; /** * Recognized record kinds for MVI projection. * * `'unknown'` is the safe fallback — when the dispatcher cannot identify the * record shape, the record is passed through untouched (no field stripping). */ export type ProjectionKind = 'task' | 'epic' | 'saga' | 'doc' | 'unknown'; /** * Project a single record down to its MVI field-set for the given kind. * * Non-object inputs (null, primitives, arrays) are returned unchanged — this * function operates on a single record, not a collection. Use * {@link projectMviList} for arrays. * * @param record - The record to project (typically a task, epic, or doc row). * @param kind - The record kind. Unknown kinds pass through unchanged so * callers don't accidentally strip fields off shapes the * projection table does not understand. * @returns A new object containing only the MVI-essential fields when `kind` * is recognized, or the original `record` reference otherwise. * * @example * ```ts * const mvi = projectMvi(taskRecord, 'task'); * // { id: 'T9922', title: '...', status: 'pending', priority: 'high', ... } * ``` */ export declare function projectMvi>(record: T, kind: ProjectionKind): Partial; /** * Options for the generalized {@link projectMVI} projector. * * @task T11351 */ export interface ProjectMVIOptions { /** * Record kind. Known kinds (`task`/`epic`/`saga`/`doc`) use their * {@link MVI_FIELDS} allow-list; `'unknown'` (or any unrecognized value) * degrades to the {@link GENERIC_MVI_FIELDS} identity/routing set rather than * leaking the full record. */ kind: ProjectionKind; /** * Projection mode. `'full'` is a no-op (returns the record unchanged); * `'mvi'` applies field selection. Defaults to `'mvi'`. */ mode?: ProjectionMode; /** * Optional hard token budget. When set, the projected record is measured by * the LAFS {@link TokenEstimator} and trailing fields are dropped until it * fits. `id` is preserved as the last-resort minimum so the result is always * routable. Omit for field-allow-listing only (no token enforcement). */ budget?: number; } /** * The single, generalized, budget-aware MVI projector. * * Supersedes the kind-only {@link projectMvi} for callers that need (a) graceful * degradation on unknown kinds and (b) a real token budget rather than pure * field-allow-listing: * * 1. `mode: 'full'` → returns the record unchanged. * 2. Known kind → keeps the kind's {@link MVI_FIELDS} allow-list. * 3. Unknown kind → keeps only {@link GENERIC_MVI_FIELDS} (never the full * payload — this closes the pre-T11351 unknown-kind leak). * 4. If `budget` is set → delegates to the LAFS {@link TokenEstimator} and drops * trailing fields until the projected record fits, keeping `id` as the * last-resort minimum. * * @typeParam T - The record shape. * @param record - The record to project. Non-object inputs are returned as-is. * @param options - {@link ProjectMVIOptions}. * @returns A new projected (and possibly budget-reduced) record. * * @example * ```ts * // Known kind, no budget — same field set as projectMvi(record, 'task'). * projectMVI(taskRecord, { kind: 'task' }); * * // Unknown kind — generic identity fields only, never the full payload. * projectMVI(weirdRecord, { kind: 'unknown' }); * * // Budget-aware — reduces below 20 tokens. * projectMVI(bigRecord, { kind: 'task', budget: 20 }); * ``` * * @task T11351 * @epic T11285 */ export declare function projectMVI>(record: T, options: ProjectMVIOptions): Partial; /** * Project every element of an array via {@link projectMvi}. * * @param records - Array of records to project. Empty arrays are returned * as-is. * @param kind - The record kind applied to every element. * @returns A new array of projected records. * * @example * ```ts * const projected = projectMviList(tasks, 'task'); * ``` */ export declare function projectMviList>(records: readonly T[], kind: ProjectionKind): Partial[]; /** * Routing table that maps a canonical `.` identifier to the * projection plan for that op's response data. * * The plan tells the dispatch middleware which key inside the envelope data * carries the record(s) to project and what kind they are. A missing entry * means "no projection" — the op opts out by default. * * @remarks * Keep this map tight: only the read ops named in the T9922 acceptance * criteria (`tasks.show`, `tasks.list`, `tasks.find`, `docs.list`, * `docs.fetch`) are wired here. Adding new ops is a deliberate act — the * caller must reason about what an agent actually needs from the response. */ export interface ProjectionPlan { /** * The path inside `response.data` to project. Use `'$'` to project the * top-level data object itself. Dot-separated paths drill into nested * objects (e.g. `'task'` projects `data.task`). */ path: string; /** The kind to apply at that path. */ kind: ProjectionKind; /** * When `true`, treat the value at `path` as an array and project each * element. When `false`, treat it as a single record. */ list: boolean; } /** SSoT for which ops get MVI-projected by default. */ export declare const PROJECTION_PLANS: Readonly>; /** * Apply the {@link PROJECTION_PLANS} entry for a given operation to a data * envelope. * * Mutation safety: the returned value is a new top-level object when the plan * matches; the original record references inside arrays are replaced with * projected copies. When the operation has no plan, the original `data` * reference is returned unchanged. * * Plans that point at a missing path (e.g. an empty list result that lacks * `tasks`) are no-ops — projection never throws on unexpected shapes. * * @param data - The dispatch response `data` payload. * @param operation - The canonical `.` identifier. * @param mode - `'mvi'` applies the plan; `'full'` is a no-op. * @returns The (possibly new) data payload after projection. */ export declare function applyProjectionPlan(data: unknown, operation: string, mode: ProjectionMode): unknown; /** * Resolve the projection mode for a request based on the opt-out signal. * * The CLI surfaces three flags that all mean "give me the full record": * `--verbose`, `--human`, and `--full`. Any of them flips the mode to * `'full'`; otherwise MVI is the default for the ops listed in * {@link PROJECTION_PLANS}. * * @param signal - The request-level opt-out signal. May arrive as a boolean * (when the CLI parsed a flag) or `undefined` (no flag set). * @returns The resolved {@link ProjectionMode}. */ export declare function resolveProjectionMode(signal: boolean | undefined): ProjectionMode; //# sourceMappingURL=mvi-projection.d.ts.map