@mux/ai 0.41.0 → 0.42.0
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/dist/{index-C3PW73qH.d.ts → index-NkSOF92U.d.ts} +11 -72
- package/dist/index.d.ts +3 -2
- package/dist/index.js +231 -82
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/prompts/index.d.ts +335 -0
- package/dist/prompts/index.js +490 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/workflows/index.d.ts +2 -1
- package/dist/workflows/index.js +230 -81
- package/dist/workflows/index.js.map +1 -1
- package/package.json +7 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalise untrusted text by folding Unicode compatibility forms
|
|
3
|
+
* (NFKC) and stripping invisible / bidi-control code points.
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth for the two places this transformation is
|
|
6
|
+
* needed:
|
|
7
|
+
*
|
|
8
|
+
* - **Output side** (internal `detectLeakReason`): applied to every
|
|
9
|
+
* model output before matching it against canary / tag / encoded-
|
|
10
|
+
* blob detectors. Normalising here ensures an attacker-emitted
|
|
11
|
+
* fullwidth `<role>` or a canary split with zero-width characters
|
|
12
|
+
* still matches.
|
|
13
|
+
*
|
|
14
|
+
* - **Input side** (`sanitizeUntrustedText` in
|
|
15
|
+
* `src/primitives/transcripts.ts`): applied to every transcript
|
|
16
|
+
* cue before it reaches a prompt. Normalising here prevents
|
|
17
|
+
* payloads hidden in homoglyphs or invisible splitters from being
|
|
18
|
+
* invisible to human reviewers while still understandable to the
|
|
19
|
+
* model.
|
|
20
|
+
*
|
|
21
|
+
* Using the same function on both sides keeps the hygiene symmetric —
|
|
22
|
+
* any character folded / stripped on one end is folded / stripped on
|
|
23
|
+
* the other. Example transformations:
|
|
24
|
+
*
|
|
25
|
+
* - Fullwidth angle brackets (U+FF1C / U+FF1E) around a word fold to
|
|
26
|
+
* ASCII `<` / `>` under NFKC, so a "fullwidth-tag" wrapping still
|
|
27
|
+
* trips the tag detector.
|
|
28
|
+
* - A zero-width space (U+200B) spliced into the middle of a word is
|
|
29
|
+
* removed, collapsing "ig + ZWSP + nore" back to "ignore".
|
|
30
|
+
* - A leading right-to-left override (U+202E) is stripped, so a
|
|
31
|
+
* filename that would render reversed in a terminal reads forward
|
|
32
|
+
* for the model and for any downstream substring check.
|
|
33
|
+
*/
|
|
34
|
+
declare function normalizeUntrustedUnicode(text: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Which detector fired on a leak, or `null` when the text is clean.
|
|
37
|
+
*
|
|
38
|
+
* - `canary` — the system-prompt canary token appeared in the output.
|
|
39
|
+
* Near-zero false-positive rate; treat as a confirmed leak.
|
|
40
|
+
* - `prompt_tag` — an XML-like tag taken from our prompt structure
|
|
41
|
+
* appeared in the output. Low false-positive rate.
|
|
42
|
+
* - `encoded_blob` — a long run of base64- or hex-shaped characters
|
|
43
|
+
* appeared in a field expected to hold prose. Heuristic; some false
|
|
44
|
+
* positives on hashes or tokens in tech-content transcripts.
|
|
45
|
+
* - `unexpected_key` — the model emitted a JSON field not declared in
|
|
46
|
+
* the schema (see {@link detectUnexpectedKeys}). Stripped silently
|
|
47
|
+
* by zod's default `.strip()` mode; this signal surfaces the
|
|
48
|
+
* smuggling attempt rather than letting it go unseen.
|
|
49
|
+
*/
|
|
50
|
+
type LeakReason = "canary" | "prompt_tag" | "encoded_blob" | "unexpected_key" | null;
|
|
51
|
+
/**
|
|
52
|
+
* Returns which detector fires on `text`, or `null` if none do.
|
|
53
|
+
*
|
|
54
|
+
* Exposed alongside {@link scrubFreeTextField} so workflows can decide
|
|
55
|
+
* how to surface the signal (an `engagement-insights` summary might be
|
|
56
|
+
* suppressed silently, whereas an `ask-questions` reasoning field might
|
|
57
|
+
* be converted into a skip).
|
|
58
|
+
*/
|
|
59
|
+
declare function detectLeakReason(text: string | null | undefined): LeakReason;
|
|
60
|
+
/**
|
|
61
|
+
* Returns true when `text` contains evidence of a system-prompt leak.
|
|
62
|
+
*
|
|
63
|
+
* Convenience wrapper around {@link detectLeakReason} preserved for
|
|
64
|
+
* readability at call sites that do not need the reason code.
|
|
65
|
+
*/
|
|
66
|
+
declare function detectSystemPromptLeak(text: string | null | undefined): boolean;
|
|
67
|
+
/** Result of a scrub check — see {@link scrubFreeTextField}. */
|
|
68
|
+
interface ScrubResult {
|
|
69
|
+
/** Original text when clean; empty string when a leak was detected. */
|
|
70
|
+
text: string;
|
|
71
|
+
/** True when a leak was detected and the text was suppressed. */
|
|
72
|
+
leaked: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Which detector fired, when a leak was detected.
|
|
75
|
+
*
|
|
76
|
+
* Useful for telemetry — operators may want to treat a `canary` hit
|
|
77
|
+
* (near-zero false-positive rate) differently from an `encoded_blob`
|
|
78
|
+
* hit (heuristic, occasional false positives).
|
|
79
|
+
*/
|
|
80
|
+
reason: LeakReason;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Checks a free-text model output field for system-prompt leakage.
|
|
84
|
+
*
|
|
85
|
+
* When a leak is detected: emits a warning (with the reason code and
|
|
86
|
+
* caller-supplied context label) and returns an empty string so the
|
|
87
|
+
* caller can substitute a neutral placeholder appropriate to its output
|
|
88
|
+
* shape. When clean: returns the original text unchanged.
|
|
89
|
+
*
|
|
90
|
+
* Callers typically pair this with length caps on the underlying zod
|
|
91
|
+
* schema — a cap on `reasoning`/`insight` of ~400 chars makes encoded
|
|
92
|
+
* exfiltration mechanically difficult, and the scrubber catches what
|
|
93
|
+
* still fits.
|
|
94
|
+
*
|
|
95
|
+
* @param text the raw free-text field from the model
|
|
96
|
+
* @param context a short label used in the warning and returned
|
|
97
|
+
* telemetry (e.g. "ask-questions reasoning for question 3")
|
|
98
|
+
*/
|
|
99
|
+
declare function scrubFreeTextField(text: string | null | undefined, context: string): ScrubResult;
|
|
100
|
+
/**
|
|
101
|
+
* Per-field record of a detected leak. Collected across every scrub
|
|
102
|
+
* performed inside a single workflow invocation and returned to the
|
|
103
|
+
* caller in the workflow result's `safety` field.
|
|
104
|
+
*/
|
|
105
|
+
interface ScrubbedFieldReport {
|
|
106
|
+
/**
|
|
107
|
+
* Identifier for the field that leaked. Format is workflow-specific
|
|
108
|
+
* (e.g. "reasoning[0]", "moment_insight[2]", "description"). Intended
|
|
109
|
+
* for debugging and observability; do not parse programmatically.
|
|
110
|
+
*/
|
|
111
|
+
field: string;
|
|
112
|
+
/** Which detector fired — see {@link LeakReason}. */
|
|
113
|
+
reason: Exclude<LeakReason, null>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Aggregate safety report returned from workflows that perform
|
|
117
|
+
* output-side scrubbing.
|
|
118
|
+
*
|
|
119
|
+
* Operators can use this to alert on suspected prompt-injection traffic
|
|
120
|
+
* without inspecting log warnings. Shape is stable across workflows so a
|
|
121
|
+
* single observer can consume it uniformly.
|
|
122
|
+
*/
|
|
123
|
+
interface SafetyReport {
|
|
124
|
+
/** `true` when at least one field was suppressed by the scrubber. */
|
|
125
|
+
leaksDetected: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* One entry per suppressed field. Empty (but present) when no leaks
|
|
128
|
+
* were detected. Iteration order matches the order scrubs ran, so
|
|
129
|
+
* callers can correlate entries with workflow structure if they wish.
|
|
130
|
+
*/
|
|
131
|
+
scrubbedFields: ScrubbedFieldReport[];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Collector helper used inside a workflow to thread a {@link SafetyReport}
|
|
135
|
+
* through multiple scrub calls without each call site having to track
|
|
136
|
+
* state. Usage:
|
|
137
|
+
*
|
|
138
|
+
* const safety = createSafetyReporter();
|
|
139
|
+
* const cleanReasoning = safety.scrub(raw.reasoning, "reasoning[0]");
|
|
140
|
+
* const cleanSummary = safety.scrub(raw.summary, "summary");
|
|
141
|
+
* return { ..., safety: safety.report() };
|
|
142
|
+
*/
|
|
143
|
+
interface SafetyReporter {
|
|
144
|
+
/**
|
|
145
|
+
* Scrub `text`, record a report entry if it leaked, and return the
|
|
146
|
+
* resulting text. Mirrors {@link scrubFreeTextField} but returns the
|
|
147
|
+
* string directly for convenience at call sites that only care about
|
|
148
|
+
* the suppressed/clean text.
|
|
149
|
+
*/
|
|
150
|
+
scrub: (text: string | null | undefined, field: string) => string;
|
|
151
|
+
/**
|
|
152
|
+
* Underlying scrub helper for call sites that need the full
|
|
153
|
+
* {@link ScrubResult} (e.g. to decide between "skip" and "substitute
|
|
154
|
+
* placeholder"). Recording into the aggregate report happens
|
|
155
|
+
* automatically.
|
|
156
|
+
*/
|
|
157
|
+
scrubDetailed: (text: string | null | undefined, field: string) => ScrubResult;
|
|
158
|
+
/**
|
|
159
|
+
* Inspect a pre-parse object for keys that are not declared in the
|
|
160
|
+
* consuming zod schema, log + record each as `unexpected_key`, and
|
|
161
|
+
* return them so the caller can take additional action if desired.
|
|
162
|
+
*
|
|
163
|
+
* This complements zod's default `.strip()` behaviour: `.strip()`
|
|
164
|
+
* silently drops the extras on parse (which is what we want — no
|
|
165
|
+
* hard failures), while this helper surfaces the smuggling attempt
|
|
166
|
+
* so it does not go unseen.
|
|
167
|
+
*
|
|
168
|
+
* @param raw the pre-parse model output
|
|
169
|
+
* @param expectedKeys the keys declared by the receiving schema
|
|
170
|
+
* @param context a short label used in the warning and report
|
|
171
|
+
* (e.g. "summary_metadata" or "chapters[3]")
|
|
172
|
+
* @returns the list of keys present in `raw` but not in
|
|
173
|
+
* `expectedKeys`
|
|
174
|
+
*/
|
|
175
|
+
recordUnexpectedKeys: (raw: unknown, expectedKeys: readonly string[], context: string) => string[];
|
|
176
|
+
/**
|
|
177
|
+
* Record a pre-computed safety signal. Used when the detection
|
|
178
|
+
* happens inside a `"use step"` boundary and the step returns the
|
|
179
|
+
* finding as a serialisable value for the workflow to aggregate.
|
|
180
|
+
*
|
|
181
|
+
* The caller is responsible for logging if desired — this method
|
|
182
|
+
* only updates the aggregate report.
|
|
183
|
+
*/
|
|
184
|
+
record: (field: string, reason: Exclude<LeakReason, null>) => void;
|
|
185
|
+
/** Snapshot the current aggregate report. */
|
|
186
|
+
report: () => SafetyReport;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Detect top-level keys in a model-emitted object that are not declared
|
|
190
|
+
* in the expected-key list. The check is deliberately shallow — nested
|
|
191
|
+
* arrays-of-objects (chapters, momentInsights, etc.) should call this
|
|
192
|
+
* once per element.
|
|
193
|
+
*
|
|
194
|
+
* Zod's default `.strip()` mode silently drops the extras during parse;
|
|
195
|
+
* this helper does the detection the strip otherwise hides. It is
|
|
196
|
+
* exported separately so call sites can detect without necessarily
|
|
197
|
+
* going through a {@link SafetyReporter} (e.g. in tests).
|
|
198
|
+
*/
|
|
199
|
+
declare function detectUnexpectedKeys(raw: unknown, expectedKeys: readonly string[]): string[];
|
|
200
|
+
/**
|
|
201
|
+
* Parse `rawText` as JSON and compare its top-level keys against
|
|
202
|
+
* `expectedKeys`.
|
|
203
|
+
*
|
|
204
|
+
* Returns an empty array when the text is not valid JSON (so a model
|
|
205
|
+
* that wraps its output in markdown fences or trailing whitespace does
|
|
206
|
+
* not produce spurious safety signals). Intended for use inside step
|
|
207
|
+
* functions that receive the model's raw text output but run inside
|
|
208
|
+
* a `"use step"` boundary that can't ship a {@link SafetyReporter}
|
|
209
|
+
* across — pass the returned array back as a serialisable value for
|
|
210
|
+
* the workflow to aggregate.
|
|
211
|
+
*/
|
|
212
|
+
declare function detectUnexpectedKeysFromRawText(rawText: string | undefined, expectedKeys: readonly string[]): string[];
|
|
213
|
+
declare function createSafetyReporter(): SafetyReporter;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* A single section of a prompt, rendered as an XML-like tag.
|
|
217
|
+
*/
|
|
218
|
+
interface PromptSection {
|
|
219
|
+
/** The XML tag name for this section */
|
|
220
|
+
tag: string;
|
|
221
|
+
/** The content inside the tag */
|
|
222
|
+
content: string;
|
|
223
|
+
/** Optional attributes to add to the tag (e.g., { format: "plain text" }) */
|
|
224
|
+
attributes?: Record<string, string>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Configuration for building a prompt section.
|
|
228
|
+
* Can be a full PromptSection object, just a string (content only), or undefined to use default.
|
|
229
|
+
*/
|
|
230
|
+
type SectionOverride = string | PromptSection | undefined;
|
|
231
|
+
/**
|
|
232
|
+
* A template defining the default sections for a prompt.
|
|
233
|
+
* Keys are section identifiers, values are the default PromptSection definitions.
|
|
234
|
+
*/
|
|
235
|
+
type PromptTemplate<TSections extends string> = Record<TSections, PromptSection>;
|
|
236
|
+
/**
|
|
237
|
+
* User-provided overrides for prompt sections.
|
|
238
|
+
* Each key can override the corresponding section's content or full definition.
|
|
239
|
+
*/
|
|
240
|
+
type PromptOverrides<TSections extends string> = Partial<Record<TSections, SectionOverride>>;
|
|
241
|
+
/**
|
|
242
|
+
* Configuration for the prompt builder.
|
|
243
|
+
*/
|
|
244
|
+
interface PromptBuilderConfig<TSections extends string> {
|
|
245
|
+
/** Default sections that make up the prompt template */
|
|
246
|
+
template: PromptTemplate<TSections>;
|
|
247
|
+
/** Order in which sections should appear in the final prompt */
|
|
248
|
+
sectionOrder: TSections[];
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* A configured prompt builder instance with methods to build prompts.
|
|
252
|
+
*/
|
|
253
|
+
interface PromptBuilder<TSections extends string> {
|
|
254
|
+
/** The default template sections */
|
|
255
|
+
template: PromptTemplate<TSections>;
|
|
256
|
+
/** Build a prompt string, optionally overriding specific sections */
|
|
257
|
+
build: (overrides?: PromptOverrides<TSections>) => string;
|
|
258
|
+
/** Build a prompt with additional dynamic sections appended */
|
|
259
|
+
buildWithContext: (overrides?: PromptOverrides<TSections>, additionalSections?: PromptSection[]) => string;
|
|
260
|
+
/** Get a single section's content (useful for partial rendering) */
|
|
261
|
+
getSection: (section: TSections, override?: SectionOverride) => string;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Renders a PromptSection as an XML-like string.
|
|
265
|
+
*/
|
|
266
|
+
declare function renderSection(section: PromptSection): string;
|
|
267
|
+
/**
|
|
268
|
+
* Creates a type-safe prompt builder from a template configuration.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const builder = createPromptBuilder({
|
|
273
|
+
* template: {
|
|
274
|
+
* task: { tag: 'task', content: 'Analyze the image...' },
|
|
275
|
+
* tone: { tag: 'tone', content: 'Be professional.' },
|
|
276
|
+
* },
|
|
277
|
+
* sectionOrder: ['task', 'tone'],
|
|
278
|
+
* });
|
|
279
|
+
*
|
|
280
|
+
* // Use defaults
|
|
281
|
+
* const prompt1 = builder.build();
|
|
282
|
+
*
|
|
283
|
+
* // Override specific sections
|
|
284
|
+
* const prompt2 = builder.build({
|
|
285
|
+
* tone: 'Be casual and friendly.',
|
|
286
|
+
* });
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
declare function createPromptBuilder<TSections extends string>(config: PromptBuilderConfig<TSections>): PromptBuilder<TSections>;
|
|
290
|
+
/**
|
|
291
|
+
* Creates a transcript section for inclusion in prompts.
|
|
292
|
+
*/
|
|
293
|
+
declare function createTranscriptSection(transcriptText: string, format?: "plain text" | "WebVTT"): PromptSection;
|
|
294
|
+
/**
|
|
295
|
+
* Creates a tone section for inclusion in prompts.
|
|
296
|
+
*/
|
|
297
|
+
declare function createToneSection(instruction: string): PromptSection;
|
|
298
|
+
/**
|
|
299
|
+
* Creates a language section for inclusion in prompts.
|
|
300
|
+
* Instructs the model to produce all output in the specified language.
|
|
301
|
+
*/
|
|
302
|
+
declare function createLanguageSection(languageName: string): PromptSection;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Compact, provider-independent scene context shared by prompt consumers.
|
|
306
|
+
*
|
|
307
|
+
* Timestamps are absolute offsets from the beginning of the asset. The
|
|
308
|
+
* context intentionally excludes source windows, cues, images, shot details,
|
|
309
|
+
* and scored concepts so callers can pass it between workflows without
|
|
310
|
+
* coupling them to scene-generation internals.
|
|
311
|
+
*/
|
|
312
|
+
interface SceneContextItemV1 {
|
|
313
|
+
/** Stable zero-based position in the ordered scene sequence. */
|
|
314
|
+
scene_index: number;
|
|
315
|
+
/** Absolute scene start time in milliseconds. */
|
|
316
|
+
start_ms: number;
|
|
317
|
+
/** Absolute scene end time in milliseconds. */
|
|
318
|
+
end_ms: number;
|
|
319
|
+
/** Short human-readable scene title. */
|
|
320
|
+
title: string;
|
|
321
|
+
/** Compact summary of the scene's spoken content, when available. */
|
|
322
|
+
audible_narrative?: string;
|
|
323
|
+
/** Compact summary of the scene's visual content, when available. */
|
|
324
|
+
visual_narrative?: string;
|
|
325
|
+
/** Combined audible and visual narrative, when both signals are available. */
|
|
326
|
+
blended_narrative?: string;
|
|
327
|
+
/** Important spoken concepts represented as compact phrases. */
|
|
328
|
+
notable_audible_concepts?: string[];
|
|
329
|
+
/** Important visual concepts represented as compact phrases. */
|
|
330
|
+
notable_visual_concepts?: string[];
|
|
331
|
+
/** Number of shots or visual beats that make up the scene. */
|
|
332
|
+
shot_count?: number;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export { type LeakReason, type PromptBuilder, type PromptBuilderConfig, type PromptOverrides, type PromptSection, type PromptTemplate, type SafetyReport, type SafetyReporter, type SceneContextItemV1, type ScrubResult, type ScrubbedFieldReport, type SectionOverride, createLanguageSection, createPromptBuilder, createSafetyReporter, createToneSection, createTranscriptSection, detectLeakReason, detectSystemPromptLeak, detectUnexpectedKeys, detectUnexpectedKeysFromRawText, normalizeUntrustedUnicode, renderSection, scrubFreeTextField };
|