@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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.flexharness.d.ts +1 -0
- package/dist_ts/classes.flexharness.js +115 -8
- package/dist_ts/interfaces.d.ts +23 -1
- package/dist_ts/interfaces.js +1 -1
- package/dist_ts/utils.harnessoptions.d.ts +1 -0
- package/dist_ts/utils.harnessoptions.js +30 -2
- package/dist_ts_document/index.d.ts +67 -2
- package/dist_ts_document/index.js +203 -21
- package/dist_ts_document/plugins.d.ts +35 -0
- package/dist_ts_document/plugins.js +6 -1
- package/package.json +7 -3
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.flexharness.ts +128 -7
- package/ts/interfaces.ts +26 -0
- package/ts/readme.md +1 -1
- package/ts/utils.harnessoptions.ts +31 -1
- package/ts_document/index.ts +310 -22
- package/ts_document/plugins.ts +35 -0
- package/ts_document/readme.md +39 -2
|
@@ -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
|
|
74
|
-
|
|
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 -
|
|
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(
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
...
|
|
315
|
+
...modelPages.map(page => ({
|
|
134
316
|
type: 'file',
|
|
135
|
-
data: page,
|
|
136
|
-
mediaType:
|
|
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:
|
|
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19kb2N1bWVudC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QywrR0FBK0c7QUFDL0csSUFBSSxhQUFhLEdBQThCLElBQUksQ0FBQztBQUVwRCxTQUFTLGNBQWM7SUFDckIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE1BQU0sUUFBUSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNqRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3BELGFBQWEsR0FBRyxLQUFLLENBQUM7UUFDdEIsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDZixJQUFJLGFBQWEsS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDNUIsYUFBYSxHQUFHLElBQUksQ0FBQztZQUN2QixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBQ0QsT0FBTyxhQUFhLENBQUM7QUFDdkIsQ0FBQztBQXlFRCwrRkFBK0Y7QUFDL0YsTUFBTSxPQUFPLHNCQUF1QixTQUFRLEtBQUs7SUFDL0M7Ozs7O09BS0c7SUFDYSxhQUFhLENBQVM7SUFDdEIsUUFBUSxDQUFTO0lBQ2pDLGdHQUFnRztJQUNoRixhQUFhLENBQVM7SUFFdEMsWUFBWSxPQUF1QyxFQUFFLE9BQXNCO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxhQUFhLEtBQUssQ0FBQztZQUN6QyxDQUFDLENBQUMsb0JBQW9CLE9BQU8sQ0FBQyxhQUFhLFFBQVE7WUFDbkQsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0I7Z0JBQzVCLENBQUMsQ0FBQyxzQkFBc0IsT0FBTyxDQUFDLGFBQWEsUUFBUTtnQkFDckQsQ0FBQyxDQUFDLCtCQUErQixPQUFPLENBQUMsYUFBYSxRQUFRLENBQUM7UUFDbkUsS0FBSyxDQUFDLEdBQUcsT0FBTyxtQkFBbUIsT0FBTyxDQUFDLFFBQVEsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3pFLElBQUksQ0FBQyxJQUFJLEdBQUcsd0JBQXdCLENBQUM7UUFDckMsSUFBSSxDQUFDLGFBQWEsR0FBRyxPQUFPLENBQUMsYUFBYSxDQUFDO1FBQzNDLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUNqQyxJQUFJLENBQUMsYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUM7SUFDN0MsQ0FBQztDQUNGO0FBRUQsTUFBTSwwQkFBMEIsR0FBRyw2RUFBNkUsQ0FBQztBQUNqSCxNQUFNLHdCQUF3QixHQUFHLG1KQUFtSixDQUFDO0FBRXJMLG9FQUFvRTtBQUNwRSxNQUFNLENBQUMsS0FBSyxVQUFVLGtCQUFrQixDQUN0QyxXQUF1QixFQUN2QixPQUFrQztJQUVsQyxPQUFPLENBQUMsTUFBTSwyQkFBMkIsQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7QUFDeEUsQ0FBQztBQUVELDJGQUEyRjtBQUMzRixNQUFNLENBQUMsS0FBSyxVQUFVLDJCQUEyQixDQUMvQyxXQUF1QixFQUN2QixPQUFrQztJQUVsQyxPQUFPLHlCQUF5QixDQUFDO1FBQy9CLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSztRQUNwQixhQUFhLEVBQUUsT0FBTyxDQUFDLGFBQWEsSUFBSSwwQkFBMEI7UUFDbEUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksd0JBQXdCO1FBQzVELFlBQVksRUFBRSxDQUFDLFdBQVcsQ0FBQztRQUMzQixjQUFjLEVBQUUsT0FBTyxDQUFDLGNBQWM7UUFDdEMsZUFBZSxFQUFFLE9BQU8sQ0FBQyxlQUFlO1FBQ3hDLFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVztRQUNoQyxRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7UUFDMUIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxPQUFPO0tBQ3pCLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxNQUFNLENBQUMsS0FBSyxVQUFVLGdCQUFnQixDQUFDLE9BQXlCO0lBQzlELE9BQU8sQ0FBQyxNQUFNLHlCQUF5QixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0FBQ3pELENBQUM7QUFFRCx5RkFBeUY7QUFDekYsTUFBTSxDQUFDLEtBQUssVUFBVSx5QkFBeUIsQ0FBQyxPQUF5QjtJQUN2RSxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO0lBQ2xDLElBQUksUUFBUSxLQUFLLFNBQVMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNoRixNQUFNLElBQUksVUFBVSxDQUFDLDJDQUEyQyxDQUFDLENBQUM7SUFDcEUsQ0FBQztJQUNELE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUM7SUFDeEMsV0FBVyxFQUFFLGNBQWMsRUFBRSxDQUFDO0lBRTlCLE1BQU0sVUFBVSxHQUFpQixFQUFFLENBQUM7SUFDcEMsS0FBSyxNQUFNLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztRQUMxRCxNQUFNLEdBQUcsR0FBRyxNQUFNLGNBQWMsRUFBRSxDQUFDO1FBQ25DLFdBQVcsRUFBRSxjQUFjLEVBQUUsQ0FBQztRQUM5QixNQUFNLFlBQVksR0FBRyxDQUFDLGFBQXFCLEVBQUUsS0FBYSxFQUFrQyxFQUFFLENBQUMsQ0FBQztZQUM5RixhQUFhO1lBQ2IsUUFBUSxFQUFFLEtBQUs7WUFDZixhQUFhLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxNQUFNO1lBQzFDLGFBQWEsRUFBRSxLQUFLO1lBQ3BCLG9CQUFvQixFQUFFLEtBQUssS0FBSyxPQUFPLENBQUMsWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDO1NBQ2hFLENBQUMsQ0FBQztRQUNILDZGQUE2RjtRQUM3RiwrRkFBK0Y7UUFDL0Ysb0dBQW9HO1FBQ3BHLE1BQU0sY0FBYyxHQUFHLFFBQVEsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN0RyxJQUFJLEtBQW1CLENBQUM7UUFDeEIsSUFBSSxDQUFDO1lBQ0gsS0FBSyxHQUFHLE1BQU0sR0FBRyxDQUFDLG9CQUFvQixDQUFDLEdBQUcsRUFBRSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUM7UUFDakcsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFFBQVEsS0FBSyxTQUFTLElBQUksS0FBSyxZQUFZLE9BQU8sQ0FBQyxRQUFRLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztnQkFDdkYsTUFBTSxJQUFJLHNCQUFzQixDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLEVBQUUsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUNsSCxDQUFDO1lBQ0QsTUFBTSxLQUFLLENBQUM7UUFDZCxDQUFDO1FBQ0QsV0FBVyxFQUFFLGNBQWMsRUFBRSxDQUFDO1FBQzlCLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxLQUFLLENBQUMsQ0FBQztRQUMxQixJQUFJLFFBQVEsS0FBSyxTQUFTLElBQUksVUFBVSxDQUFDLE1BQU0sR0FBRyxRQUFRLEVBQUUsQ0FBQztZQUMzRCxNQUFNLElBQUksc0JBQXNCLENBQUMsWUFBWSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUM5RSxDQUFDO0lBQ0gsQ0FBQztJQUVELDJCQUEyQjtJQUMzQixNQUFNLFVBQVUsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFFdEUsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQztJQUNoQyxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBTyxDQUFDLDJCQUEyQixDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUNwRyw2RkFBNkY7SUFDN0YsZ0dBQWdHO0lBQ2hHLE1BQU0sS0FBSyxHQUFHLFVBQVU7UUFDdEIsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQztZQUN4QixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsVUFBVSxFQUFFO2dCQUNWLFlBQVksRUFBRSxLQUFLLEVBQUUsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRTtvQkFDN0MsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO29CQUNuQixJQUFJLENBQUM7d0JBQ0gsT0FBTyxNQUFNLFVBQVUsRUFBRSxDQUFDO29CQUM1QixDQUFDO29CQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7d0JBQ2YsVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQzt3QkFDdEUsTUFBTSxLQUFLLENBQUM7b0JBQ2QsQ0FBQztnQkFDSCxDQUFDO2FBQ0Y7U0FDRixDQUFDO1FBQ0osQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7SUFDbEIsTUFBTSxRQUFRLEdBQTJCO1FBQ3ZDLEdBQUcsQ0FBQyxPQUFPLENBQUMsY0FBYyxJQUFJLEVBQUUsQ0FBQztRQUNqQztZQUNFLElBQUksRUFBRSxNQUFNO1lBQ1osT0FBTyxFQUFFO2dCQUNQLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLFdBQVcsRUFBRTtnQkFDM0MsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztvQkFDekIsSUFBSSxFQUFFLE1BQWU7b0JBQ3JCLElBQUksRUFBRSxJQUFJO29CQUNWLFNBQVMsRUFBRSxXQUFvQjtpQkFDaEMsQ0FBQyxDQUFDO2FBQ0o7U0FDRjtLQUNGLENBQUM7SUFDRixJQUFJLE1BQXdELENBQUM7SUFDN0QsSUFBSSxDQUFDO1FBQ0gsTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLFlBQVksQ0FBQztZQUNsQyxLQUFLO1lBQ0wsWUFBWSxFQUFFLE9BQU8sQ0FBQyxhQUFhO1lBQ25DLFFBQVE7WUFDUixlQUFlLEVBQUUsT0FBTyxDQUFDLGVBQWU7WUFDeEMsV0FBVztZQUNYLEdBQUcsQ0FBQyxVQUFVO2dCQUNaLENBQUMsQ0FBQztvQkFDRSxzQkFBc0IsRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO3dCQUN4RCxVQUFVLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQztvQkFDckMsQ0FBQztpQkFDRjtnQkFDSCxDQUFDLENBQUMsRUFBRSxDQUFDO1NBQ1IsQ0FBQyxDQUFDO1FBQ0gsVUFBVSxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNoQyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLFVBQVUsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNoRSxNQUFNLEtBQUssQ0FBQztJQUNkLENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDO0lBQ2hDLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxXQUFXLElBQUksQ0FBQyxDQUFDO0lBQzNDLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUksQ0FBQyxDQUFDO0lBQzdDLE9BQU87UUFDTCxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7UUFDakIsS0FBSyxFQUFFO1lBQ0wsV0FBVztZQUNYLFlBQVk7WUFDWixXQUFXLEVBQUUsV0FBVyxHQUFHLFlBQVk7WUFDdkMsZUFBZSxFQUFFLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxlQUFlLElBQUksQ0FBQztZQUM3RCxnQkFBZ0IsRUFBRSxLQUFLLENBQUMsaUJBQWlCLENBQUMsZ0JBQWdCLElBQUksQ0FBQztTQUNoRTtRQUNELFNBQVMsRUFBRSxVQUFVLENBQUMsTUFBTTtLQUM3QixDQUFDO0FBQ0osQ0FBQztBQUVELE1BQU0sQ0FBQyxLQUFLLFVBQVUsWUFBWTtJQUNoQyxNQUFNLEtBQUssR0FBRyxhQUFhLENBQUM7SUFDNUIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsT0FBTztJQUNULENBQUM7SUFDRCxtR0FBbUc7SUFDbkcsMERBQTBEO0lBQzFELGFBQWEsR0FBRyxJQUFJLENBQUM7SUFDckIsSUFBSSxRQUFtQixDQUFDO0lBQ3hCLElBQUksQ0FBQztRQUNILFFBQVEsR0FBRyxNQUFNLEtBQUssQ0FBQztJQUN6QixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsZ0VBQWdFO1FBQ2hFLE9BQU87SUFDVCxDQUFDO0lBQ0QsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDeEIsQ0FBQyJ9
|
|
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
|
-
|
|
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.
|
|
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.
|
|
66
|
-
"@modelprofile.com/flexharness-models": "9.
|
|
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
|
|
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
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
)
|
|
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
|
-
|
|
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
|
-
|
|
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))
|
|
7762
|
-
|
|
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,
|