| 1 | import { RESOURCE_BUDGETS } from "./resourceBudgets"; |
| 2 | |
| 3 | export const TOOL_PREVIEW_MAX_BYTES = RESOURCE_BUDGETS.toolPreviewBytes; |
| 4 | export const TOOL_PREVIEW_MAX_BLOCKS = RESOURCE_BUDGETS.toolPreviewBlocks; |
| 5 | |
| 6 | const encoder = new TextEncoder(); |
| 7 | |
| 8 | export function utf8Prefix(text: string, maxBytes: number): string { |
| 9 | if (maxBytes <= 0) return ""; |
| 10 | if (encoder.encode(text).byteLength <= maxBytes) return text; |
| 11 | let low = 0, high = text.length; |
| 12 | while (low < high) { |
| 13 | const middle = Math.ceil((low + high) / 2); |
| 14 | if (encoder.encode(text.slice(0, middle)).byteLength <= maxBytes) low = middle; |
| 15 | else high = middle - 1; |
| 16 | } |
| 17 | if (low > 0 && /[\uD800-\uDBFF]/.test(text[low - 1])) low -= 1; |
| 18 | return text.slice(0, low); |
| 19 | } |
| 20 | |
| 21 | export function boundedPayloadSections(value: Record<string, unknown>): Array<{ key: string; body: string }> { |
| 22 | let remaining = TOOL_PREVIEW_MAX_BYTES; |
| 23 | const sections: Array<{ key: string; body: string }> = []; |
| 24 | for (const [key, content] of Object.entries(value).slice(0, TOOL_PREVIEW_MAX_BLOCKS)) { |
| 25 | if (content == null || remaining <= 0) continue; |
| 26 | const body = typeof content === "string" ? content : JSON.stringify(content, null, 2); |
| 27 | const shown = utf8Prefix(body, remaining); |
| 28 | remaining -= encoder.encode(shown).byteLength; |
| 29 | sections.push({ key, body: shown }); |
| 30 | } |
| 31 | return sections; |
| 32 | } |
| 33 |