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 Parameter | Description |
|---|---|
T | The validated output type (inferred from the Zod schema) |
Methods
agentLimit()
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
| Parameter | Type | Description |
|---|---|---|
max | number | Maximum items to keep in the data array |
onTruncate | (omittedCount) => UiBlock | Callback that generates a warning UI block. Receives the count of omitted items. |
Returns
this
this for chaining
Example
.agentLimit(50, (omitted) =>
ui.summary(`⚠️ Truncated. 50 shown, ${omitted} hidden. Use filters.`)
)asyncCollectionUiBlocks()
asyncCollectionUiBlocks(fn): this;Defined in: packages/core/src/presenter/Presenter.ts:782
Register an async UI block callback for collections.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn | AsyncCollectionUiBlocksFn<T> | Async function receiving the full validated array + optional context |
Returns
this
this for chaining
asyncRules()
asyncRules(fn): this;Defined in: packages/core/src/presenter/Presenter.ts:793
Register async system rules generation.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn | AsyncRulesFn<T> | Async function receiving validated data + optional context |
Returns
this
this for chaining
asyncSuggestActions()
asyncSuggestActions(fn): this;Defined in: packages/core/src/presenter/Presenter.ts:804
Register async action suggestions generation.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn | AsyncSuggestActionsFn<T> | Async function receiving validated data + optional context |
Returns
this
this for chaining
asyncUiBlocks()
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
| Parameter | Type | Description |
|---|---|---|
fn | AsyncItemUiBlocksFn<T> | Async function receiving validated data + optional context |
Returns
this
this for chaining
Example
createPresenter('Invoice')
.asyncUiBlocks(async (inv, ctx) => {
const history = await ctx.db.payments.history(inv.id);
return [ui.echarts(buildTimeline(history))];
});collectionRules()
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
| Parameter | Type | Description |
|---|---|---|
rules | CollectionRulesFn<T> | Static rules array, or dynamic `(items[], ctx?) => (string |
Returns
this
this for chaining
Example
.collectionRules((invoices, ctx) => [
`Total: ${invoices.length} invoices found.`,
invoices.some(i => i.status === 'overdue')
? '⚠️ Some invoices are OVERDUE. Highlight them.'
: null,
])collectionSuggest()
collectionSuggest(fn): this;Defined in: packages/core/src/presenter/Presenter.ts:470
Alias for .collectionSuggestActions() — fluent shorthand.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn | CollectionSuggestActionsFn<T> | `(items[], ctx?) => (ActionSuggestion |
Returns
this
this for chaining
collectionSuggestActions()
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
| Parameter | Type | Description |
|---|---|---|
fn | CollectionSuggestActionsFn<T> | `(items[], ctx?) => (ActionSuggestion |
Returns
this
this for chaining
Example
.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()
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
| Parameter | Type | Description |
|---|---|---|
fn | CollectionUiBlocksFn<T> | `(items[], ctx?) => (UiBlock |
Returns
this
this for chaining
Example
.collectionUiBlocks((invoices) => [
ui.echarts({ xAxis: { data: invoices.map(i => i.id) } }),
ui.summary(`${invoices.length} invoices found.`),
])embed()
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
| Parameter | Type | Description |
|---|---|---|
key | string | The property key containing the nested data |
childPresenter | Presenter<unknown> | The Presenter to apply to data[key] |
Returns
this
this for chaining
Example
import { ClientPresenter } from './ClientPresenter';
export const InvoicePresenter = createPresenter('Invoice')
.schema(invoiceSchema)
.embed('client', ClientPresenter);getSchemaKeys()
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()
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()
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()
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()
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()
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
| Parameter | Type | Description |
|---|---|---|
max | number | Maximum items to keep in the data array |
Returns
this
this for chaining
Example
// 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()
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
| Parameter | Type | Description |
|---|---|---|
data | T | T[] | Raw data from the handler (object or array) |
ctx? | unknown | Optional 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
A ResponseBuilder ready for chaining or .build()
Throws
If Zod validation fails
Example
// Full data (default)
return InvoicePresenter.make(rawInvoice).build();
// With _select filtering — only 'status' reaches the AI
return InvoicePresenter.make(rawInvoice, ctx, ['status']).build();makeAsync()
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
| Parameter | Type | Description |
|---|---|---|
data | T | T[] | Raw data from the handler (object or array) |
ctx? | unknown | Optional request context |
selectFields? | string[] | Optional top-level field names for context window optimization |
Returns
Promise<ResponseBuilder>
A Promise resolving to a ResponseBuilder
Example
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()
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
| Parameter | Type | Description |
|---|---|---|
config | PromptFirewallConfig | Firewall configuration (adapter or chain) |
Returns
this
this for chaining
Example
createPresenter('Invoice')
.systemRules((inv) => [`Status: ${inv.description}`])
.promptFirewall({
adapter: { name: 'gpt-4o-mini', evaluate: (p) => openai.chat(p) },
});redact()
redact(paths, censor?): this;Defined in: packages/core/src/presenter/Presenter.ts:665
Alias for .redactPII() — fluent shorthand.
Parameters
| Parameter | Type | Description |
|---|---|---|
paths | string[] | Object paths to redact |
censor? | string | (value) => string | Replacement value or function |
Returns
this
this for chaining
Example
createPresenter('User')
.schema({ name: t.string, ssn: t.string })
.redact(['ssn'])redactPII()
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
| Parameter | Type | Description |
|---|---|---|
paths | string[] | Object paths to redact. Supports dot notation, bracket notation, wildcards ('*'), and array indices. |
censor? | string | (value) => string | Replacement value. Default: '[REDACTED]'. Can be a function (originalValue) => maskedValue. |
Returns
this
this for chaining
Example
// 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
rules()
rules(rules): this;Defined in: packages/core/src/presenter/Presenter.ts:488
Alias for .systemRules() — fluent shorthand.
Parameters
| Parameter | Type | Description |
|---|---|---|
rules | readonly string[] | (data, ctx?) => (string | null)[] | Static rules array or dynamic `(data, ctx?) => (string |
Returns
this
this for chaining
Example
.rules(['CRITICAL: amounts in CENTS.'])
.rules((inv) => [
inv.status === 'overdue' ? '⚠️ OVERDUE' : null,
])schema()
Call Signature
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 Parameter | Description |
|---|---|
TSchema extends ZodType<any, any, any> | Zod type with output type inference |
Parameters
| Parameter | Type | Description |
|---|---|---|
zodSchema | TSchema | A Zod schema (z.object, z.array, etc.) |
Returns
Presenter<TSchema["_output"]>
A narrowed Presenter with the schema's output type
Example
createPresenter('Invoice')
.schema(z.object({
id: z.string(),
amount_cents: z.number(),
}))
// Presenter<{ id: string; amount_cents: number }>Call Signature
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
| Parameter | Type | Description |
|---|---|---|
shape | TShape | Object 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
import { createPresenter, t } from '@vurb/core';
createPresenter('Invoice')
.schema({
id: t.string,
amount: t.number.describe('in cents'),
status: t.enum('draft', 'paid'),
})suggest()
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
| Parameter | Type | Description |
|---|---|---|
fn | SuggestActionsFn<T> | `(data, ctx?) => (ActionSuggestion |
Returns
this
this for chaining
Example
import { suggest } from '@vurb/core';
.suggest((inv) => [
suggest('invoices.get', 'View details'),
inv.status === 'overdue'
? suggest('billing.remind', 'Send reminder')
: null,
])suggestActions()
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
| Parameter | Type | Description |
|---|---|---|
fn | SuggestActionsFn<T> | (data, ctx?) => ActionSuggestion[] |
Returns
this
this for chaining
Example
.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()
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
| Parameter | Type | Description |
|---|---|---|
rules | readonly string[] | (data, ctx?) => (string | null)[] | Array of rule strings, or a function `(data, ctx?) => (string |
Returns
this
this for chaining
Example
// 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()
ui(fn): this;Defined in: packages/core/src/presenter/Presenter.ts:533
Alias for .uiBlocks() — fluent shorthand.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn | ItemUiBlocksFn<T> | `(item, ctx?) => (UiBlock |
Returns
this
this for chaining
Example
.ui((inv) => [
ui.echarts({ series: [{ type: 'gauge', data: [{ value: inv.amount / 100 }] }] }),
])uiBlocks()
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
| Parameter | Type | Description |
|---|---|---|
fn | ItemUiBlocksFn<T> | `(item, ctx?) => (UiBlock |
Returns
this
this for chaining
Example
.uiBlocks((invoice, ctx) => [
ui.echarts({ series: [...] }),
ctx?.user?.department === 'finance' ? ui.echarts(advancedChart) : null,
])