@sema-agent/core
Version:
Stateless, task-oriented AI agent core
58 lines (57 loc) • 3.73 kB
TypeScript
/**
* The ONE folding engine every listing frame renders through — the `<skills>` block, the skills drift
* frame's add arm, the agent-type roster and its drift frame's add arm. A listing is an intact-class
* turn attachment (`turn-attachments.ts` `ATTACHMENT_DELIVERY_CLASS`): the delivery lane never clips
* it, so this engine is the frame's whole bound, byte-exact over the caller's `frame` (headers, fences,
* markers, joiners included).
*
* Three honesty tiers over ONE set of items, each chosen with the LATER tiers' fixed overhead already
* reserved so the final render never blows the budget it was sized against:
* A. everything fits → every item gets its full line;
* B. reserve the cap marker + EVERY item's name-only line first, then spend the remaining slack on
* full lines in PRIORITY order (`rank` ascending; absent ⇒ index), admitting each full line whose
* incremental cost still fits and skipping (not stopping at) the ones that do not — every item stays
* listed, the low-priority ones lose their detail;
* C. even all-name-only overflows → the longest name-only prefix that fits, closed by the caller's
* overflow count line.
* Pure: the same items, spec and frame always render the same bytes.
*/
export interface ListingFoldSpec<T> {
/** The whole frame's byte budget, honoured as given. */
blockMaxBytes: number;
/** One item's full line (tier A, and tier B's admitted lines). */
full: (item: T) => string;
/** One item's name-only line (tier B's degraded form, tier C's listed prefix). */
nameOnly: (item: T) => string;
/** The raw name reported for an item the budget degraded (host-facing data, not prompt text). */
nameOf: (item: T) => string;
/** Priority for keeping the full line: ascending, 0 = first. Absent ⇒ the item's index. */
rank?: (item: T, index: number) => number;
/** The marker line that opens a degraded (tier B/C) body. */
capMarker: string;
/** Tier C's closing line for `n` items that could not be listed at all. */
overflowLine: (n: number) => string;
/** Renders body lines into the lane's frame; its fixed cost is priced once by rendering it empty. */
frame: (lines: readonly string[]) => string;
}
/** What one fold produced: the frame, and which items the budget degraded (display order). */
export interface ListingFoldRender {
text: string;
/** Items listed WITHOUT their full line. */
nameOnly: readonly string[];
/** Items the frame could not list at all (tier C's overflow tail). */
dropped: readonly string[];
}
export declare function foldListingWithin<T>(items: ReadonlyArray<T>, spec: ListingFoldSpec<T>): ListingFoldRender | undefined;
/** The removal arm's FLOOR — the departure fact with none of the names; constant-size, which is what
* keeps the fact from being starved by a budget the names cannot fit. */
export declare function removalArmFloor(header: string, count: number): string;
/**
* A listing frame's REMOVAL arm rendered INSIDE a byte budget: every departed name when they fit,
* otherwise the longest prefix that does, closed by an honest count line — tier C over a list whose
* lines have one form, so there is no name-only tier between them. Names are neutralized on the way
* out: the announced map holds raw names from the commit half. Cost of the bound, stated: a name that
* does not fit is never announced (the commit half advances past it), so the model can still hold an
* entry that is gone — one failed call, against a frame that would otherwise never fit its budget.
*/
export declare function foldRemovalArmWithin(removed: readonly string[], header: string, maxBytes: number): string;