@modelprofile.com/flexharness 9.7.0 → 9.9.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.
@@ -39,6 +39,165 @@ export class DocumentPageLimitError extends Error {
39
39
  this.documentIndex = details.documentIndex;
40
40
  }
41
41
  }
42
+ /*
43
+ * The image limits below stay within what the providers publish (read 2026-09-28):
44
+ * - Anthropic, https://platform.claude.com/docs/en/build-with-claude/vision: at most 8000x8000 px per
45
+ * image; in a request with more than 20 images at most 2000 px per side; at most 10 MB per image
46
+ * (base64) on the Claude API and 5 MB (base64) on Amazon Bedrock and Google Cloud; JPEG, PNG, GIF
47
+ * and WebP, of an animation only the first frame.
48
+ * - OpenAI, https://developers.openai.com/api/docs/guides/images-vision: PNG, JPEG, WebP and
49
+ * non-animated GIF; up to 512 MB payload and 1500 images per request; the page names no size per
50
+ * image. Images are scaled down on the provider's side.
51
+ */
52
+ /**
53
+ * The longest edge, in pixels, of an image sent to the model when a request names none: Anthropic's
54
+ * 2000 pixels per side for a request with more than 20 images, the tightest of the limits above.
55
+ */
56
+ export const DEFAULT_MAX_IMAGE_EDGE = 2000;
57
+ /**
58
+ * The most bytes of one image sent to the model: 5 000 000 characters once base64-encoded, within
59
+ * Anthropic's 5 MB per image on Amazon Bedrock and Google Cloud, the smallest of the limits above.
60
+ */
61
+ export const MAX_IMAGE_BYTES = 3_750_000;
62
+ /**
63
+ * The most pixels (width times height) of one image or TIFF page that is decoded: an A3 page scanned
64
+ * at 600 dpi has about 70 million. The images come from mail and uploads, so a larger one is refused
65
+ * from its header, before it is decoded, instead of being given sharp's own limit of about 268 million.
66
+ */
67
+ export const MAX_IMAGE_PIXELS = 100_000_000;
68
+ /** An image document this component does not read; the model was not called. */
69
+ export class DocumentFormatError extends Error {
70
+ /** Zero-based position, in the request's documents (the PDFs first, then the images), of the image. */
71
+ documentIndex;
72
+ /** The format the image was read as, or null when it could not be read at all. */
73
+ format;
74
+ reason;
75
+ constructor(message, details, options) {
76
+ super(message, options);
77
+ this.name = 'DocumentFormatError';
78
+ this.documentIndex = details.documentIndex;
79
+ this.format = details.format;
80
+ this.reason = details.reason;
81
+ }
82
+ }
83
+ /** The formats sent as they are when they need no scaling and no turning. */
84
+ const AS_IS_MEDIA_TYPES = new Map([
85
+ ['jpeg', 'image/jpeg'],
86
+ ['png', 'image/png'],
87
+ ['webp', 'image/webp'],
88
+ ]);
89
+ /** The formats read at all: sharp's names, HEIF only with AV1 compression (AVIF). */
90
+ const READABLE_IMAGE_FORMATS = new Set(['jpeg', 'png', 'webp', 'gif', 'tiff', 'heif']);
91
+ /** The formats whose further pages are frames of an animation, not pages of a document. */
92
+ const ANIMATED_FORMATS = new Set(['gif', 'webp']);
93
+ /** The one load of the optional peer dependency sharp; a failed load is forgotten. */
94
+ let sharpLoad = null;
95
+ function loadSharp() {
96
+ if (!sharpLoad) {
97
+ const load = plugins.importSharp().catch((error) => {
98
+ if (sharpLoad === load) {
99
+ sharpLoad = null;
100
+ }
101
+ throw new Error('Image documents need the optional peer dependency sharp.', { cause: error });
102
+ });
103
+ sharpLoad = load;
104
+ }
105
+ return sharpLoad;
106
+ }
107
+ /** Refuses a page with more pixels than `MAX_IMAGE_PIXELS`, from what its header says. */
108
+ function checkPixels(metadata, documentIndex, format) {
109
+ const pixels = (metadata.width ?? 0) * (metadata.height ?? 0);
110
+ if (pixels > MAX_IMAGE_PIXELS) {
111
+ throw new DocumentFormatError(`The image has ${metadata.width}x${metadata.height} pixels, more than the ${MAX_IMAGE_PIXELS} read here.`, { documentIndex, format, reason: 'too_many_pixels' });
112
+ }
113
+ }
114
+ /** What an image document is, read from its header without decoding it. */
115
+ async function readImage(sharp, image, documentIndex) {
116
+ let metadata;
117
+ try {
118
+ metadata = await sharp(image).metadata();
119
+ }
120
+ catch (error) {
121
+ throw new DocumentFormatError('The image could not be read.', { documentIndex, format: null, reason: 'unreadable' }, { cause: error });
122
+ }
123
+ const format = metadata.format ?? null;
124
+ if (format === 'heif' && metadata.compression !== 'av1') {
125
+ throw new DocumentFormatError('The image is HEIC, which cannot be decoded here; send it as JPEG.', { documentIndex, format, reason: 'heic' });
126
+ }
127
+ if (!format || !READABLE_IMAGE_FORMATS.has(format)) {
128
+ throw new DocumentFormatError(`The image is ${format ?? 'of an unknown format'}; images are read as JPEG, PNG, WebP, GIF, TIFF or AVIF.`, { documentIndex, format, reason: 'unsupported' });
129
+ }
130
+ const pages = Math.max(metadata.pages ?? 1, 1);
131
+ if (ANIMATED_FORMATS.has(format) && pages > 1) {
132
+ throw new DocumentFormatError(`The image is an animated ${format === 'gif' ? 'GIF' : 'WebP'} of ${pages} frames; its frames are no pages, so it is not read.`, { documentIndex, format, reason: 'animated' });
133
+ }
134
+ // sharp reports the top-level images of a HEIF container as its pages (libvips heifload, n-pages);
135
+ // only a TIFF's pages are pages of a document
136
+ if (format !== 'tiff' && pages > 1) {
137
+ throw new DocumentFormatError(`The image is an AVIF holding ${pages} images; which of them is the document is not known, so it is not read.`, { documentIndex, format, reason: 'multiple_images' });
138
+ }
139
+ checkPixels(metadata, documentIndex, format);
140
+ return { metadata, pageCount: format === 'tiff' ? pages : 1 };
141
+ }
142
+ /** One page of an image document, upright, scaled down and encoded; a page that cannot be decoded is refused. */
143
+ async function imagePage(sharp, image, page, format, photo, maxEdge, documentIndex) {
144
+ const pipeline = () => sharp(image, { page, limitInputPixels: MAX_IMAGE_PIXELS })
145
+ .autoOrient()
146
+ .resize({ width: maxEdge, height: maxEdge, fit: 'inside', withoutEnlargement: true });
147
+ // JPEG has no transparency: a transparent background becomes white, not black
148
+ const jpeg = async (quality) => ({
149
+ data: await pipeline().flatten({ background: '#ffffff' }).jpeg({ quality }).toBuffer(),
150
+ mediaType: 'image/jpeg',
151
+ });
152
+ let encoded;
153
+ try {
154
+ if (page > 0) {
155
+ // each TIFF page has its own size; the first was checked with the document
156
+ checkPixels(await sharp(image, { page }).metadata(), documentIndex, format);
157
+ }
158
+ encoded = photo ? await jpeg(90) : { data: await pipeline().png().toBuffer(), mediaType: 'image/png' };
159
+ // too large: a photo tries a lower quality, any other a JPEG at the higher one first
160
+ for (const quality of photo ? [75] : [90, 75]) {
161
+ if (encoded.data.byteLength <= MAX_IMAGE_BYTES) {
162
+ break;
163
+ }
164
+ encoded = await jpeg(quality);
165
+ }
166
+ }
167
+ catch (error) {
168
+ if (error instanceof DocumentFormatError) {
169
+ throw error;
170
+ }
171
+ throw new DocumentFormatError(pageLabel(page, 'could not be decoded.'), { documentIndex, format, reason: 'unreadable' }, { cause: error });
172
+ }
173
+ if (encoded.data.byteLength > MAX_IMAGE_BYTES) {
174
+ throw new DocumentFormatError(pageLabel(page, `stays larger than ${MAX_IMAGE_BYTES} bytes after scaling to ${maxEdge} pixels.`), { documentIndex, format, reason: 'too_large' });
175
+ }
176
+ // a Buffer is a Uint8Array view of a pooled ArrayBuffer; send exactly its bytes
177
+ return { data: new Uint8Array(encoded.data), mediaType: encoded.mediaType };
178
+ }
179
+ /** "The image …" for a one-page image, "Page n of the image …" for a TIFF page after the first. */
180
+ const pageLabel = (page, predicate) => page === 0 ? `The image ${predicate}` : `Page ${page + 1} of the image ${predicate}`;
181
+ /** An image document's pages as they are sent: as they are where that fits, else upright and scaled down. */
182
+ async function imagePages(sharp, image, metadata, pageCount, maxEdge, documentIndex, abortSignal) {
183
+ const format = metadata.format;
184
+ const asIs = AS_IS_MEDIA_TYPES.get(format);
185
+ if (asIs
186
+ && pageCount === 1
187
+ && (metadata.orientation ?? 1) === 1
188
+ && (metadata.width ?? Infinity) <= maxEdge
189
+ && (metadata.height ?? Infinity) <= maxEdge
190
+ && image.byteLength <= MAX_IMAGE_BYTES) {
191
+ return [{ data: image, mediaType: asIs }];
192
+ }
193
+ const photo = format === 'jpeg' || format === 'heif';
194
+ const pages = [];
195
+ for (let page = 0; page < pageCount; page++) {
196
+ abortSignal?.throwIfAborted();
197
+ pages.push(await imagePage(sharp, image, page, format, photo, maxEdge, documentIndex));
198
+ }
199
+ return pages;
200
+ }
42
201
  const DEFAULT_OCR_SYSTEM_MESSAGE = 'You are a precise OCR engine. Extract text faithfully and do not summarize.';
