/**
 * UTF-16-aware slice helpers.
 *
 * `String.prototype.slice` operates on UTF-16 code units, not code points,
 * so cutting at an arbitrary index can split a surrogate pair (any non-BMP
 * character — most commonly emoji). The resulting lone surrogate survives
 * `JSON.stringify` as a literal `\uXXXX` escape, but is rejected by strict
 * JSON parsers — including the Anthropic API, which returns
 * "invalid high surrogate in string" and fails the request.
 *
 * These helpers shift the cut to a code-point boundary so each pair lands
 * wholly on one side of the slice. They do nothing extra when the boundary
 * is already clean, so it is safe to swap them in wherever `slice` is used
 * on user-controlled text that may travel through a JSON channel.
 */
/**
 * If `i` lands between the high and low half of a UTF-16 surrogate pair,
 * return `i - 1` so a `slice` at this boundary keeps the pair intact (it
 * ends up wholly on one side). Otherwise return `i` unchanged.
 */
export function alignCodePointBoundary(s, i) {
    if (i <= 0 || i >= s.length)
        return i;
    const prev = s.charCodeAt(i - 1);
    const curr = s.charCodeAt(i);
    if (prev >= 0xd800 && prev <= 0xdbff && curr >= 0xdc00 && curr <= 0xdfff) {
        return i - 1;
    }
    return i;
}
/** Like `s.slice(0, n)` but never splits a surrogate pair. */
export function safeSliceHead(s, n) {
    return s.slice(0, alignCodePointBoundary(s, n));
}
/** Like `s.slice(s.length - n)` but never splits a surrogate pair. */
export function safeSliceTail(s, n) {
    if (n <= 0)
        return "";
    if (n >= s.length)
        return s;
    return s.slice(alignCodePointBoundary(s, s.length - n));
}
//# sourceMappingURL=safe-slice.js.map