Skip to content

Class: Presenter<T>

Defined in: packages/core/src/presenter/Presenter.ts:152

Domain-level Presenter — the "View" in MVA (Model-View-Agent).

Encapsulates:

  • Schema (Zod): Validates and filters data before it reaches the LLM
  • System Rules: JIT context directives that travel with the data
  • UI Blocks: SSR-rendered visual blocks (charts, diagrams, tables)
  • Agent Limit: Smart truncation for large collections
  • Action Suggestions: HATEOAS-style next-action hints
  • Embeds: Relational Presenter composition (DRY)

See

createPresenter for the factory function

Type Parameters

Type ParameterDescription
TThe validated output type (inferred from the Zod schema)

Methods

agentLimit()

ts
agentLimit(max, onTruncate): this;

Defined in: packages/core/src/presenter/Presenter.ts:371

Set a cognitive guardrail that truncates large collections.

Protects against context DDoS: if a tool returns thousands of records, the Presenter automatically truncates the data array and injects a summary block teaching the AI to use filters.

Parameters

ParameterTypeDescription
maxnumberMaximum items to keep in the data array
onTruncate(omittedCount) => UiBlockCallback that generates a warning UI block. Receives the count of omitted items.

Returns

this

this for chaining

Example

typescript
.agentLimit(50, (omitted) =>
    ui.summary(`⚠️ Truncated. 50 shown, ${omitted} hidden. Use filters.`)
)

asyncCollectionUiBlocks()

