mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-25 05:51:15 +00:00
* 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
50 lines
1.8 KiB
TypeScript
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;
|
|
}
|