Files
openclaw/src/shared/text-chunking.ts
Peter Steinberger 2aa4ff0825 refactor: consolidate duplicate helper exports (#106016)
* refactor(normalization): consolidate string helpers

* refactor(normalization): consolidate agent ids

* refactor(paths): consolidate user path resolution

* refactor(gateway): preserve transcript helper facade

* fix(normalization): align helper build aliases

* fix(memory): keep helper import line neutral

* chore(plugin-sdk): align consolidated helper surface

* refactor(normalization): reuse lowercase string helper

* refactor(normalization): reuse record guard

* refactor(normalization): share surrogate boundary helper

* refactor(agents): share token estimate constant

* refactor(memory): distinguish standalone helper contracts

* refactor(normalization): share error cause formatter

* refactor(config): distinguish eligibility predicates

* refactor(plugins): share registry state symbol

* refactor(cli): reuse outbound dependency adapter

* refactor(cron): distinguish positive duration parser

* fix(gateway): keep transcript helper private

* fix(paths): preserve public helper status

* refactor(channels): reuse registry normalizer

* refactor(agents): reuse agent core runtime facade

* refactor(auth): reuse locked profile upsert

* refactor(records): distinguish trap-safe guard

* refactor(migrations): distinguish sync directory helper

* test(plugins): drop stale duration alias expectations

* fix(channels): remove stale registry import

* chore(plugin-sdk): refresh helper API baseline

* fix(memory): keep renamed helpers private

* fix(plugins): keep registry state key private

* fix(plugin-sdk): tighten wildcard surface budget

* chore(plugin-sdk): refresh rebased helper API baseline
2026-07-13 03:22:00 -07:00

50 lines
1.8 KiB
TypeScript

import { avoidTrailingHighSurrogateBreak } from "@openclaw/normalization-core/utf16-slice";
export { avoidTrailingHighSurrogateBreak };
/**
* Splits text into bounded chunks using caller-owned soft-break selection.
*
* The resolver sees each limit-sized window and returns an in-window break index;
* invalid indexes fall back to the hard limit so chunking always makes progress.
*/
export function chunkTextByBreakResolver(
text: string,
limit: number,
resolveBreakIndex: (window: string) => number,
): string[] {
if (!text) {
return [];
}
if (limit <= 0 || text.length <= limit) {
return [text];
}
const chunks: string[] = [];
let remaining = text;
while (remaining.length > limit) {
const window = remaining.slice(0, limit);
const candidateBreak = resolveBreakIndex(window);
// Invalid or zero-width soft breaks would stall the loop, so fall back to the hard limit.
const breakIdx =
Number.isFinite(candidateBreak) && candidateBreak > 0 && candidateBreak <= limit
? candidateBreak
: limit;
const safeBreakIdx = avoidTrailingHighSurrogateBreak(remaining, 0, breakIdx);
const rawChunk = remaining.slice(0, safeBreakIdx);
const chunk = rawChunk.trimEnd();
if (chunk.length > 0) {
chunks.push(chunk);
}
// Keep separator ownership with the boundary: one matched separator is
// consumed here, and any adjacent whitespace is trimmed before the next window.
const brokeOnSeparator =
safeBreakIdx < remaining.length && /\s/.test(remaining.charAt(safeBreakIdx));
const nextStart = Math.min(remaining.length, safeBreakIdx + (brokeOnSeparator ? 1 : 0));
remaining = remaining.slice(nextStart).trimStart();
}
if (remaining.length) {
chunks.push(remaining);
}
return chunks;
}