@vurb/core 3.2.3 → 3.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +677 -677
- package/dist/cli/constants.js +59 -59
- package/dist/cli/templates/config.js +26 -26
- package/dist/cli/templates/constants.d.ts +1 -1
- package/dist/cli/templates/constants.d.ts.map +1 -1
- package/dist/cli/templates/constants.js +1 -1
- package/dist/cli/templates/constants.js.map +1 -1
- package/dist/cli/templates/core.d.ts.map +1 -1
- package/dist/cli/templates/core.js +96 -169
- package/dist/cli/templates/core.js.map +1 -1
- package/dist/cli/templates/middleware.js +25 -25
- package/dist/cli/templates/readme.js +142 -142
- package/dist/cli/templates/testing.js +84 -84
- package/dist/cli/templates/tools.js +46 -46
- package/dist/cli/templates/vectors/database.js +69 -69
- package/dist/cli/templates/vectors/oauth.js +63 -63
- package/dist/cli/templates/vectors/openapi.js +97 -97
- package/dist/core/middleware/AuditTrail.d.ts +128 -0
- package/dist/core/middleware/AuditTrail.d.ts.map +1 -0
- package/dist/core/middleware/AuditTrail.js +94 -0
- package/dist/core/middleware/AuditTrail.js.map +1 -0
- package/dist/core/middleware/InputFirewall.d.ts +95 -0
- package/dist/core/middleware/InputFirewall.d.ts.map +1 -0
- package/dist/core/middleware/InputFirewall.js +104 -0
- package/dist/core/middleware/InputFirewall.js.map +1 -0
- package/dist/core/middleware/RateLimiter.d.ts +151 -0
- package/dist/core/middleware/RateLimiter.d.ts.map +1 -0
- package/dist/core/middleware/RateLimiter.js +121 -0
- package/dist/core/middleware/RateLimiter.js.map +1 -0
- package/dist/core/middleware/index.d.ts +6 -0
- package/dist/core/middleware/index.d.ts.map +1 -1
- package/dist/core/middleware/index.js +4 -0
- package/dist/core/middleware/index.js.map +1 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/introspection/SemanticProbe.js +49 -49
- package/dist/observability/TelemetryEvent.d.ts +61 -1
- package/dist/observability/TelemetryEvent.d.ts.map +1 -1
- package/dist/presenter/JudgeChain.d.ts +129 -0
- package/dist/presenter/JudgeChain.d.ts.map +1 -0
- package/dist/presenter/JudgeChain.js +215 -0
- package/dist/presenter/JudgeChain.js.map +1 -0
- package/dist/presenter/PostProcessor.d.ts.map +1 -1
- package/dist/presenter/PostProcessor.js +11 -66
- package/dist/presenter/PostProcessor.js.map +1 -1
- package/dist/presenter/Presenter.d.ts +175 -37
- package/dist/presenter/Presenter.d.ts.map +1 -1
- package/dist/presenter/Presenter.js +265 -154
- package/dist/presenter/Presenter.js.map +1 -1
- package/dist/presenter/PresenterPipeline.d.ts +147 -0
- package/dist/presenter/PresenterPipeline.d.ts.map +1 -0
- package/dist/presenter/PresenterPipeline.js +271 -0
- package/dist/presenter/PresenterPipeline.js.map +1 -0
- package/dist/presenter/PromptFirewall.d.ts +160 -0
- package/dist/presenter/PromptFirewall.d.ts.map +1 -0
- package/dist/presenter/PromptFirewall.js +228 -0
- package/dist/presenter/PromptFirewall.js.map +1 -0
- package/dist/presenter/ResponseBuilder.d.ts +13 -0
- package/dist/presenter/ResponseBuilder.d.ts.map +1 -1
- package/dist/presenter/ResponseBuilder.js +28 -1
- package/dist/presenter/ResponseBuilder.js.map +1 -1
- package/dist/presenter/TelemetryCollector.d.ts +48 -0
- package/dist/presenter/TelemetryCollector.d.ts.map +1 -0
- package/dist/presenter/TelemetryCollector.js +93 -0
- package/dist/presenter/TelemetryCollector.js.map +1 -0
- package/dist/presenter/definePresenter.d.ts +112 -0
- package/dist/presenter/definePresenter.d.ts.map +1 -1
- package/dist/presenter/definePresenter.js +110 -0
- package/dist/presenter/definePresenter.js.map +1 -1
- package/dist/presenter/index.d.ts +6 -2
- package/dist/presenter/index.d.ts.map +1 -1
- package/dist/presenter/index.js +5 -1
- package/dist/presenter/index.js.map +1 -1
- package/dist/presenter/ui.d.ts +31 -8
- package/dist/presenter/ui.d.ts.map +1 -1
- package/dist/presenter/ui.js +16 -16
- package/dist/presenter/ui.js.map +1 -1
- package/dist/prompt/FluentPromptBuilder.d.ts.map +1 -1
- package/dist/resource/ResourceBuilder.d.ts +129 -0
- package/dist/resource/ResourceBuilder.d.ts.map +1 -0
- package/dist/resource/ResourceBuilder.js +93 -0
- package/dist/resource/ResourceBuilder.js.map +1 -0
- package/dist/resource/ResourceRegistry.d.ts +147 -0
- package/dist/resource/ResourceRegistry.d.ts.map +1 -0
- package/dist/resource/ResourceRegistry.js +234 -0
- package/dist/resource/ResourceRegistry.js.map +1 -0
- package/dist/resource/SubscriptionManager.d.ts +67 -0
- package/dist/resource/SubscriptionManager.d.ts.map +1 -0
- package/dist/resource/SubscriptionManager.js +86 -0
- package/dist/resource/SubscriptionManager.js.map +1 -0
- package/dist/resource/index.d.ts +13 -0
- package/dist/resource/index.d.ts.map +1 -0
- package/dist/resource/index.js +13 -0
- package/dist/resource/index.js.map +1 -0
- package/dist/server/ServerAttachment.d.ts +26 -0
- package/dist/server/ServerAttachment.d.ts.map +1 -1
- package/dist/server/ServerAttachment.js +70 -2
- package/dist/server/ServerAttachment.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/startServer.d.ts +22 -1
- package/dist/server/startServer.d.ts.map +1 -1
- package/dist/server/startServer.js +98 -5
- package/dist/server/startServer.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PresenterPipeline — Decomposed Make Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `Presenter.make()` to uphold SRP and enable isolated testing.
|
|
5
|
+
* Each step is a pure function that receives a `PresenterSnapshot` (read-only
|
|
6
|
+
* config) and produces side-effects on a `ResponseBuilder`.
|
|
7
|
+
*
|
|
8
|
+
* The `executePipeline()` orchestrator replaces the inline logic previously
|
|
9
|
+
* in `Presenter.make()`, maintaining the exact same behavior and output.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { ZodType } from 'zod';
|
|
15
|
+
import { ResponseBuilder } from './ResponseBuilder.js';
|
|
16
|
+
import { type UiBlock } from './ui.js';
|
|
17
|
+
import { type ActionSuggestion } from './Presenter.js';
|
|
18
|
+
import { type RedactConfig, type RedactFn } from './RedactEngine.js';
|
|
19
|
+
import { type StringifyFn } from '../core/serialization/JsonSerializer.js';
|
|
20
|
+
import { type Presenter } from './Presenter.js';
|
|
21
|
+
/** Static rules (string array) OR dynamic rules with context (function) */
|
|
22
|
+
export type RulesConfig<T> = readonly string[] | ((data: T, ctx?: unknown) => (string | null)[]);
|
|
23
|
+
/** Collection-level rules — static or dynamic */
|
|
24
|
+
export type CollectionRulesFn<T> = readonly string[] | ((items: T[], ctx?: unknown) => (string | null)[]);
|
|
25
|
+
/** UI blocks callback — with optional context */
|
|
26
|
+
export type ItemUiBlocksFn<T> = (item: T, ctx?: unknown) => (UiBlock | null)[];
|
|
27
|
+
/** Collection UI blocks callback — with optional context */
|
|
28
|
+
export type CollectionUiBlocksFn<T> = (items: T[], ctx?: unknown) => (UiBlock | null)[];
|
|
29
|
+
/** Suggest actions callback — with optional context */
|
|
30
|
+
export type SuggestActionsFn<T> = (data: T, ctx?: unknown) => ActionSuggestion[];
|
|
31
|
+
/** Collection-level suggest actions callback */
|
|
32
|
+
export type CollectionSuggestActionsFn<T> = (items: T[], ctx?: unknown) => (ActionSuggestion | null)[];
|
|
33
|
+
/** Agent limit configuration */
|
|
34
|
+
export interface AgentLimitConfig {
|
|
35
|
+
readonly max: number;
|
|
36
|
+
readonly onTruncate: (omittedCount: number) => UiBlock;
|
|
37
|
+
}
|
|
38
|
+
/** An embedded child Presenter for relational composition */
|
|
39
|
+
export interface EmbedEntry {
|
|
40
|
+
readonly key: string;
|
|
41
|
+
readonly presenter: Presenter<unknown>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read-only snapshot of Presenter configuration for the pipeline.
|
|
45
|
+
*
|
|
46
|
+
* Created by `Presenter._toSnapshot()` on each `make()` call.
|
|
47
|
+
* Avoids exposing private state while enabling standalone step functions.
|
|
48
|
+
*
|
|
49
|
+
* @typeParam T - The validated output type
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export interface PresenterSnapshot<T> {
|
|
53
|
+
readonly name: string;
|
|
54
|
+
readonly schema?: ZodType<any, any, any> | undefined;
|
|
55
|
+
readonly rules: RulesConfig<T>;
|
|
56
|
+
readonly collectionRules: CollectionRulesFn<T>;
|
|
57
|
+
readonly itemUiBlocks?: ItemUiBlocksFn<T> | undefined;
|
|
58
|
+
readonly collectionUiBlocks?: CollectionUiBlocksFn<T> | undefined;
|
|
59
|
+
readonly suggestActions?: SuggestActionsFn<T> | undefined;
|
|
60
|
+
readonly collectionSuggestActions?: CollectionSuggestActionsFn<T> | undefined;
|
|
61
|
+
readonly agentLimit?: AgentLimitConfig | undefined;
|
|
62
|
+
readonly embeds: readonly EmbedEntry[];
|
|
63
|
+
readonly redactConfig?: RedactConfig | undefined;
|
|
64
|
+
readonly compiledStringify?: StringifyFn | undefined;
|
|
65
|
+
/** Mutable: may be lazily compiled */
|
|
66
|
+
compiledRedactor?: RedactFn | undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Step 0: Cognitive Guardrails — truncate array by agentLimit.
|
|
70
|
+
*
|
|
71
|
+
* If the data is an array larger than the configured limit, it is sliced
|
|
72
|
+
* and a truncation UiBlock is returned for insertion.
|
|
73
|
+
*
|
|
74
|
+
* @returns Potentially truncated data + optional truncation UiBlock
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
export declare function stepTruncate<T>(data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>): {
|
|
78
|
+
data: T | T[];
|
|
79
|
+
truncationBlock?: UiBlock | undefined;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Step 1: Validate data through the Zod schema (if configured).
|
|
83
|
+
* For arrays, each item is validated independently.
|
|
84
|
+
*
|
|
85
|
+
* @throws {PresenterValidationError} on schema mismatch
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
export declare function stepValidate<T>(data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>): T | T[];
|
|
89
|
+
/**
|
|
90
|
+
* Step 2: Apply PII redaction to wire-facing data.
|
|
91
|
+
* Creates a deep clone to preserve original data for UI/rules.
|
|
92
|
+
*
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
export declare function stepRedact<T>(data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>): T | T[];
|
|
96
|
+
/**
|
|
97
|
+
* Step 3: Generate and attach UI blocks to the response builder.
|
|
98
|
+
* Auto-detects single vs collection. Filters `null` blocks.
|
|
99
|
+
*
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
export declare function stepUiBlocks<T>(builder: ResponseBuilder, data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>, ctx?: unknown): void;
|
|
103
|
+
/**
|
|
104
|
+
* Step 4: Resolve and attach domain rules to the response builder.
|
|
105
|
+
* Supports both static arrays and dynamic context-aware functions.
|
|
106
|
+
*
|
|
107
|
+
* For collections: also evaluates `collectionRules` (if defined) and
|
|
108
|
+
* merges them after per-item rules.
|
|
109
|
+
*
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
export declare function stepRules<T>(builder: ResponseBuilder, data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>, ctx?: unknown): void;
|
|
113
|
+
/**
|
|
114
|
+
* Step 5: Evaluate and attach action suggestions to the response.
|
|
115
|
+
*
|
|
116
|
+
* For collections: uses `collectionSuggestActions` if defined,
|
|
117
|
+
* otherwise falls back to per-item evaluation on the first item.
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
export declare function stepSuggestions<T>(builder: ResponseBuilder, data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>, ctx?: unknown): void;
|
|
122
|
+
/**
|
|
123
|
+
* Step 6: Process embedded child Presenters for nested relational data.
|
|
124
|
+
* Merges child UI blocks and rules into the parent builder.
|
|
125
|
+
*
|
|
126
|
+
* For collections: iterates all array items. Rules are deduplicated
|
|
127
|
+
* via a Set to avoid repetition. UI blocks are emitted only for the
|
|
128
|
+
* first item to prevent context window explosion.
|
|
129
|
+
*
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
export declare function stepEmbeds<T>(builder: ResponseBuilder, data: T | T[], isArray: boolean, snapshot: PresenterSnapshot<T>, ctx?: unknown): void;
|
|
133
|
+
/**
|
|
134
|
+
* Execute the full Presenter pipeline.
|
|
135
|
+
*
|
|
136
|
+
* Orchestrates: truncate → validate → embed → render UI → attach rules
|
|
137
|
+
* → suggest actions → **Late Guillotine** (`_select` filter).
|
|
138
|
+
*
|
|
139
|
+
* @param data - Raw data from handler
|
|
140
|
+
* @param snapshot - Read-only configuration snapshot
|
|
141
|
+
* @param ctx - Optional request context
|
|
142
|
+
* @param selectFields - Optional field names for context window optimization
|
|
143
|
+
* @returns A fully-composed ResponseBuilder
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
export declare function executePipeline<T>(data: T | T[], snapshot: PresenterSnapshot<T>, ctx?: unknown, selectFields?: string[]): ResponseBuilder;
|
|
147
|
+
//# sourceMappingURL=PresenterPipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenterPipeline.d.ts","sourceRoot":"","sources":["../../src/presenter/PresenterPipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PresenterPipeline — Decomposed Make Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `Presenter.make()` to uphold SRP and enable isolated testing.
|
|
5
|
+
* Each step is a pure function that receives a `PresenterSnapshot` (read-only
|
|
6
|
+
* config) and produces side-effects on a `ResponseBuilder`.
|
|
7
|
+
*
|
|
8
|
+
* The `executePipeline()` orchestrator replaces the inline logic previously
|
|
9
|
+
* in `Presenter.make()`, maintaining the exact same behavior and output.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { ZodType, ZodError } from 'zod';
|
|
15
|
+
import { ResponseBuilder } from './ResponseBuilder.js';
|
|
16
|
+
import {} from './ui.js';
|
|
17
|
+
import {} from './Presenter.js';
|
|
18
|
+
import { PresenterValidationError } from './PresenterValidationError.js';
|
|
19
|
+
import { applySelectFilter } from './SelectUtils.js';
|
|
20
|
+
import { compileRedactor } from './RedactEngine.js';
|
|
21
|
+
import {} from '../core/serialization/JsonSerializer.js';
|
|
22
|
+
import {} from './Presenter.js';
|
|
23
|
+
// ── Step Functions ──────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* Step 0: Cognitive Guardrails — truncate array by agentLimit.
|
|
26
|
+
*
|
|
27
|
+
* If the data is an array larger than the configured limit, it is sliced
|
|
28
|
+
* and a truncation UiBlock is returned for insertion.
|
|
29
|
+
*
|
|
30
|
+
* @returns Potentially truncated data + optional truncation UiBlock
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export function stepTruncate(data, isArray, snapshot) {
|
|
34
|
+
if (isArray && snapshot.agentLimit && data.length > snapshot.agentLimit.max) {
|
|
35
|
+
const fullLength = data.length;
|
|
36
|
+
const omitted = fullLength - snapshot.agentLimit.max;
|
|
37
|
+
return {
|
|
38
|
+
data: data.slice(0, snapshot.agentLimit.max),
|
|
39
|
+
truncationBlock: snapshot.agentLimit.onTruncate(omitted),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { data };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Step 1: Validate data through the Zod schema (if configured).
|
|
46
|
+
* For arrays, each item is validated independently.
|
|
47
|
+
*
|
|
48
|
+
* @throws {PresenterValidationError} on schema mismatch
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export function stepValidate(data, isArray, snapshot) {
|
|
52
|
+
if (!snapshot.schema)
|
|
53
|
+
return data;
|
|
54
|
+
try {
|
|
55
|
+
if (isArray) {
|
|
56
|
+
return data.map(item => snapshot.schema.parse(item));
|
|
57
|
+
}
|
|
58
|
+
return snapshot.schema.parse(data);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof ZodError) {
|
|
62
|
+
throw new PresenterValidationError(snapshot.name, err);
|
|
63
|
+
}
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Step 2: Apply PII redaction to wire-facing data.
|
|
69
|
+
* Creates a deep clone to preserve original data for UI/rules.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
export function stepRedact(data, isArray, snapshot) {
|
|
74
|
+
// Lazy recompilation: if redactPII was called before fast-redact loaded
|
|
75
|
+
if (!snapshot.compiledRedactor && snapshot.redactConfig) {
|
|
76
|
+
snapshot.compiledRedactor = compileRedactor(snapshot.redactConfig);
|
|
77
|
+
if (!snapshot.compiledRedactor) {
|
|
78
|
+
console.warn(`[vurb] Presenter "${snapshot.name}": PII redaction configured but fast-redact is not available. ` +
|
|
79
|
+
`Data will pass through WITHOUT redaction. Ensure initVurb() completes before .make() is called, ` +
|
|
80
|
+
`or install fast-redact as a dependency.`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!snapshot.compiledRedactor)
|
|
84
|
+
return data;
|
|
85
|
+
if (isArray) {
|
|
86
|
+
return data.map(item => snapshot.compiledRedactor(item));
|
|
87
|
+
}
|
|
88
|
+
return snapshot.compiledRedactor(data);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Step 3: Generate and attach UI blocks to the response builder.
|
|
92
|
+
* Auto-detects single vs collection. Filters `null` blocks.
|
|
93
|
+
*
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
export function stepUiBlocks(builder, data, isArray, snapshot, ctx) {
|
|
97
|
+
if (isArray && snapshot.collectionUiBlocks) {
|
|
98
|
+
const blocks = snapshot.collectionUiBlocks(data, ctx).filter(Boolean);
|
|
99
|
+
if (blocks.length > 0)
|
|
100
|
+
builder.uiBlocks(blocks);
|
|
101
|
+
}
|
|
102
|
+
else if (!isArray && snapshot.itemUiBlocks) {
|
|
103
|
+
const blocks = snapshot.itemUiBlocks(data, ctx).filter(Boolean);
|
|
104
|
+
if (blocks.length > 0)
|
|
105
|
+
builder.uiBlocks(blocks);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Step 4: Resolve and attach domain rules to the response builder.
|
|
110
|
+
* Supports both static arrays and dynamic context-aware functions.
|
|
111
|
+
*
|
|
112
|
+
* For collections: also evaluates `collectionRules` (if defined) and
|
|
113
|
+
* merges them after per-item rules.
|
|
114
|
+
*
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export function stepRules(builder, data, isArray, snapshot, ctx) {
|
|
118
|
+
// Per-item rules
|
|
119
|
+
if (typeof snapshot.rules === 'function') {
|
|
120
|
+
const singleData = isArray ? data[0] : data;
|
|
121
|
+
if (singleData !== undefined) {
|
|
122
|
+
const resolved = snapshot.rules(singleData, ctx)
|
|
123
|
+
.filter((r) => r !== null);
|
|
124
|
+
if (resolved.length > 0)
|
|
125
|
+
builder.systemRules(resolved);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (snapshot.rules.length > 0) {
|
|
129
|
+
builder.systemRules(snapshot.rules);
|
|
130
|
+
}
|
|
131
|
+
// Collection-level rules (additive — appended after per-item rules)
|
|
132
|
+
const hasCollectionRules = typeof snapshot.collectionRules === 'function'
|
|
133
|
+
|| snapshot.collectionRules.length > 0;
|
|
134
|
+
if (isArray && hasCollectionRules) {
|
|
135
|
+
const items = data;
|
|
136
|
+
if (typeof snapshot.collectionRules === 'function') {
|
|
137
|
+
const resolved = snapshot.collectionRules(items, ctx)
|
|
138
|
+
.filter((r) => r !== null);
|
|
139
|
+
if (resolved.length > 0)
|
|
140
|
+
builder.systemRules(resolved);
|
|
141
|
+
}
|
|
142
|
+
else if (snapshot.collectionRules.length > 0) {
|
|
143
|
+
builder.systemRules(snapshot.collectionRules);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Step 5: Evaluate and attach action suggestions to the response.
|
|
149
|
+
*
|
|
150
|
+
* For collections: uses `collectionSuggestActions` if defined,
|
|
151
|
+
* otherwise falls back to per-item evaluation on the first item.
|
|
152
|
+
*
|
|
153
|
+
* @internal
|
|
154
|
+
*/
|
|
155
|
+
export function stepSuggestions(builder, data, isArray, snapshot, ctx) {
|
|
156
|
+
// Collection-level suggestions (preferred for arrays)
|
|
157
|
+
if (isArray && snapshot.collectionSuggestActions) {
|
|
158
|
+
const items = data;
|
|
159
|
+
if (items.length === 0)
|
|
160
|
+
return;
|
|
161
|
+
const suggestions = snapshot.collectionSuggestActions(items, ctx)
|
|
162
|
+
.filter((s) => s !== null);
|
|
163
|
+
if (suggestions.length > 0) {
|
|
164
|
+
builder.systemHint(suggestions);
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// Per-item fallback (single item or first item of array)
|
|
169
|
+
if (!snapshot.suggestActions)
|
|
170
|
+
return;
|
|
171
|
+
const singleData = isArray ? data[0] : data;
|
|
172
|
+
if (singleData === undefined)
|
|
173
|
+
return;
|
|
174
|
+
const suggestions = snapshot.suggestActions(singleData, ctx);
|
|
175
|
+
if (suggestions.length > 0) {
|
|
176
|
+
builder.systemHint(suggestions);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Step 6: Process embedded child Presenters for nested relational data.
|
|
181
|
+
* Merges child UI blocks and rules into the parent builder.
|
|
182
|
+
*
|
|
183
|
+
* For collections: iterates all array items. Rules are deduplicated
|
|
184
|
+
* via a Set to avoid repetition. UI blocks are emitted only for the
|
|
185
|
+
* first item to prevent context window explosion.
|
|
186
|
+
*
|
|
187
|
+
* @internal
|
|
188
|
+
*/
|
|
189
|
+
export function stepEmbeds(builder, data, isArray, snapshot, ctx) {
|
|
190
|
+
if (snapshot.embeds.length === 0)
|
|
191
|
+
return;
|
|
192
|
+
const items = isArray
|
|
193
|
+
? data.filter((item) => item !== undefined && typeof item === 'object')
|
|
194
|
+
: [data].filter((item) => item !== undefined && typeof item === 'object');
|
|
195
|
+
if (items.length === 0)
|
|
196
|
+
return;
|
|
197
|
+
// Track which rule blocks we've already emitted (dedup for arrays)
|
|
198
|
+
const emittedRules = new Set();
|
|
199
|
+
for (let itemIdx = 0; itemIdx < items.length; itemIdx++) {
|
|
200
|
+
const item = items[itemIdx];
|
|
201
|
+
for (const embed of snapshot.embeds) {
|
|
202
|
+
const nestedData = item[embed.key];
|
|
203
|
+
if (nestedData === undefined || nestedData === null)
|
|
204
|
+
continue;
|
|
205
|
+
const childBuilder = embed.presenter.make(nestedData, ctx);
|
|
206
|
+
const childResponse = childBuilder.build();
|
|
207
|
+
// Skip the first block (data) — parent already has it
|
|
208
|
+
for (let i = 1; i < childResponse.content.length; i++) {
|
|
209
|
+
const blockText = childResponse.content[i].text;
|
|
210
|
+
// For arrays: deduplicate rule/hint blocks, emit UI only for first item
|
|
211
|
+
if (isArray && itemIdx > 0) {
|
|
212
|
+
if (emittedRules.has(blockText))
|
|
213
|
+
continue;
|
|
214
|
+
if (blockText.includes('<ui_passthrough'))
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
emittedRules.add(blockText);
|
|
218
|
+
builder.rawBlock(blockText);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ── Pipeline Orchestrator ───────────────────────────────
|
|
224
|
+
/**
|
|
225
|
+
* Execute the full Presenter pipeline.
|
|
226
|
+
*
|
|
227
|
+
* Orchestrates: truncate → validate → embed → render UI → attach rules
|
|
228
|
+
* → suggest actions → **Late Guillotine** (`_select` filter).
|
|
229
|
+
*
|
|
230
|
+
* @param data - Raw data from handler
|
|
231
|
+
* @param snapshot - Read-only configuration snapshot
|
|
232
|
+
* @param ctx - Optional request context
|
|
233
|
+
* @param selectFields - Optional field names for context window optimization
|
|
234
|
+
* @returns A fully-composed ResponseBuilder
|
|
235
|
+
* @internal
|
|
236
|
+
*/
|
|
237
|
+
export function executePipeline(data, snapshot, ctx, selectFields) {
|
|
238
|
+
const isArray = Array.isArray(data);
|
|
239
|
+
// Step 0: Cognitive Guardrails — truncate if needed
|
|
240
|
+
const truncated = stepTruncate(data, isArray, snapshot);
|
|
241
|
+
data = truncated.data;
|
|
242
|
+
// Step 1: Process embedded child Presenters (on RAW data, before validation)
|
|
243
|
+
const rawForEmbeds = data;
|
|
244
|
+
// Step 2: Validate — produces the FULL validated data
|
|
245
|
+
const validated = stepValidate(data, isArray, snapshot);
|
|
246
|
+
// ── Late Guillotine ──────────────────────────────────
|
|
247
|
+
// Steps 3-6 use the FULL validated data so that UI blocks,
|
|
248
|
+
// system rules, and action suggestions never see undefined
|
|
249
|
+
// for pruned fields.
|
|
250
|
+
// Step 3: Determine wire-facing data (filtered or full)
|
|
251
|
+
const wireData = (selectFields && selectFields.length > 0)
|
|
252
|
+
? applySelectFilter(validated, selectFields, isArray)
|
|
253
|
+
: validated;
|
|
254
|
+
// Step 3.1: DLP Redaction — mask PII on the wire-facing data
|
|
255
|
+
const safeWireData = stepRedact(wireData, isArray, snapshot);
|
|
256
|
+
const builder = new ResponseBuilder(safeWireData, snapshot.compiledStringify);
|
|
257
|
+
// Step 3.5: Truncation warning (first UI block, before all others)
|
|
258
|
+
if (truncated.truncationBlock) {
|
|
259
|
+
builder.uiBlock(truncated.truncationBlock);
|
|
260
|
+
}
|
|
261
|
+
// Step 4: Merge embedded child Presenter blocks (using FULL data)
|
|
262
|
+
stepEmbeds(builder, rawForEmbeds, isArray, snapshot, ctx);
|
|
263
|
+
// Step 5: Attach UI blocks (using FULL validated data)
|
|
264
|
+
stepUiBlocks(builder, validated, isArray, snapshot, ctx);
|
|
265
|
+
// Step 6: Attach rules (using FULL validated data)
|
|
266
|
+
stepRules(builder, validated, isArray, snapshot, ctx);
|
|
267
|
+
// Step 7: Attach action suggestions (using FULL validated data)
|
|
268
|
+
stepSuggestions(builder, validated, isArray, snapshot, ctx);
|
|
269
|
+
return builder;
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=PresenterPipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenterPipeline.js","sourceRoot":"","sources":["../../src/presenter/PresenterPipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptFirewall — LLM-as-Judge Output Protection
|
|
3
|
+
*
|
|
4
|
+
* Evaluates dynamically-generated system rules via a {@link JudgeChain}
|
|
5
|
+
* before they reach the LLM as `<domain_rules>`. This prevents prompt
|
|
6
|
+
* injection attacks where tainted data (e.g., user input stored in a
|
|
7
|
+
* database) is interpolated into system rules.
|
|
8
|
+
*
|
|
9
|
+
* The firewall operates inside `Presenter.makeAsync()`, AFTER all sync
|
|
10
|
+
* and async rules have been resolved. This means:
|
|
11
|
+
* - `executePipeline()` is NOT modified (zero async ripple)
|
|
12
|
+
* - `make()` throws if a firewall is configured (forces async path)
|
|
13
|
+
*
|
|
14
|
+
* **Design**: The framework provides the judge prompt via
|
|
15
|
+
* `buildFirewallPrompt()`. The developer only brings the LLM adapter(s).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { createPresenter } from '@vurb/core';
|
|
20
|
+
*
|
|
21
|
+
* const InvoicePresenter = createPresenter('Invoice')
|
|
22
|
+
* .schema(invoiceSchema)
|
|
23
|
+
* .systemRules((inv) => [`Status: ${inv.description}`])
|
|
24
|
+
* .promptFirewall({
|
|
25
|
+
* adapter: { name: 'gpt-4o-mini', evaluate: (p) => openai.chat(p) },
|
|
26
|
+
* timeoutMs: 3000,
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // MUST use makeAsync():
|
|
30
|
+
* return (await InvoicePresenter.makeAsync(data, ctx)).build();
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
*/
|
|
35
|
+
import type { SemanticProbeAdapter } from '../introspection/SemanticProbe.js';
|
|
36
|
+
import type { TelemetrySink } from '../observability/TelemetryEvent.js';
|
|
37
|
+
import { type JudgeChain, type JudgeChainResult } from './JudgeChain.js';
|
|
38
|
+
/**
|
|
39
|
+
* Configuration for the PromptFirewall.
|
|
40
|
+
*
|
|
41
|
+
* Accepts either a single adapter or a pre-built JudgeChain for
|
|
42
|
+
* multi-adapter evaluation (fallback/consensus).
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Single adapter:
|
|
47
|
+
* .promptFirewall({ adapter: gptMini })
|
|
48
|
+
*
|
|
49
|
+
* // Multi-adapter (fallback):
|
|
50
|
+
* .promptFirewall({
|
|
51
|
+
* chain: createJudgeChain({
|
|
52
|
+
* adapters: [gptMini, claudeHaiku],
|
|
53
|
+
* strategy: 'fallback',
|
|
54
|
+
* }),
|
|
55
|
+
* })
|
|
56
|
+
*
|
|
57
|
+
* // Multi-adapter (consensus — both must agree):
|
|
58
|
+
* .promptFirewall({
|
|
59
|
+
* chain: createJudgeChain({
|
|
60
|
+
* adapters: [gptMini, claudeHaiku],
|
|
61
|
+
* strategy: 'consensus',
|
|
62
|
+
* }),
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export interface PromptFirewallConfig {
|
|
67
|
+
/**
|
|
68
|
+
* Single LLM adapter for evaluation.
|
|
69
|
+
* Mutually exclusive with `chain`. If both provided, `chain` wins.
|
|
70
|
+
*/
|
|
71
|
+
readonly adapter?: SemanticProbeAdapter;
|
|
72
|
+
/**
|
|
73
|
+
* Pre-built JudgeChain for multi-adapter evaluation.
|
|
74
|
+
* Takes precedence over `adapter` if both are set.
|
|
75
|
+
*/
|
|
76
|
+
readonly chain?: JudgeChain;
|
|
77
|
+
/**
|
|
78
|
+
* Timeout per individual adapter call in milliseconds.
|
|
79
|
+
* Only used when `adapter` is set (JudgeChain manages its own timeouts).
|
|
80
|
+
*
|
|
81
|
+
* @default 5000
|
|
82
|
+
*/
|
|
83
|
+
readonly timeoutMs?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Behavior when ALL judges fail (timeout, error, unparseable).
|
|
86
|
+
*
|
|
87
|
+
* - `false` (default) — **Fail-closed**: rules are DROPPED.
|
|
88
|
+
* - `true` — **Fail-open**: rules PASS (use at your own risk).
|
|
89
|
+
*
|
|
90
|
+
* Only used when `adapter` is set. JudgeChain manages its own failOpen.
|
|
91
|
+
*
|
|
92
|
+
* @default false
|
|
93
|
+
*/
|
|
94
|
+
readonly failOpen?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Optional telemetry sink for `security.firewall` events.
|
|
97
|
+
* When provided, emits an event after every evaluation with
|
|
98
|
+
* verdict details (allowed/rejected counts, duration, fallback).
|
|
99
|
+
*/
|
|
100
|
+
readonly telemetry?: TelemetrySink;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Structured result of a firewall evaluation.
|
|
104
|
+
*
|
|
105
|
+
* Contains both allowed and rejected rules with reasoning,
|
|
106
|
+
* enabling telemetry and audit trails.
|
|
107
|
+
*/
|
|
108
|
+
export interface FirewallVerdict {
|
|
109
|
+
/** Rules that passed the firewall (safe to send to LLM) */
|
|
110
|
+
readonly allowed: readonly string[];
|
|
111
|
+
/** Rules that were rejected (stripped from the response) */
|
|
112
|
+
readonly rejected: readonly FirewallRejection[];
|
|
113
|
+
/** Whether the verdict was determined by failOpen/failClosed */
|
|
114
|
+
readonly fallbackTriggered: boolean;
|
|
115
|
+
/** Total evaluation duration in milliseconds */
|
|
116
|
+
readonly durationMs: number;
|
|
117
|
+
/** Raw chain result for telemetry */
|
|
118
|
+
readonly chainResult: JudgeChainResult;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A single rejected rule with the reason for rejection.
|
|
122
|
+
*/
|
|
123
|
+
export interface FirewallRejection {
|
|
124
|
+
/** The rule string that was rejected */
|
|
125
|
+
readonly rule: string;
|
|
126
|
+
/** Reason for rejection (from the judge) */
|
|
127
|
+
readonly reason: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build the evaluation prompt for the firewall judge.
|
|
131
|
+
*
|
|
132
|
+
* The prompt asks the LLM to evaluate whether any of the provided
|
|
133
|
+
* system rules appear to contain prompt injection attempts.
|
|
134
|
+
*
|
|
135
|
+
* @param rules - Array of system rules to evaluate
|
|
136
|
+
* @returns Complete evaluation prompt
|
|
137
|
+
*/
|
|
138
|
+
export declare function buildFirewallPrompt(rules: readonly string[]): string;
|
|
139
|
+
/**
|
|
140
|
+
* Parse the firewall judge's response into a structured verdict.
|
|
141
|
+
*
|
|
142
|
+
* @param rules - Original rules array
|
|
143
|
+
* @param chainResult - Result from the JudgeChain
|
|
144
|
+
* @returns Structured {@link FirewallVerdict}
|
|
145
|
+
*
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
148
|
+
export declare function parseFirewallVerdict(rules: readonly string[], chainResult: JudgeChainResult): FirewallVerdict;
|
|
149
|
+
/**
|
|
150
|
+
* Evaluate system rules through the firewall.
|
|
151
|
+
*
|
|
152
|
+
* Builds the judge prompt, sends it through the chain, and
|
|
153
|
+
* parses the response into a structured {@link FirewallVerdict}.
|
|
154
|
+
*
|
|
155
|
+
* @param rules - System rules to evaluate
|
|
156
|
+
* @param config - Firewall configuration
|
|
157
|
+
* @returns A structured verdict with allowed and rejected rules
|
|
158
|
+
*/
|
|
159
|
+
export declare function evaluateRules(rules: readonly string[], config: PromptFirewallConfig): Promise<FirewallVerdict>;
|
|
160
|
+
//# sourceMappingURL=PromptFirewall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromptFirewall.d.ts","sourceRoot":"","sources":["../../src/presenter/PromptFirewall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAEH,KAAK,UAAU,EAEf,KAAK,gBAAgB,EACxB,MAAM,iBAAiB,CAAC;AAIzB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,oBAAoB;IACjC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAExC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAE5B;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;;;;;;OASG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAEpC,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAEhD,gEAAgE;IAChE,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,qCAAqC;IACrC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B;AAID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAgCpE;AAID;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,WAAW,EAAE,gBAAgB,GAC9B,eAAe,CAsFjB;AAoCD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAC/B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,oBAAoB,GAC7B,OAAO,CAAC,eAAe,CAAC,CA6C1B"}
|