ts
asyncCollectionUiBlocks(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:782

Register an async UI block callback for collections.

Parameters

ParameterTypeDescription
fnAsyncCollectionUiBlocksFn<T>Async function receiving the full validated array + optional context

Returns

this

this for chaining


asyncRules()

ts
asyncRules(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:793

Register async system rules generation.

Parameters

ParameterTypeDescription
fnAsyncRulesFn<T>Async function receiving validated data + optional context

Returns

this

this for chaining


asyncSuggestActions()

ts
asyncSuggestActions(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:804

Register async action suggestions generation.

Parameters

ParameterTypeDescription
fnAsyncSuggestActionsFn<T>Async function receiving validated data + optional context

Returns

this

this for chaining


asyncUiBlocks()

ts
asyncUiBlocks(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:771

Register an async UI block callback for single items.

Use when UI generation requires I/O (database, API, file system). Must be consumed via makeAsync()make() ignores async callbacks.

Parameters

ParameterTypeDescription
fnAsyncItemUiBlocksFn<T>Async function receiving validated data + optional context

Returns

this

this for chaining

Example

typescript
createPresenter('Invoice')
    .asyncUiBlocks(async (inv, ctx) => {
        const history = await ctx.db.payments.history(inv.id);
        return [ui.echarts(buildTimeline(history))];
    });

collectionRules()

ts
collectionRules(rules): this;

Defined in: packages/core/src/presenter/Presenter.ts:514

Set collection-level system rules.

Unlike .systemRules(), these rules are evaluated with the entire array of validated items. Use for aggregate context (totals, counts, mixed-status warnings) that cannot be derived from a single item.

Both per-item and collection rules are merged in the response.

Parameters

ParameterTypeDescription
rulesCollectionRulesFn<T>Static rules array, or dynamic `(items[], ctx?) => (string

Returns

this

this for chaining

Example

typescript
.collectionRules((invoices, ctx) => [
    `Total: ${invoices.length} invoices found.`,
    invoices.some(i => i.status === 'overdue')
        ? '⚠️ Some invoices are OVERDUE. Highlight them.'
        : null,
])

collectionSuggest()

ts
collectionSuggest(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:470

Alias for .collectionSuggestActions() — fluent shorthand.

Parameters

ParameterTypeDescription
fnCollectionSuggestActionsFn<T>`(items[], ctx?) => (ActionSuggestion

Returns

this

this for chaining


collectionSuggestActions()

ts
collectionSuggestActions(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:431

Define HATEOAS-style action suggestions for collections.

Unlike .suggestActions(), this callback receives the entire array of validated items, enabling aggregate-level suggestions like batch operations, bulk approvals, or summary insights.

When both .suggestActions() and .collectionSuggestActions() are set, only collectionSuggestActions is used for arrays.

Parameters

ParameterTypeDescription
fnCollectionSuggestActionsFn<T>`(items[], ctx?) => (ActionSuggestion

Returns

this

this for chaining

Example

typescript
.collectionSuggestActions((invoices) => [
    invoices.some(i => i.status === 'overdue')
        ? { tool: 'billing.batch_remind', reason: 'Send batch reminders' }
        : null,
    invoices.length > 100
        ? { tool: 'billing.export', reason: 'Export results for offline review' }
        : null,
])

collectionUiBlocks()

ts
collectionUiBlocks(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:346

Define aggregated UI blocks for a collection (array) of items.

When the handler returns an array, this callback is called once with the entire validated array. Prevents N individual charts from flooding the LLM's context.

Parameters

ParameterTypeDescription
fnCollectionUiBlocksFn<T>`(items[], ctx?) => (UiBlock

Returns

this

this for chaining

Example

typescript
.collectionUiBlocks((invoices) => [
    ui.echarts({ xAxis: { data: invoices.map(i => i.id) } }),
    ui.summary(`${invoices.length} invoices found.`),
])

embed()

ts
embed(key, childPresenter): this;

Defined in: packages/core/src/presenter/Presenter.ts:583

Compose a child Presenter for a nested relation.

When data[key] exists, the child Presenter's rules and UI blocks are merged into the parent response. This is the DRY solution for relational data: define ClientPresenter once, embed it everywhere.

Parameters

ParameterTypeDescription
keystringThe property key containing the nested data
childPresenterPresenter<unknown>The Presenter to apply to data[key]

Returns

this

this for chaining

Example

typescript
import { ClientPresenter } from './ClientPresenter';

export const InvoicePresenter = createPresenter('Invoice')
    .schema(invoiceSchema)
    .embed('client', ClientPresenter);

getSchemaKeys()

ts
getSchemaKeys(): string[];

Defined in: packages/core/src/presenter/Presenter.ts:682

Get the Zod schema's top-level keys.

Returns the field names declared in the Presenter's schema. Safe to call at any time — does NOT seal the Presenter.

Returns

string[]

Array of key names, or empty array if no schema is set


getStaticRuleStrings()

ts
getStaticRuleStrings(): readonly string[];

Defined in: packages/core/src/presenter/Presenter.ts:746

Return static rule strings for introspection hashing.

If rules are dynamic (function), returns an empty array because the actual rule content depends on runtime data/context.

Returns

readonly string[]

Static rule strings, or empty array if rules are contextual


getUiBlockTypes()

ts
getUiBlockTypes(): string[];

Defined in: packages/core/src/presenter/Presenter.ts:717

Get which UI block factory methods were configured.

Inspects the configuration callbacks to determine supported UI block types. Does NOT execute any callbacks.

Returns

string[]

Array of UI block type labels


hasAsyncCallbacks()

ts
hasAsyncCallbacks(): boolean;

Defined in: packages/core/src/presenter/Presenter.ts:903

Check if this Presenter has any async callbacks configured.

Used by the pipeline to decide between sync make() and async makeAsync(). When no async callbacks are set, makeAsync() is equivalent to Promise.resolve(make()), so the sync path is preferred.

Returns

boolean

true if any async callback is configured


hasContextualRules()

ts
hasContextualRules(): boolean;

Defined in: packages/core/src/presenter/Presenter.ts:734

Whether the Presenter uses dynamic (context-aware) system rules.

Static rules (string arrays) are NOT contextual. Functions (data, ctx?) => ... ARE contextual.

Returns

boolean

true if rules are a function


limit()

ts
limit(max): this;

Defined in: packages/core/src/presenter/Presenter.ts:556

Cognitive guardrail shorthand with auto-generated message.

Truncates large collections and injects a smart summary block. For custom truncation messages, use .agentLimit(max, onTruncate) instead.

Parameters

ParameterTypeDescription
maxnumberMaximum items to keep in the data array

Returns

this

this for chaining

Example

typescript
// Auto-generated message:
.limit(50)
// → "⚠️ Dataset truncated. 50 shown, {omitted} hidden. Use filters to narrow results."

// For custom message, use agentLimit():
.agentLimit(50, (omitted) => ui.summary(`Custom: ${omitted} hidden`))

make()

ts
make(
   data, 
   ctx?, 
   selectFields?): ResponseBuilder;

Defined in: packages/core/src/presenter/Presenter.ts:875

Compose a ResponseBuilder from raw data.

Orchestrates: truncate → validate → embed → render UI → attach rules → suggest actions → Late Guillotine (_select filter).

Late Guillotine pattern: UI blocks, system rules, and action suggestions are computed using the full validated data, ensuring charts and rules never see undefined for pruned fields. Only the wire-facing data block in the ResponseBuilder is filtered by _select — the UI consumes full data in RAM, the AI consumes pruned data on the wire.

After the first call, the Presenter is sealed (immutable).

Auto-detection: If data is an array, items are validated individually and collectionUiBlocks is called (if defined). Otherwise, uiBlocks is called for the single item.

Parameters

ParameterTypeDescription
dataT | T[]Raw data from the handler (object or array)
ctx?unknownOptional request context (for RBAC, locale, etc.)
selectFields?string[]Optional top-level field names to keep in the data block. When provided, only these keys survive in the JSON payload sent to the AI. Nested objects are kept whole (shallow).

Returns

ResponseBuilder

A ResponseBuilder ready for chaining or .build()

Throws

If Zod validation fails

Example

typescript
// Full data (default)
return InvoicePresenter.make(rawInvoice).build();

// With _select filtering — only 'status' reaches the AI
return InvoicePresenter.make(rawInvoice, ctx, ['status']).build();

makeAsync()

ts
makeAsync(
   data, 
   ctx?, 
selectFields?): Promise<ResponseBuilder>;

Defined in: packages/core/src/presenter/Presenter.ts:935

Async version of make() — enriches the response with async callbacks.

Runs all sync steps first (via make()), then awaits async callbacks and appends their results to the builder. The sync make() method remains unchanged (zero breaking changes).

Parameters

ParameterTypeDescription
dataT | T[]Raw data from the handler (object or array)
ctx?unknownOptional request context
selectFields?string[]Optional top-level field names for context window optimization

Returns

Promise<ResponseBuilder>

A Promise resolving to a ResponseBuilder

Example

typescript
const presenter = createPresenter('Invoice')
    .schema(invoiceSchema)
    .asyncUiBlocks(async (inv, ctx) => {
        const history = await ctx.db.payments.history(inv.id);
        return [ui.echarts(buildTimeline(history))];
    });

// In handler:
const builder = await presenter.makeAsync(data, ctx);
return builder.build();

promptFirewall()

ts
promptFirewall(config): this;

Defined in: packages/core/src/presenter/Presenter.ts:831

Enable LLM-as-Judge evaluation of dynamic system rules.

The PromptFirewall evaluates all resolved rules (sync + async) through a judge chain before they reach the LLM. This prevents prompt injection via tainted data interpolated in .systemRules().

Important: Presenters with a firewall MUST use makeAsync(). Calling make() will throw an error.

Parameters

ParameterTypeDescription
configPromptFirewallConfigFirewall configuration (adapter or chain)

Returns

this

this for chaining

Example

typescript
createPresenter('Invoice')
    .systemRules((inv) => [`Status: ${inv.description}`])
    .promptFirewall({
        adapter: { name: 'gpt-4o-mini', evaluate: (p) => openai.chat(p) },
    });

redact()

ts
redact(paths, censor?): this;

Defined in: packages/core/src/presenter/Presenter.ts:665

Alias for .redactPII() — fluent shorthand.

Parameters

ParameterTypeDescription
pathsstring[]Object paths to redact
censor?string | (value) => stringReplacement value or function

Returns

this

this for chaining

Example

typescript
createPresenter('User')
    .schema({ name: t.string, ssn: t.string })
    .redact(['ssn'])

redactPII()

ts
redactPII(paths, censor?): this;

Defined in: packages/core/src/presenter/Presenter.ts:634

Declare PII fields to redact before data leaves the framework.

Uses fast-redact (Pino's V8-optimized serialization engine) to compile object paths into hyper-fast masking functions at config time — zero overhead on the hot path.

The redaction is applied structurally on the wire-facing data (after _select filter, before ResponseBuilder). UI blocks and system rules still see the full unmasked data (Late Guillotine pattern preserved).

Requires fast-redact as an optional peer dependency. If not installed, passes data through unmodified (defensive fallback).

Parameters

ParameterTypeDescription
pathsstring[]Object paths to redact. Supports dot notation, bracket notation, wildcards ('*'), and array indices.
censor?string | (value) => stringReplacement value. Default: '[REDACTED]'. Can be a function (originalValue) => maskedValue.

Returns

this

this for chaining

Example

typescript
// Basic PII masking
createPresenter('Patient')
    .schema({ name: t.string, ssn: t.string, diagnosis: t.string })
    .redactPII(['ssn', 'diagnosis'])

// Wildcard — redact all nested SSN fields
createPresenter('Users')
    .redactPII(['*.ssn', '*.password', 'credit_card.number'])

// Array wildcard — redact diagnosis in all patients
createPresenter('Hospital')
    .redactPII(['patients[*].diagnosis', 'patients[*].ssn'])

// Custom censor — last 4 digits visible
createPresenter('Payment')
    .redactPII(['credit_card.number'], (v) => '****-****-****-' + String(v).slice(-4))

See

fast-redact


rules()

ts
rules(rules): this;

Defined in: packages/core/src/presenter/Presenter.ts:488

Alias for .systemRules() — fluent shorthand.

Parameters

ParameterTypeDescription
rulesreadonly string[] | (data, ctx?) => (string | null)[]Static rules array or dynamic `(data, ctx?) => (string

Returns

this

this for chaining

Example

typescript
.rules(['CRITICAL: amounts in CENTS.'])
.rules((inv) => [
    inv.status === 'overdue' ? '⚠️ OVERDUE' : null,
])

schema()

Call Signature

ts
schema<TSchema>(zodSchema): Presenter<TSchema["_output"]>;

Defined in: packages/core/src/presenter/Presenter.ts:223

Set the Zod schema for data validation and filtering.

The schema acts as a security contract: only fields declared in the schema will reach the LLM. Sensitive data (passwords, tenant IDs, internal flags) is automatically stripped.

Type Parameters
Type ParameterDescription
TSchema extends ZodType<any, any, any>Zod type with output type inference
Parameters
ParameterTypeDescription
zodSchemaTSchemaA Zod schema (z.object, z.array, etc.)
Returns

Presenter<TSchema["_output"]>

A narrowed Presenter with the schema's output type

Example
typescript
createPresenter('Invoice')
    .schema(z.object({
        id: z.string(),
        amount_cents: z.number(),
    }))
// Presenter<{ id: string; amount_cents: number }>

Call Signature

ts
schema<TShape>(shape): Presenter<{ [k in string | number | symbol]: addQuestionMarks<baseObjectOutputType<TShape>, any>[k] }>;

Defined in: packages/core/src/presenter/Presenter.ts:248

Set the schema from an object of ZodTypes (enables t.* shorthand).

Accepts a plain object where each value is a ZodType. Internally wraps it into z.object(shape) for full validation.

Type Parameters
Type Parameter
TShape extends ZodRawShape
Parameters
ParameterTypeDescription
shapeTShapeObject shape with ZodType values (e.g. { id: t.string, name: t.string })
Returns

Presenter<{ [k in string | number | symbol]: addQuestionMarks<baseObjectOutputType<TShape>, any>[k] }>

A narrowed Presenter with the inferred output type

Example
typescript
import { createPresenter, t } from '@vurb/core';

createPresenter('Invoice')
    .schema({
        id:     t.string,
        amount: t.number.describe('in cents'),
        status: t.enum('draft', 'paid'),
    })

suggest()

ts
suggest(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:460

Alias for .suggestActions() — fluent shorthand.

Define HATEOAS-style action suggestions based on data state. Use with the suggest() helper for maximum fluency.

Parameters

ParameterTypeDescription
fnSuggestActionsFn<T>`(data, ctx?) => (ActionSuggestion

Returns

this

this for chaining

Example

typescript
import { suggest } from '@vurb/core';

.suggest((inv) => [
    suggest('invoices.get', 'View details'),
    inv.status === 'overdue'
        ? suggest('billing.remind', 'Send reminder')
        : null,
])

suggestActions()

ts
suggestActions(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:400

Define HATEOAS-style action suggestions based on data state.

The callback inspects the data and returns suggested next tools, guiding the AI through the business state machine. Eliminates routing hallucinations by providing explicit next-step hints.

Parameters

ParameterTypeDescription
fnSuggestActionsFn<T>(data, ctx?) => ActionSuggestion[]

Returns

this

this for chaining

Example

typescript
.suggestActions((invoice, ctx) => {
    if (invoice.status === 'pending') {
        return [
            { tool: 'billing.pay', reason: 'Offer immediate payment' },
            { tool: 'billing.send_reminder', reason: 'Send reminder email' },
        ];
    }
    return [];
})

systemRules()

ts
systemRules(rules): this;

Defined in: packages/core/src/presenter/Presenter.ts:298

Set JIT system rules that travel with the data.

Rules are Context Tree-Shaking: they only appear in the LLM's context when this specific domain's data is returned.

Accepts either a static array or a dynamic function that receives the data and request context for RBAC/DLP/locale rules. Return null from dynamic rules to conditionally exclude them.

Parameters

ParameterTypeDescription
rulesreadonly string[] | (data, ctx?) => (string | null)[]Array of rule strings, or a function `(data, ctx?) => (string

Returns

this

this for chaining

Example

typescript
// Static rules
.systemRules(['CRITICAL: amounts in CENTS.'])

// Dynamic context-aware rules (RBAC)
.systemRules((user, ctx) => [
    ctx?.user?.role !== 'admin' ? 'Mask email and phone.' : null,
    `Format dates using ${ctx?.tenant?.locale ?? 'en-US'}`
])

ui()

ts
ui(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:533

Alias for .uiBlocks() — fluent shorthand.

Parameters

ParameterTypeDescription
fnItemUiBlocksFn<T>`(item, ctx?) => (UiBlock

Returns

this

this for chaining

Example

typescript
.ui((inv) => [
    ui.echarts({ series: [{ type: 'gauge', data: [{ value: inv.amount / 100 }] }] }),
])

uiBlocks()

ts
uiBlocks(fn): this;

Defined in: packages/core/src/presenter/Presenter.ts:322

Define UI blocks generated for a single data item.

The callback receives the validated data item and optionally the request context. Return null for conditional blocks (filtered automatically).

Parameters

ParameterTypeDescription
fnItemUiBlocksFn<T>`(item, ctx?) => (UiBlock

Returns

this

this for chaining

Example

typescript
.uiBlocks((invoice, ctx) => [
    ui.echarts({ series: [...] }),
    ctx?.user?.department === 'finance' ? ui.echarts(advancedChart) : null,
])