Schema field verification and explicit preview scope
The policy implementation follows.
ts
export function createReplyPreviewRules(options: {
readonly stages: readonly ReplyPreviewStage[];
readonly tools: readonly ReplyPreviewTool[];
}): readonly ReplyPreviewRule[] {
const tools = new Map<string, ReplyPreviewTool>();
for (const capability of options.tools) {
const id = capability.id;
breakWhile(!id || tools.has(id), 'Duplicate preview capability declaration');
const preview = previewSnapshot(capability.preview);
// Validate and emit the same canonical snapshot, never reread App getters.
const jsonSchema = preview ? structuredClone(capability.jsonSchema) : {};
tools.set(id, { id, preview, jsonSchema });
}
const stages = new Set<string>(),
rules: ReplyPreviewRule[] = [];
for (const stage of options.stages) {
const { id: stageId, preview, toolIds } = stage;
breakWhile(!stageId || stages.has(stageId), 'Duplicate preview stage declaration');
stages.add(stageId);
const ids = toolIds === undefined ? [] : stringList(toolIds, 'capability IDs');
breakWhile(
new Set(ids).size !== ids.length || ids.some((id) => !tools.has(id)),
'Preview stage references an unknown capability',
);
breakWhile(
preview !== undefined && !['text', 'tools'].includes(preview),
'Unknown preview stage mode',
);
if (!preview) continue;
if (preview === 'text') {
breakWhile(ids.length > 0, 'Text preview requires a declared plain-text stage');
rules.push(Object.freeze({ stageId }));
continue;
}
for (const id of ids) {
const capability = tools.get(id)!;
if (!capability.preview) continue;
const source = capability.preview === true ? {} : capability.preview;
const { paths, when } = source;
const checkedPaths =
paths ??
[['text'], ['bubbles', '*']].filter((path) => fieldExists(capability.jsonSchema, path));
breakWhile(
!checkedPaths.length ||
checkedPaths.some((path) => !fieldExists(capability.jsonSchema, path)),
`Invalid preview field: ${id}`,
);
breakWhile(
!!when && !fieldExists(capability.jsonSchema, when.path),
`Invalid preview eligibility field: ${id}`,
);
// Only generator-owned snapshots are frozen; App declarations remain untouched.
const emittedPaths = Object.freeze(checkedPaths.map((path) => Object.freeze(path)));
const emittedWhen =
when &&
Object.freeze({ path: Object.freeze(when.path), values: Object.freeze(when.values) });
rules.push(
Object.freeze({
stageId,
toolId: wireName(id),
paths: emittedPaths,
...(emittedWhen ? { when: emittedWhen } : {}),
}),
);
}
}
return Object.freeze(rules);
}
function fieldExists(root: JsonSchema, path: readonly string[]): boolean {
const visit = (schema: unknown, remaining: readonly string[], depth: number): boolean => {
if (!schema || typeof schema !== 'object' || depth > 64) return false;
if (!remaining.length) return true;
const current = schema as Record<string, unknown>;
if (typeof current.$ref === 'string' && current.$ref.startsWith('#/')) {
const target = current.$ref
.slice(2)
.split('/')
.reduce<unknown>(
(value, part) =>
value && typeof value === 'object'
? (() => {
const key = part.replace(/~1/g, '/').replace(/~0/g, '~');
return Object.hasOwn(value, key)
? (value as Record<string, unknown>)[key]
: undefined;
})()
: undefined,
root,
);
if (visit(target, remaining, depth + 1)) return true;
}
for (const keyword of ['anyOf', 'oneOf', 'allOf']) {
const alternatives = current[keyword];
if (
Array.isArray(alternatives) &&
alternatives.some((value) => visit(value, remaining, depth + 1))
)
return true;
}
const [key, ...rest] = remaining;
const properties = current.properties;
if (
key !== '*' &&
properties &&
typeof properties === 'object' &&
Object.hasOwn(properties, key!) &&
visit((properties as Record<string, unknown>)[key!], rest, depth + 1)
)
return true;
return (key === '*' || /^\d+$/.test(key!)) && visit(current.items, rest, depth + 1);
};
return visit(root, path, 0);
}