43
202
  const DEFAULT_OCR_USER_MESSAGE = 'Extract all readable text from this PDF. Preserve page order, line breaks, table structure, and obvious headings. Return only the extracted text.';
44
203
  /** The model-based PDF OCR recipe formerly provided by SmartOCR. */
@@ -68,41 +227,64 @@ export async function analyzeDocumentsWithUsage(options) {
68
227
  if (maxPages !== undefined && (!Number.isSafeInteger(maxPages) || maxPages < 1)) {
69
228
  throw new RangeError('maxPages must be a positive whole number.');
70
229
  }
230
+ const maxImageEdge = options.maxImageEdge ?? DEFAULT_MAX_IMAGE_EDGE;
231
+ if (!Number.isSafeInteger(maxImageEdge) || maxImageEdge < 1) {
232
+ throw new RangeError('maxImageEdge must be a positive whole number.');
233
+ }
71
234
  const abortSignal = options.abortSignal;
72
235
  abortSignal?.throwIfAborted();
73
- const imagePages = [];
74
- for (const [index, doc] of options.pdfDocuments.entries()) {
236
+ const pdfDocuments = options.pdfDocuments ?? [];
237
+ const imageDocuments = options.imageDocuments ?? [];
238
+ const documentCount = pdfDocuments.length + imageDocuments.length;
239
+ const limitDetails = (index, renderedPages, limit) => ({
240
+ renderedPages,
241
+ maxPages: limit,
242
+ documentCount,
243
+ documentIndex: index,
244
+ allDocumentsRendered: index === documentCount - 1,
245
+ });
246
+ const modelPages = [];
247
+ // every rendered page counts against maxPages, also a PDF page that renders empty and is not sent
248
+ let countedPages = 0;
249
+ for (const [index, doc] of pdfDocuments.entries()) {
75
250
  const pdf = await ensureSmartpdf();
76
251
  abortSignal?.throwIfAborted();
77
- const limitDetails = (renderedPages, limit) => ({
78
- renderedPages,
79
- maxPages: limit,
80
- documentCount: options.pdfDocuments.length,
81
- documentIndex: index,
82
- allDocumentsRendered: index === options.pdfDocuments.length - 1,
83
- });
84
252
  // SmartPDF limits one document, so each render gets the pages the request has left. It takes
85
253
  // no limit of zero: when the earlier documents used up the request's pages, the smallest limit
86
254
  // lets the document's page count be known, and a document of one page renders and is refused below.
87
- const renderMaxPages = maxPages === undefined ? undefined : Math.max(maxPages - imagePages.length, 1);
255
+ const renderMaxPages = maxPages === undefined ? undefined : Math.max(maxPages - countedPages, 1);
88
256
  let pages;
89
257
  try {
90
258
  pages = await pdf.convertPDFToPngBytes(doc, { signal: abortSignal, maxPages: renderMaxPages });
91
259
  }
92
260
  catch (error) {
93
261
  if (maxPages !== undefined && error instanceof plugins.smartpdf.SmartPdfPageLimitError) {
94
- throw new DocumentPageLimitError(limitDetails(imagePages.length + error.pageCount, maxPages), { cause: error });
262
+ throw new DocumentPageLimitError(limitDetails(index, countedPages + error.pageCount, maxPages), { cause: error });
95
263
  }
96
264
  throw error;
97
265
  }
98
266
  abortSignal?.throwIfAborted();
99
- imagePages.push(...pages);
100
- if (maxPages !== undefined && imagePages.length > maxPages) {
101
- throw new DocumentPageLimitError(limitDetails(imagePages.length, maxPages));
267
+ countedPages += pages.length;
268
+ modelPages.push(...pages.filter((page) => page && page.length > 0).map((page) => ({ data: page, mediaType: 'image/png' })));
269
+ if (maxPages !== undefined && countedPages > maxPages) {
270
+ throw new DocumentPageLimitError(limitDetails(index, countedPages, maxPages));
102
271
  }
103
272
  }
104
- // Filter out empty buffers
105
- const validPages = imagePages.filter(page => page && page.length > 0);
273
+ if (imageDocuments.length) {
274
+ const sharp = await loadSharp();
275
+ for (const [imageIndex, image] of imageDocuments.entries()) {
276
+ abortSignal?.throwIfAborted();
277
+ const index = pdfDocuments.length + imageIndex;
278
+ const { metadata, pageCount } = await readImage(sharp, image, index);
279
+ // an image's pages are known from its header, so an image over the limit is not decoded
280
+ if (maxPages !== undefined && countedPages + pageCount > maxPages) {
281
+ throw new DocumentPageLimitError(limitDetails(index, countedPages + pageCount, maxPages));
282
+ }
283
+ countedPages += pageCount;
284
+ modelPages.push(...await imagePages(sharp, image, metadata, pageCount, maxImageEdge, index, abortSignal));
285
+ }
286
+ abortSignal?.throwIfAborted();
287
+ }
106
288
  const onUsage = options.onUsage;
107
289
  const modelCalls = onUsage ? new plugins.AgentModelCallUsageRecorder(options.model, onUsage) : null;
108
290
  // Each attempt, retries included, is one model call: it starts in the middleware, which also
@@ -130,10 +312,10 @@ export async function analyzeDocumentsWithUsage(options) {
130
312
  role: 'user',
131
313
  content: [
132
314
  { type: 'text', text: options.userMessage },
133
- ...validPages.map(page => ({
315
+ ...modelPages.map(page => ({
134
316
  type: 'file',
135
- data: page,
136
- mediaType: 'image/png',
317
+ data: page.data,
318
+ mediaType: page.mediaType,
137
319
  })),
138
320
  ],
139
321
  },
@@ -172,7 +354,7 @@ export async function analyzeDocumentsWithUsage(options) {
172
354
  cacheReadTokens: usage.inputTokenDetails.cacheReadTokens ?? 0,
173
355
  cacheWriteTokens: usage.inputTokenDetails.cacheWriteTokens ?? 0,
174
356
  },
175
- pageCount: validPages.length,
357
+ pageCount: modelPages.length,
176
358
  };
177
359
  }
178
360
  export async function stopSmartpdf() {
@@ -193,4 +375,4 @@ export async function stopSmartpdf() {
193
375
  }
194
376
  await instance.stop();
195
377
  }
196
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19kb2N1bWVudC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QywrR0FBK0c7QUFDL0csSUFBSSxhQUFhLEdBQThCLElBQUksQ0FBQztBQUVwRCxTQUFTLGNBQWM7SUFDckIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE1BQU0sUUFBUSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNqRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3BELGFBQWEsR0FBRyxLQUFLLENBQUM7UUFDdEIsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDZixJQUFJLGFBQWEsS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDNUIsYUFBYSxHQUFHLElBQUksQ0FBQztZQUN2QixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBQ0QsT0FBTyxhQUFhLENBQUM7QUFDdkIsQ0FBQztBQXlFRCwrRkFBK0Y7QUFDL0YsTUFBTSxPQUFPLHNCQUF1QixTQUFRLEtBQUs7SUFDL0M7Ozs7O09BS0c7SUFDYSxhQUFhLENBQVM7SUFDdEIsUUFBUSxDQUFTO0lBQ2pDLGdHQUFnRztJQUNoRixhQUFhLENBQVM7SUFFdEMsWUFBWSxPQUF1QyxFQUFFLE9BQXNCO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxhQUFhLEtBQUssQ0FBQztZQUN6QyxDQUFDLENBQUMsb0JBQW9CLE9BQU8sQ0FBQyxhQUFhLFFBQVE7WUFDbkQsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0I7Z0JBQzVCLENBQUMsQ0FBQyxzQkFBc0IsT0FBTyxDQUFDLGFBQWEsUUFBUTtnQkFDckQs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
378
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19kb2N1bWVudC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QywrR0FBK0c7QUFDL0csSUFBSSxhQUFhLEdBQThCLElBQUksQ0FBQztBQUVwRCxTQUFTLGNBQWM7SUFDckIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE1BQU0sUUFBUSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNqRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3BELGFBQWEsR0FBRyxLQUFLLENBQUM7UUFDdEIsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDZixJQUFJLGFBQWEsS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDNUIsYUFBYSxHQUFHLElBQUksQ0FBQztZQUN2QixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBQ0QsT0FBTyxhQUFhLENBQUM7QUFDdkIsQ0FBQztBQTZGRCwrRkFBK0Y7QUFDL0YsTUFBTSxPQUFPLHNCQUF1QixTQUFRLEtBQUs7SUFDL0M7Ozs7O09BS0c7SUFDYSxhQUFhLENBQVM7SUFDdEIsUUFBUSxDQUFTO0lBQ2pDLGdHQUFnRztJQUNoRixhQUFhLENBQVM7SUFFdEMsWUFBWSxPQUF1QyxFQUFFLE9BQXNCO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxhQUFhLEtBQUssQ0FBQztZQUN6QyxDQUFDLENBQUMsb0JBQW9CLE9BQU8sQ0FBQyxhQUFhLFFBQVE7WUFDbkQsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0I7Z0JBQzVCLENBQUMsQ0FBQyxzQkFBc0IsT0FBTyxDQUFDLGFBQWEsUUFBUTtnQkFDckQs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
@@ -5,3 +5,38 @@ export { generateText, wrapLanguageModel, smartpdf, AgentModelCallUsageRecorder
5
5
  export type { ModelMessage } from 'ai';
6
6
  export type { TSmartAiLanguageModel, TSmartAiProviderOptions } from '@modelprofile.com/flexharness-models';
7
7
  export type { TAgentModelCallUsageReporter } from '@modelprofile.com/flexharness-agent';
8
+ /** What sharp reads from an image's header (`sharp.Metadata`, the fields used here). */
9
+ export interface IImageMetadata {
10
+ format?: string;
11
+ compression?: string;
12
+ pages?: number;
13
+ orientation?: number;
14
+ width?: number;
15
+ height?: number;
16
+ }
17
+ /** A sharp pipeline, the operations used here. */
18
+ export interface IImagePipeline {
19
+ metadata(): Promise<IImageMetadata>;
20
+ autoOrient(): IImagePipeline;
21
+ resize(options: {
22
+ width: number;
23
+ height: number;
24
+ fit: 'inside';
25
+ withoutEnlargement: boolean;
26
+ }): IImagePipeline;
27
+ flatten(options: {
28
+ background: string;
29
+ }): IImagePipeline;
30
+ jpeg(options: {
31
+ quality: number;
32
+ }): IImagePipeline;
33
+ png(): IImagePipeline;
34
+ toBuffer(): Promise<Uint8Array>;
35
+ }
36
+ /** Opens an image (a page of it) as a pipeline, as `sharp(input, options)` does. */
37
+ export type TImageReader = (input: Uint8Array, options?: {
38
+ page?: number;
39
+ limitInputPixels?: number;
40
+ }) => IImagePipeline;
41
+ /** Loads sharp and hands out its reader. */
42
+ export declare const importSharp: () => Promise<TImageReader>;
@@ -2,4 +2,9 @@ import { generateText, wrapLanguageModel } from 'ai';
2
2
  import * as smartpdf from '@push.rocks/smartpdf';
3
3
  import { AgentModelCallUsageRecorder } from '@modelprofile.com/flexharness-agent';
4
4
  export { generateText, wrapLanguageModel, smartpdf, AgentModelCallUsageRecorder };
5
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2RvY3VtZW50L3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxpQkFBaUIsRUFBRSxNQUFNLElBQUksQ0FBQztBQUNyRCxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ2xGLE9BQU8sRUFBRSxZQUFZLEVBQUUsaUJBQWlCLEVBQUUsUUFBUSxFQUFFLDJCQUEyQixFQUFFLENBQUMifQ==
5
+ /** Loads sharp and hands out its reader. */
6
+ export const importSharp = async () => {
7
+ const sharp = (await import('sharp')).default;
8
+ return (input, options) => sharp(input, options);
9
+ };
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2RvY3VtZW50L3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxpQkFBaUIsRUFBRSxNQUFNLElBQUksQ0FBQztBQUNyRCxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ2xGLE9BQU8sRUFBRSxZQUFZLEVBQUUsaUJBQWlCLEVBQUUsUUFBUSxFQUFFLDJCQUEyQixFQUFFLENBQUM7QUFrQ2xGLDRDQUE0QztBQUM1QyxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsS0FBSyxJQUEyQixFQUFFO0lBQzNELE1BQU0sS0FBSyxHQUFHLENBQUMsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFDOUMsT0FBTyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDbkQsQ0FBQyxDQUFDIn0=
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Task Venture Capital GmbH",
3
3
  "license": "MIT",
4
4
  "name": "@modelprofile.com/flexharness",
5
- "version": "9.7.0",
5
+ "version": "9.9.0",
6
6
  "type": "module",
7
7
  "description": "Managed agent sessions with tools, MCP, chat, OCR, documents and browser control through focused subpath imports.",
8
8
  "main": "./dist_ts/index.js",
@@ -62,8 +62,8 @@
62
62
  }
63
63
  },
64
64
  "dependencies": {
65
- "@modelprofile.com/flexharness-agent": "9.7.0",
66
- "@modelprofile.com/flexharness-models": "9.7.0",
65
+ "@modelprofile.com/flexharness-agent": "9.9.0",
66
+ "@modelprofile.com/flexharness-models": "9.9.0",
67
67
  "@push.rocks/smartrequest": "^5.0.1",
68
68
  "ai": "^7.0.107",
69
69
  "zod": "^4.4.1"
@@ -71,6 +71,7 @@
71
71
  "peerDependencies": {
72
72
  "@modelcontextprotocol/sdk": "^1.29.0",
73
73
  "@push.rocks/smartpdf": "^6.0.0",
74
+ "sharp": "^0.35.3",
74
75
  "puppeteer": "^24.42.0",
75
76
  "ink": "^6.8.0",
76
77
  "ink-text-input": "^6.0.0",
@@ -85,6 +86,9 @@
85
86
  "@push.rocks/smartpdf": {
86
87
  "optional": true
87
88
  },
89
+ "sharp": {
90
+ "optional": true
91
+ },
88
92
  "puppeteer": {
89
93
  "optional": true
90
94
  },
package/readme.md CHANGED
@@ -62,7 +62,7 @@ or provider SDKs.
62
62
  | `/chat/cli` | Ink/React terminal chat | `ink ink-text-input react`; TypeScript: `@types/react` |
63
63
  | `/chat/web` | Lit `flexchat-window`, `flexchat-message`, `flexchat-input` | `lit` |
64
64
  | `/kvm` | Browser control, terminal framing and OCR observation | `puppeteer` |
65
- | `/documents` | PDF processing and `extractTextFromPdf` | `@push.rocks/smartpdf` |
65
+ | `/documents` | PDF and image documents, `extractTextFromPdf` | `@push.rocks/smartpdf`; for image documents `sharp` |
66
66
  | `/mcp` | MCP clients and AI SDK tool conversion | `@modelcontextprotocol/sdk` |
67
67
  | `/migration` | Existing versioned harness migrations | None |
68
68
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@modelprofile.com/flexharness',
6
- version: '9.7.0',
6
+ version: '9.9.0',
7
7
  description: 'The FlexHarness toolbox source repository and shared dependency catalog.'
8
8
  }
@@ -87,6 +87,7 @@ import type {
87
87
  IFlexSchedulePromptOptions,
88
88
  IFlexScopeSnapshot,
89
89
  IFlexSession,
90
+ IFlexSessionPermissionPolicyContext,
90
91
  IFlexSessionActivity,
91
92
  IFlexSessionGeneration,
92
93
  IFlexSessionGenerationCohortEntry,
@@ -155,6 +156,7 @@ import {
155
156
  } from './utils.prompt.js';
156
157
  import {
157
158
  maxSubagentNameBytes,
159
+ normalizeAllowedTools,
158
160
  normalizeAgentSessionPolicy,
159
161
  normalizeBuiltInTools,
160
162
  normalizeSlashCommands,
@@ -399,6 +401,9 @@ interface IActiveRun {
399
401
  reservedAssistantMessage?: IFlexMessage;
400
402
  modelResolution?: IFlexResolvedModel;
401
403
  subagents: ReadonlyMap<string, Readonly<IFlexSubagentDefinition>>;
404
+ allowedTools?: readonly string[];
405
+ agentAllowedTools?: readonly string[];
406
+ permissionPolicy?: IFlexResolvedSessionConfiguration<unknown>['permissionPolicy'];
402
407
  delegatedRunAdmissionOwner?: IDelegatedRunAdmissionOwner;
403
408
  delegatedRunAdmissionClose?: Promise<void>;
404
409
  completion: Promise<IFlexPromptResult>;
@@ -517,6 +522,8 @@ interface IActiveSlashCommandListing {
517
522
  interface IResolvedSessionConfiguration<TScope> {
518
523
  subagents: ReadonlyMap<string, Readonly<IFlexSubagentDefinition>>;
519
524
  slashCommands: ReadonlyMap<string, TRegisteredSlashCommand<TScope>>;
525
+ allowedTools?: readonly string[];
526
+ permissionPolicy?: IFlexResolvedSessionConfiguration<TScope>['permissionPolicy'];
520
527
  }
521
528
 
522
529
  interface ISessionConfigurationOwner<TScope> {
@@ -2046,11 +2053,16 @@ export class FlexHarness<TScope = unknown> {
2046
2053
  || Array.isArray(configuration)
2047
2054
  || ![Object.prototype, null].includes(Object.getPrototypeOf(configuration))
2048
2055
  || Reflect.ownKeys(configuration).some((name) =>
2049
- typeof name !== 'string' || !['subagents', 'slashCommands'].includes(name))
2056
+ typeof name !== 'string' || !['subagents', 'slashCommands', 'allowedTools', 'permissionPolicy'].includes(name))
2050
2057
  ) throw new FlexHarnessValidationError('Resolved session configuration is invalid.');
2058
+ if (configuration.permissionPolicy !== undefined && typeof configuration.permissionPolicy !== 'function') {
2059
+ throw new FlexHarnessValidationError('Resolved session permissionPolicy must be a function.');
2060
+ }
2051
2061
  return Object.freeze({
2052
2062
  subagents: normalizeSubagents(configuration.subagents),
2053
2063
  slashCommands: normalizeSlashCommands(configuration.slashCommands),
2064
+ allowedTools: normalizeAllowedTools(configuration.allowedTools),
2065
+ permissionPolicy: configuration.permissionPolicy,
2054
2066
  });
2055
2067
  }).catch((error: unknown) => {
2056
2068
  if (this.sessionConfigurations.get(key)?.completion === completion) {
@@ -5182,13 +5194,22 @@ export class FlexHarness<TScope = unknown> {
5182
5194
  signal: AbortSignal,
5183
5195
  ): Promise<plugins.IAgentGenerationLease> {
5184
5196
  signal.throwIfAborted();
5185
- run.subagents = (await this.resolveSessionConfiguration(
5197
+ const configuration = await this.resolveSessionConfiguration(
5186
5198
  run.scopeId,
5187
5199
  run.scope as TScope,
5188
5200
  run.state,
5189
5201
  run.stored,
5190
5202
  signal,
5191
- )).subagents;
5203
+ );
5204
+ run.subagents = configuration.subagents;
5205
+ run.allowedTools = configuration.allowedTools;
5206
+ run.permissionPolicy = configuration.permissionPolicy as IActiveRun['permissionPolicy'];
5207
+ const agent = run.stored.session.agent;
5208
+ if (agent !== undefined) {
5209
+ const definition = configuration.subagents.get(agent);
5210
+ if (!definition) throw new FlexHarnessValidationError(`Subagent "${agent}" is disabled.`);
5211
+ run.agentAllowedTools = definition.allowedTools;
5212
+ }
5192
5213
  await this.prepareRunReversion(run, signal);
5193
5214
  run.phase = 'running';
5194
5215
  const queued = run.stored.outstandingPromptsById.get(run.queueId);
@@ -5299,9 +5320,16 @@ export class FlexHarness<TScope = unknown> {
5299
5320
  const providedTools = toolHandle?.tools;
5300
5321
  const combinedTools: Record<string, unknown> = { ...(providedTools ?? {}) };
5301
5322
  Object.assign(combinedTools, this.createBuiltInTools(run));
5302
- tools = Object.keys(combinedTools).length > 0
5323
+ const globalNames = run.allowedTools === undefined ? undefined : new Set(run.allowedTools);
5324
+ const agentNames = run.agentAllowedTools === undefined ? undefined : new Set(run.agentAllowedTools);
5325
+ const admittedTools = globalNames === undefined && agentNames === undefined
5326
+ ? combinedTools
5327
+ : Object.fromEntries(Object.entries(combinedTools).filter(([name]) =>
5328
+ (globalNames === undefined || globalNames.has(name))
5329
+ && (agentNames === undefined || agentNames.has(name))));
5330
+ tools = Object.keys(admittedTools).length > 0
5303
5331
  ? wrapToolSet(
5304
- combinedTools as TFlexAgentToolSet,
5332
+ admittedTools as TFlexAgentToolSet,
5305
5333
  this.toolOutputLimits,
5306
5334
  (toolError) => this.projectExternalError(run, toolError, 'toolExecution'),
5307
5335
  )
@@ -7758,8 +7786,10 @@ export class FlexHarness<TScope = unknown> {
7758
7786
  if (state.activeRuns.get(run.sessionId) !== run || run.callbacksClosed) {
7759
7787
  throw new FlexHarnessPermissionStateError(`Run "${run.runId}" no longer accepts permission requests.`);
7760
7788
  }
7761
- if (input.rememberKey && run.stored.rememberedPermissionKeys.has(input.rememberKey)) return;
7762
- const request: IFlexPermissionRequest = {
7789
+ if (!run.permissionPolicy && input.rememberKey && run.stored.rememberedPermissionKeys.has(input.rememberKey)) {
7790
+ return;
7791
+ }
7792
+ const candidate: IFlexPermissionRequest = {
7763
7793
  permissionId: plugins.crypto.randomUUID(),
7764
7794
  scopeId: run.scopeId,
7765
7795
  sessionId: run.sessionId,
@@ -7776,6 +7806,33 @@ export class FlexHarness<TScope = unknown> {
7776
7806
  : { metadata: normalizeJsonValue(input.metadata, this.toolOutputLimits) }),
7777
7807
  createdAt: new Date().toISOString(),
7778
7808
  };
7809
+ const policyDecision = run.permissionPolicy
7810
+ ? await this.decideSessionPermissionPolicy(state, run, candidate)
7811
+ : 'inherit';
7812
+ if (policyDecision === 'allow' || policyDecision === 'deny') {
7813
+ this.emitEvent(run.scopeId, run.stored.session, {
7814
+ type: 'permission.resolved',
7815
+ runId: run.runId,
7816
+ request: publicSnapshot(candidate),
7817
+ decision: policyDecision === 'allow' ? 'once' : 'reject',
7818
+ origin: 'policy',
7819
+ });
7820
+ if (policyDecision === 'deny') {
7821
+ const rejection = this.trustInternalError(
7822
+ new FlexHarnessPermissionRejectedError(candidate.permissionId),
7823
+ );
7824
+ this.abortRunInternally(run, rejection);
7825
+ throw rejection;
7826
+ }
7827
+ return;
7828
+ }
7829
+ if (policyDecision === 'inherit'
7830
+ && input.rememberKey && run.stored.rememberedPermissionKeys.has(input.rememberKey)) return;
7831
+ const request: IFlexPermissionRequest = { ...candidate };
7832
+ if (policyDecision === 'ask') {
7833
+ delete request.rememberKey;
7834
+ request.requiresExplicitResponse = true;
7835
+ }
7779
7836
  let resolvePermission!: () => void;
7780
7837
  let rejectPermission!: (error: unknown) => void;
7781
7838
  const promise = new Promise<void>((resolve, reject) => {
@@ -7823,6 +7880,70 @@ export class FlexHarness<TScope = unknown> {
7823
7880
  return promise;
7824
7881
  }
7825
7882
 
7883
+ private async decideSessionPermissionPolicy(
7884
+ state: IStorageState,
7885
+ run: IActiveRun,
7886
+ request: IFlexPermissionRequest,
7887
+ ): Promise<'inherit' | 'allow' | 'ask' | 'deny'> {
7888
+ const policy = run.permissionPolicy!;
7889
+ const signal = run.controller.signal;
7890
+ const context: Readonly<IFlexSessionPermissionPolicyContext<unknown>> = Object.freeze({
7891
+ scopeId: run.scopeId,
7892
+ scope: run.scope,
7893
+ storageKey: state.storageKey,
7894
+ sessionId: run.sessionId,
7895
+ ...requireSessionGeneration(run.stored.session),
7896
+ runId: run.runId,
7897
+ configurationRef: run.stored.session.configurationRef!,
7898
+ ...(run.stored.session.agent === undefined ? {} : { agent: run.stored.session.agent }),
7899
+ request: publicSnapshot(request),
7900
+ signal,
7901
+ });
7902
+ let onAbort!: () => void;
7903
+ const aborted = new Promise<never>((_resolve, reject) => {
7904
+ onAbort = () => reject(signal.reason ?? this.trustInternalError(new FlexHarnessAbortError()));
7905
+ });
7906
+ signal.addEventListener('abort', onAbort, { once: true });
7907
+ if (signal.aborted) onAbort();
7908
+ let decision: unknown;
7909
+ try {
7910
+ decision = await Promise.race([
7911
+ Promise.resolve().then(() => {
7912
+ signal.throwIfAborted();
7913
+ return policy(context);
7914
+ }),
7915
+ aborted,
7916
+ ]);
7917
+ if (!['inherit', 'allow', 'ask', 'deny'].includes(decision as string)) {
7918
+ throw new FlexHarnessValidationError('Session permissionPolicy returned an invalid decision.');
7919
+ }
7920
+ } catch (error) {
7921
+ if (signal.aborted) throw signal.reason;
7922
+ const projected = this.projectExternalError(run, error, 'permissionPolicy');
7923
+ this.abortRunInternally(run, projected);
7924
+ throw projected;
7925
+ } finally {
7926
+ signal.removeEventListener('abort', onAbort);
7927
+ }
7928
+ signal.throwIfAborted();
7929
+ const generation = requireSessionGeneration(run.stored.session);
7930
+ if (
7931
+ state.lifecycle !== 'active'
7932
+ || state.activeRuns.get(run.sessionId) !== run
7933
+ || state.sessions.get(run.sessionId) !== run.stored
7934
+ || run.callbacksClosed
7935
+ || generation.sessionGenerationId !== request.sessionGenerationId
7936
+ || generation.sessionGenerationSequence !== request.sessionGenerationSequence
7937
+ ) {
7938
+ const ownershipError = this.trustInternalError(new FlexHarnessAbortError(
7939
+ 'Permission policy lost its exact run ownership.',
7940
+ ));
7941
+ this.abortRunInternally(run, ownershipError);
7942
+ throw ownershipError;
7943
+ }
7944
+ return decision as 'inherit' | 'allow' | 'ask' | 'deny';
7945
+ }
7946
+
7826
7947
  private async applyPermissionResponse(
7827
7948
  state: IStorageState,
7828
7949
  pending: IPendingPermission,