@mrciphersmith/keryx 0.3.77 → 0.3.79
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/cli.js +1445 -128
- package/dist/core.js +1015 -59
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1757,13 +1757,367 @@ function isValidIp(value) {
|
|
|
1757
1757
|
}
|
|
1758
1758
|
return false;
|
|
1759
1759
|
}
|
|
1760
|
-
function
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1760
|
+
function isWordBoundary(flavour, content, index) {
|
|
1761
|
+
flavour.boundary.lastIndex = index;
|
|
1762
|
+
return flavour.boundary.test(content);
|
|
1763
|
+
}
|
|
1764
|
+
function wordCharBefore(content, index) {
|
|
1765
|
+
if (index <= 0) {
|
|
1766
|
+
return "";
|
|
1765
1767
|
}
|
|
1766
|
-
|
|
1768
|
+
const low = content.charCodeAt(index - 1);
|
|
1769
|
+
if (low >= 56320 && low <= 57343 && index >= 2) {
|
|
1770
|
+
const high = content.charCodeAt(index - 2);
|
|
1771
|
+
if (high >= 55296 && high <= 56319) {
|
|
1772
|
+
return content.slice(index - 2, index);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
return content[index - 1];
|
|
1776
|
+
}
|
|
1777
|
+
function wordCharAt(content, index) {
|
|
1778
|
+
const code = content.codePointAt(index);
|
|
1779
|
+
return code === undefined ? "" : String.fromCodePoint(code);
|
|
1780
|
+
}
|
|
1781
|
+
function execEmail(flavour, content, from) {
|
|
1782
|
+
let at = content.indexOf("@", from);
|
|
1783
|
+
while (at !== -1) {
|
|
1784
|
+
let start = at;
|
|
1785
|
+
while (start > from && flavour.localChar.test(wordCharBefore(content, start))) {
|
|
1786
|
+
start -= wordCharBefore(content, start).length;
|
|
1787
|
+
}
|
|
1788
|
+
while (start < at && !isWordBoundary(flavour, content, start)) {
|
|
1789
|
+
start += wordCharAt(content, start).length;
|
|
1790
|
+
}
|
|
1791
|
+
if (start < at) {
|
|
1792
|
+
flavour.from.lastIndex = start;
|
|
1793
|
+
const m = flavour.from.exec(content);
|
|
1794
|
+
if (m !== null) {
|
|
1795
|
+
return m;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
at = content.indexOf("@", at + 1);
|
|
1799
|
+
}
|
|
1800
|
+
return null;
|
|
1801
|
+
}
|
|
1802
|
+
function emailScannerFor(flavour) {
|
|
1803
|
+
return {
|
|
1804
|
+
lastIndex: 0,
|
|
1805
|
+
exec(content) {
|
|
1806
|
+
const m = execEmail(flavour, content, this.lastIndex);
|
|
1807
|
+
this.lastIndex = m === null ? 0 : m.index + m[0].length;
|
|
1808
|
+
return m;
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
function execPhone(content, from) {
|
|
1813
|
+
let pos = from;
|
|
1814
|
+
while (pos < content.length) {
|
|
1815
|
+
const digitAt = content[pos] === "+" ? pos + 1 : pos;
|
|
1816
|
+
if (!DIGIT.test(content[digitAt] ?? "") || pos > 0 && PHONE_START_BLOCKER.test(content[pos - 1])) {
|
|
1817
|
+
pos += 1;
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
PHONE_FROM.lastIndex = pos;
|
|
1821
|
+
const m = PHONE_FROM.exec(content);
|
|
1822
|
+
if (m !== null) {
|
|
1823
|
+
return m;
|
|
1824
|
+
}
|
|
1825
|
+
pos = digitAt + 1;
|
|
1826
|
+
while (pos < content.length && PHONE_RUN_CHAR.test(content[pos])) {
|
|
1827
|
+
pos += 1;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return null;
|
|
1831
|
+
}
|
|
1832
|
+
function phoneVerdict(content, start, end) {
|
|
1833
|
+
let digits = 0;
|
|
1834
|
+
for (let i = start;i < end && digits <= PHONE_MAX_DIGITS; i += 1) {
|
|
1835
|
+
if (DIGIT.test(content[i])) {
|
|
1836
|
+
digits += 1;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (digits < PHONE_MIN_DIGITS || digits > PHONE_MAX_DIGITS) {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
const value = content.slice(start, end);
|
|
1843
|
+
return hasPhoneSeparatorShape(value) && !containsCalendarDate(value) && !isIdentifierFragment(content, start, end);
|
|
1844
|
+
}
|
|
1845
|
+
function hasPhoneEvidence(value, singleToken) {
|
|
1846
|
+
return PHONE_SEPARATOR.test(value) || singleToken && value.includes(".") || PHONE_NANP_SPACED.test(value);
|
|
1847
|
+
}
|
|
1848
|
+
function phoneTokens(content, from, to) {
|
|
1849
|
+
const tokens = [];
|
|
1850
|
+
let i = from;
|
|
1851
|
+
while (i < to) {
|
|
1852
|
+
const gapStart = i;
|
|
1853
|
+
while (i < to && WHITESPACE.test(content[i])) {
|
|
1854
|
+
i += 1;
|
|
1855
|
+
}
|
|
1856
|
+
if (i >= to) {
|
|
1857
|
+
break;
|
|
1858
|
+
}
|
|
1859
|
+
const breakBefore = tokens.length > 0 && (i - gapStart >= 2 || LINE_BREAK.test(content.slice(gapStart, i)));
|
|
1860
|
+
const start = i;
|
|
1861
|
+
let digits = 0;
|
|
1862
|
+
let lastDigit = -1;
|
|
1863
|
+
let coreStart = -1;
|
|
1864
|
+
let leadDigits = 0;
|
|
1865
|
+
while (i < to && !WHITESPACE.test(content[i])) {
|
|
1866
|
+
const isDigit = DIGIT.test(content[i]);
|
|
1867
|
+
if (coreStart < 0) {
|
|
1868
|
+
const startsHere = (isDigit || content[i] === "+" && i + 1 < to && DIGIT.test(content[i + 1])) && (i === 0 || !PHONE_START_BLOCKER.test(content[i - 1]));
|
|
1869
|
+
if (startsHere) {
|
|
1870
|
+
coreStart = i;
|
|
1871
|
+
} else if (isDigit) {
|
|
1872
|
+
leadDigits += 1;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
if (isDigit) {
|
|
1876
|
+
digits += 1;
|
|
1877
|
+
lastDigit = i;
|
|
1878
|
+
}
|
|
1879
|
+
i += 1;
|
|
1880
|
+
}
|
|
1881
|
+
const text = content.slice(start, i);
|
|
1882
|
+
const groupable = (PHONE_DIAL_GROUP.test(text) || PHONE_FIRST_GROUP.test(text)) && !containsCalendarDate(text);
|
|
1883
|
+
tokens.push({ start, end: i, coreStart, coreEnd: lastDigit + 1, leadDigits, digits, breakBefore, groupable });
|
|
1884
|
+
}
|
|
1885
|
+
return tokens;
|
|
1886
|
+
}
|
|
1887
|
+
function rescanPhoneRun(content, from, to) {
|
|
1888
|
+
const tokens = phoneTokens(content, from, to);
|
|
1889
|
+
const found = [];
|
|
1890
|
+
const chainAfter = new Array(tokens.length).fill(0);
|
|
1891
|
+
for (let i = tokens.length - 2;i >= 0; i -= 1) {
|
|
1892
|
+
const next = tokens[i + 1];
|
|
1893
|
+
chainAfter[i] = next.groupable && !next.breakBefore ? chainAfter[i + 1] + next.digits : 0;
|
|
1894
|
+
}
|
|
1895
|
+
let first = 0;
|
|
1896
|
+
let resume = -1;
|
|
1897
|
+
while (first < tokens.length) {
|
|
1898
|
+
const head = tokens[first];
|
|
1899
|
+
if (head.coreStart < 0 || head.coreEnd <= head.coreStart) {
|
|
1900
|
+
first += 1;
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
const startsRun = first === 0 || first === resume || head.breakBefore || !tokens[first - 1].groupable;
|
|
1904
|
+
let longest = -1;
|
|
1905
|
+
let clean = -1;
|
|
1906
|
+
const consider = (last) => {
|
|
1907
|
+
const end = tokens[last].coreEnd;
|
|
1908
|
+
if (hasPhoneEvidence(content.slice(head.coreStart, end), last === first) && phoneVerdict(content, head.coreStart, end)) {
|
|
1909
|
+
longest = last;
|
|
1910
|
+
const rest = chainAfter[last];
|
|
1911
|
+
if (rest === 0 || rest >= PHONE_MIN_DIGITS) {
|
|
1912
|
+
clean = last;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
let digits = head.digits - head.leadDigits;
|
|
1917
|
+
if (digits >= PHONE_MIN_DIGITS) {
|
|
1918
|
+
consider(first);
|
|
1919
|
+
}
|
|
1920
|
+
const headGroup = content.slice(head.coreStart, head.end);
|
|
1921
|
+
let extendable = startsRun && (PHONE_FIRST_GROUP.test(headGroup) || PHONE_DIAL_GROUP.test(headGroup));
|
|
1922
|
+
for (let last = first + 1;last < tokens.length && extendable; last += 1) {
|
|
1923
|
+
const tail = tokens[last];
|
|
1924
|
+
digits += tail.digits;
|
|
1925
|
+
if (digits > PHONE_MAX_DIGITS || tail.digits === 0 || tail.breakBefore) {
|
|
1926
|
+
break;
|
|
1927
|
+
}
|
|
1928
|
+
if (digits >= PHONE_MIN_DIGITS && PHONE_DIAL_GROUP.test(content.slice(tail.start, tail.coreEnd))) {
|
|
1929
|
+
consider(last);
|
|
1930
|
+
}
|
|
1931
|
+
extendable = PHONE_DIAL_GROUP.test(content.slice(tail.start, tail.end));
|
|
1932
|
+
}
|
|
1933
|
+
const best = clean >= 0 ? clean : longest;
|
|
1934
|
+
if (best >= 0) {
|
|
1935
|
+
const value = content.slice(head.coreStart, tokens[best].coreEnd);
|
|
1936
|
+
found.push({ index: head.coreStart, 0: value, 1: value });
|
|
1937
|
+
first = best + 1;
|
|
1938
|
+
resume = first;
|
|
1939
|
+
} else {
|
|
1940
|
+
first += 1;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
return found;
|
|
1944
|
+
}
|
|
1945
|
+
function joinedPhones(content, from, to) {
|
|
1946
|
+
const found = [];
|
|
1947
|
+
let i = from;
|
|
1948
|
+
while (i < to) {
|
|
1949
|
+
while (i < to && WHITESPACE.test(content[i])) {
|
|
1950
|
+
i += 1;
|
|
1951
|
+
}
|
|
1952
|
+
let zoneEnd = i;
|
|
1953
|
+
let total = 0;
|
|
1954
|
+
while (zoneEnd < to && !WHITESPACE.test(content[zoneEnd])) {
|
|
1955
|
+
if (DIGIT.test(content[zoneEnd])) {
|
|
1956
|
+
total += 1;
|
|
1957
|
+
}
|
|
1958
|
+
zoneEnd += 1;
|
|
1959
|
+
}
|
|
1960
|
+
if (total > PHONE_MAX_DIGITS && !containsCalendarDate(content.slice(i, zoneEnd))) {
|
|
1961
|
+
joinedZone(content, i, zoneEnd, total, found);
|
|
1962
|
+
}
|
|
1963
|
+
i = zoneEnd;
|
|
1964
|
+
}
|
|
1965
|
+
return found;
|
|
1966
|
+
}
|
|
1967
|
+
function isJoinerAt(content, k, to) {
|
|
1968
|
+
const ch = content[k];
|
|
1969
|
+
return (ch === "-" || ch === ".") && k > 0 && DIGIT.test(content[k - 1]) && k + 1 < to && DIGIT.test(content[k + 1]);
|
|
1970
|
+
}
|
|
1971
|
+
function joinedZone(content, zoneStart, zoneEnd, total, found) {
|
|
1972
|
+
let s = zoneStart;
|
|
1973
|
+
let before = 0;
|
|
1974
|
+
for (;; ) {
|
|
1975
|
+
while (s < zoneEnd && !DIGIT.test(content[s]) && !(content[s] === "+" && s + 1 < zoneEnd && DIGIT.test(content[s + 1]))) {
|
|
1976
|
+
s += 1;
|
|
1977
|
+
}
|
|
1978
|
+
if (s >= zoneEnd) {
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
let digits = 0;
|
|
1982
|
+
let lastDigit = s;
|
|
1983
|
+
let open = true;
|
|
1984
|
+
const cuts = [];
|
|
1985
|
+
for (let k = s;k < zoneEnd && open; k += 1) {
|
|
1986
|
+
if (DIGIT.test(content[k])) {
|
|
1987
|
+
digits += 1;
|
|
1988
|
+
lastDigit = k;
|
|
1989
|
+
open = digits <= PHONE_MAX_DIGITS;
|
|
1990
|
+
} else if (digits >= PHONE_MIN_DIGITS && isJoinerAt(content, k, zoneEnd)) {
|
|
1991
|
+
cuts.push({ end: k, digits });
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
if (open && digits >= PHONE_MIN_DIGITS) {
|
|
1995
|
+
cuts.push({ end: lastDigit + 1, digits });
|
|
1996
|
+
}
|
|
1997
|
+
let pick = -1;
|
|
1998
|
+
for (let c = cuts.length - 1;c >= 0; c -= 1) {
|
|
1999
|
+
const cut = cuts[c];
|
|
2000
|
+
const piece = content.slice(s, cut.end);
|
|
2001
|
+
if (!PHONE_JOINED_PIECE.test(piece) || PHONE_CARD_SHAPE.test(piece) || !phoneVerdict(content, s, cut.end)) {
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
if (pick < 0) {
|
|
2005
|
+
pick = c;
|
|
2006
|
+
}
|
|
2007
|
+
const rest = total - before - cut.digits;
|
|
2008
|
+
if (rest === 0 || rest >= PHONE_MIN_DIGITS) {
|
|
2009
|
+
pick = c;
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
if (pick < 0) {
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
const cut = cuts[pick];
|
|
2017
|
+
const value = content.slice(s, cut.end);
|
|
2018
|
+
found.push({ index: s, 0: value, 1: value });
|
|
2019
|
+
before += cut.digits;
|
|
2020
|
+
s = cut.end + 1;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
function withJoined(joined, rescans) {
|
|
2024
|
+
if (joined.length === 0) {
|
|
2025
|
+
return rescans;
|
|
2026
|
+
}
|
|
2027
|
+
const merged = [];
|
|
2028
|
+
let cursor = 0;
|
|
2029
|
+
let pending = 0;
|
|
2030
|
+
for (const rescan of rescans) {
|
|
2031
|
+
const rescanEnd = rescan.index + (rescan[0] ?? "").length;
|
|
2032
|
+
while (cursor < joined.length && joined[cursor].index + (joined[cursor][0] ?? "").length <= rescan.index) {
|
|
2033
|
+
cursor += 1;
|
|
2034
|
+
}
|
|
2035
|
+
if (cursor < joined.length && joined[cursor].index < rescanEnd) {
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
while (pending < joined.length && joined[pending].index < rescan.index) {
|
|
2039
|
+
merged.push(joined[pending]);
|
|
2040
|
+
pending += 1;
|
|
2041
|
+
}
|
|
2042
|
+
merged.push(rescan);
|
|
2043
|
+
}
|
|
2044
|
+
while (pending < joined.length) {
|
|
2045
|
+
merged.push(joined[pending]);
|
|
2046
|
+
pending += 1;
|
|
2047
|
+
}
|
|
2048
|
+
return merged;
|
|
2049
|
+
}
|
|
2050
|
+
function phoneBaselineEnd(content, runEnd) {
|
|
2051
|
+
for (let e = runEnd;e > 0; e -= 1) {
|
|
2052
|
+
if (DIGIT.test(content[e - 1]) && (e >= content.length || !PHONE_START_BLOCKER.test(content[e]))) {
|
|
2053
|
+
return e;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
return -1;
|
|
2057
|
+
}
|
|
2058
|
+
function isIbanChar(code) {
|
|
2059
|
+
return code >= 65 && code <= 90 || code >= 48 && code <= 57;
|
|
2060
|
+
}
|
|
2061
|
+
function isAsciiWordChar(code) {
|
|
2062
|
+
return isIbanChar(code) || code >= 97 && code <= 122 || code === 95;
|
|
2063
|
+
}
|
|
2064
|
+
function ibanEnd(content, base, pos, groups) {
|
|
2065
|
+
const key = (pos - base) * (IBAN_MAX_GROUPS + 1) + groups;
|
|
2066
|
+
if (ibanFailed[key] === ibanStamp) {
|
|
2067
|
+
return -1;
|
|
2068
|
+
}
|
|
2069
|
+
if (groups < IBAN_MAX_GROUPS) {
|
|
2070
|
+
const from = content.charCodeAt(pos) === 32 ? pos + 1 : pos;
|
|
2071
|
+
let run = 0;
|
|
2072
|
+
while (run < 4 && isIbanChar(content.charCodeAt(from + run))) {
|
|
2073
|
+
run += 1;
|
|
2074
|
+
}
|
|
2075
|
+
for (let length = run;length >= 1; length -= 1) {
|
|
2076
|
+
const end = ibanEnd(content, base, from + length, groups + 1);
|
|
2077
|
+
if (end >= 0) {
|
|
2078
|
+
return end;
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
if (groups >= 2 && !isAsciiWordChar(content.charCodeAt(pos))) {
|
|
2083
|
+
return pos;
|
|
2084
|
+
}
|
|
2085
|
+
ibanFailed[key] = ibanStamp;
|
|
2086
|
+
return -1;
|
|
2087
|
+
}
|
|
2088
|
+
function isCardCandidate(value) {
|
|
2089
|
+
const separators = value.match(CARD_GROUP_SEPARATOR) ?? [];
|
|
2090
|
+
const first = separators[0];
|
|
2091
|
+
if (first === undefined) {
|
|
2092
|
+
return true;
|
|
2093
|
+
}
|
|
2094
|
+
if (!separators.every((separator) => separator === first)) {
|
|
2095
|
+
return false;
|
|
2096
|
+
}
|
|
2097
|
+
const lengths = value.split(first).map((group) => group.length);
|
|
2098
|
+
const last = lengths[lengths.length - 1];
|
|
2099
|
+
if (lengths.length === 3 && lengths[0] === 4 && lengths[1] === 6 && (last === 5 || last === 4)) {
|
|
2100
|
+
return true;
|
|
2101
|
+
}
|
|
2102
|
+
return lengths.slice(0, -1).every((length) => length === 4) && last >= 1 && last <= 7;
|
|
2103
|
+
}
|
|
2104
|
+
function hasNonHexGlue(content, start, end) {
|
|
2105
|
+
const before = GLUE_LETTER_BEFORE.exec(content.slice(Math.max(0, start - GLUE_FLANK), start))?.[0] ?? "";
|
|
2106
|
+
const after = GLUE_LETTER_AFTER.exec(content.slice(end, end + GLUE_FLANK))?.[0] ?? "";
|
|
2107
|
+
return before === "" && after === "" || NON_HEX_LETTER.test(before) || NON_HEX_LETTER.test(after);
|
|
2108
|
+
}
|
|
2109
|
+
function outsideUuid(content, start, end) {
|
|
2110
|
+
const from = Math.max(0, start - UUID_REACH);
|
|
2111
|
+
const window = content.slice(from, Math.min(content.length, end + UUID_REACH));
|
|
2112
|
+
UUID_ANYWHERE.lastIndex = 0;
|
|
2113
|
+
let m;
|
|
2114
|
+
while ((m = UUID_ANYWHERE.exec(window)) !== null) {
|
|
2115
|
+
if (from + m.index < end && from + m.index + m[0].length > start) {
|
|
2116
|
+
return false;
|
|
2117
|
+
}
|
|
2118
|
+
UUID_ANYWHERE.lastIndex = m.index + 1;
|
|
2119
|
+
}
|
|
2120
|
+
return true;
|
|
1767
2121
|
}
|
|
1768
2122
|
function containsCalendarDate(value) {
|
|
1769
2123
|
return CALENDAR_DATE.test(value);
|
|
@@ -1816,71 +2170,644 @@ function hasPhoneSeparatorShape(value) {
|
|
|
1816
2170
|
return /^\(?\d{2,4}\)?(?:[-.]\d{2,4})*$/.test(group);
|
|
1817
2171
|
});
|
|
1818
2172
|
}
|
|
1819
|
-
function
|
|
2173
|
+
function scanWith(content, rule, scanner, validate, accept) {
|
|
1820
2174
|
const matches = [];
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
2175
|
+
scanner.reset?.();
|
|
2176
|
+
scanner.lastIndex = 0;
|
|
2177
|
+
let m;
|
|
2178
|
+
while ((m = scanner.exec(content)) !== null) {
|
|
2179
|
+
const group = rule.valueGroup ?? 0;
|
|
2180
|
+
const value = m[group];
|
|
2181
|
+
if (value === undefined || value.length === 0) {
|
|
2182
|
+
continue;
|
|
2183
|
+
}
|
|
2184
|
+
if (validate && !validate(value)) {
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
const start = group === 0 ? m.index : m.index + m[0].indexOf(value);
|
|
2188
|
+
const end = start + value.length;
|
|
2189
|
+
if (rule.accept && !rule.accept(content, start, end) || accept && !accept(content, start, end)) {
|
|
2190
|
+
if (m.index === scanner.lastIndex) {
|
|
2191
|
+
scanner.lastIndex += 1;
|
|
2192
|
+
}
|
|
2193
|
+
continue;
|
|
2194
|
+
}
|
|
2195
|
+
const match = {
|
|
2196
|
+
category: "pii",
|
|
2197
|
+
policyId: rule.policyId,
|
|
2198
|
+
severity: rule.severity,
|
|
2199
|
+
confidence: rule.confidence,
|
|
2200
|
+
start,
|
|
2201
|
+
end,
|
|
2202
|
+
value,
|
|
2203
|
+
mask: rule.mask,
|
|
2204
|
+
remediation: "Redact personal data before persisting or publishing."
|
|
2205
|
+
};
|
|
2206
|
+
if (rescanned.has(m)) {
|
|
2207
|
+
rescanTag.add(match);
|
|
2208
|
+
}
|
|
2209
|
+
matches.push(match);
|
|
2210
|
+
if (m.index === scanner.lastIndex) {
|
|
2211
|
+
scanner.lastIndex += 1;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
return matches;
|
|
2215
|
+
}
|
|
2216
|
+
function scanRule(content, rule) {
|
|
2217
|
+
const matches = scanWith(content, rule, rule.regex, rule.validate, undefined);
|
|
2218
|
+
if (rule.also === undefined) {
|
|
2219
|
+
return matches;
|
|
2220
|
+
}
|
|
2221
|
+
let result = matches;
|
|
2222
|
+
for (const extra of rule.also) {
|
|
2223
|
+
const found = scanWith(content, rule, extra.regex, extra.validate ?? rule.validate, extra.accept);
|
|
2224
|
+
const added = found.filter((match) => !containedIn(result, match.start, match.end));
|
|
2225
|
+
if (added.length > 0) {
|
|
2226
|
+
result = coalesce(content, [...result, ...added].sort((x, y) => x.start - y.start || y.end - x.end));
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
return result;
|
|
2230
|
+
}
|
|
2231
|
+
function scanRules(content) {
|
|
2232
|
+
return RULES.map((rule) => scanRule(content, rule));
|
|
2233
|
+
}
|
|
2234
|
+
function decimalDigitValue(codePoint) {
|
|
2235
|
+
if (codePoint < 1632) {
|
|
2236
|
+
return -1;
|
|
2237
|
+
}
|
|
2238
|
+
const cached = digitValues.get(codePoint);
|
|
2239
|
+
if (cached !== undefined) {
|
|
2240
|
+
return cached;
|
|
2241
|
+
}
|
|
2242
|
+
let value = -1;
|
|
2243
|
+
if (DECIMAL_DIGIT.test(String.fromCodePoint(codePoint))) {
|
|
2244
|
+
let runStart = codePoint;
|
|
2245
|
+
while (runStart > 0 && DECIMAL_DIGIT.test(String.fromCodePoint(runStart - 1))) {
|
|
2246
|
+
runStart -= 1;
|
|
2247
|
+
}
|
|
2248
|
+
value = (codePoint - runStart) % 10;
|
|
2249
|
+
}
|
|
2250
|
+
digitValues.set(codePoint, value);
|
|
2251
|
+
return value;
|
|
2252
|
+
}
|
|
2253
|
+
function isHyphenLookalike(code) {
|
|
2254
|
+
return code >= 8208 && code <= 8213 || code === 8722 || code === 8315 || code === 8331 || code === 65112 || code === 65123 || code === 65293 || code === 1418 || code === 1470 || code === 6150 || code === 8259 || code === 12540 || code === 12448 || code === 11834 || code === 11835;
|
|
2255
|
+
}
|
|
2256
|
+
function isInvisible(code) {
|
|
2257
|
+
return code === 173 || code >= 8203 && code <= 8205 || code >= 8288 && code <= 8292 || code === 6158 || code === 847 || code === 8206 || code === 8207;
|
|
2258
|
+
}
|
|
2259
|
+
function foldNumberChar(code, options) {
|
|
2260
|
+
if (isInvisible(code)) {
|
|
2261
|
+
return code === 173 && options.shyHyphen ? "-" : "";
|
|
2262
|
+
}
|
|
2263
|
+
if (code === 160 || code === 5760 || code >= 8192 && code <= 8202 || code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279) {
|
|
2264
|
+
return " ";
|
|
2265
|
+
}
|
|
2266
|
+
if (isHyphenLookalike(code)) {
|
|
2267
|
+
return "-";
|
|
2268
|
+
}
|
|
2269
|
+
if (code === 8228 || code === 65106 || code === 65294 || code === 12290 || code === 65377) {
|
|
2270
|
+
return ".";
|
|
2271
|
+
}
|
|
2272
|
+
if (code === 65312 || code === 65131) {
|
|
2273
|
+
return "@";
|
|
2274
|
+
}
|
|
2275
|
+
if (code === 65291 || code === 8314 || code === 8330 || code === 65122) {
|
|
2276
|
+
return "+";
|
|
2277
|
+
}
|
|
2278
|
+
if (code === 65288 || code === 8317 || code === 8333 || code === 65113) {
|
|
2279
|
+
return "(";
|
|
2280
|
+
}
|
|
2281
|
+
if (code === 65289 || code === 8318 || code === 8334 || code === 65114) {
|
|
2282
|
+
return ")";
|
|
2283
|
+
}
|
|
2284
|
+
if (options.letters && code >= 65313 && code <= 65338) {
|
|
2285
|
+
return String.fromCharCode(code - 65313 + 65);
|
|
2286
|
+
}
|
|
2287
|
+
const digit = decimalDigitValue(code);
|
|
2288
|
+
return digit < 0 ? null : String(digit);
|
|
2289
|
+
}
|
|
2290
|
+
function isNumberBreak(content, from, to) {
|
|
2291
|
+
const before = content[from - 1];
|
|
2292
|
+
const after = content[to];
|
|
2293
|
+
return from > 1 && (before === "-" || before === ".") && DIGIT.test(content[from - 2]) && DIGIT.test(after ?? "") || from > 0 && DIGIT.test(before ?? "") && (after === "-" || after === ".") && DIGIT.test(content[to + 1] ?? "");
|
|
2294
|
+
}
|
|
2295
|
+
function normalizeWindow(content, from, to, options) {
|
|
2296
|
+
const origin = new Int32Array(to - from + 1);
|
|
2297
|
+
const parts = [];
|
|
2298
|
+
let length = 0;
|
|
2299
|
+
let flushed = from;
|
|
2300
|
+
let changed = false;
|
|
2301
|
+
for (let i = from;i < to; ) {
|
|
2302
|
+
const code = content.charCodeAt(i);
|
|
2303
|
+
if (code < 128) {
|
|
2304
|
+
if (options.breaks && (code === 9 || code === 10 || code === 13)) {
|
|
2305
|
+
let j = i + 1;
|
|
2306
|
+
while (j < to && BREAK_CHAR.test(content[j])) {
|
|
2307
|
+
j += 1;
|
|
2308
|
+
}
|
|
2309
|
+
if (isNumberBreak(content, i, j)) {
|
|
2310
|
+
changed = true;
|
|
2311
|
+
parts.push(content.slice(flushed, i));
|
|
2312
|
+
flushed = j;
|
|
2313
|
+
} else {
|
|
2314
|
+
for (let k = i;k < j; k += 1) {
|
|
2315
|
+
origin[length++] = k;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
i = j;
|
|
1828
2319
|
continue;
|
|
1829
2320
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2321
|
+
origin[length++] = i;
|
|
2322
|
+
i += 1;
|
|
2323
|
+
continue;
|
|
2324
|
+
}
|
|
2325
|
+
const codePoint = content.codePointAt(i);
|
|
2326
|
+
const width = codePoint > 65535 ? 2 : 1;
|
|
2327
|
+
const folded = foldNumberChar(codePoint, options);
|
|
2328
|
+
if (folded === null) {
|
|
2329
|
+
for (let k = 0;k < width; k += 1) {
|
|
2330
|
+
origin[length++] = i;
|
|
2331
|
+
}
|
|
2332
|
+
} else {
|
|
2333
|
+
changed = true;
|
|
2334
|
+
parts.push(content.slice(flushed, i));
|
|
2335
|
+
flushed = i + width;
|
|
2336
|
+
if (folded !== "") {
|
|
2337
|
+
parts.push(folded);
|
|
2338
|
+
origin[length++] = i;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
i += width;
|
|
2342
|
+
}
|
|
2343
|
+
if (!changed) {
|
|
2344
|
+
return null;
|
|
2345
|
+
}
|
|
2346
|
+
parts.push(content.slice(flushed, to));
|
|
2347
|
+
origin[length] = to;
|
|
2348
|
+
return { text: parts.join(""), origin: origin.subarray(0, length + 1) };
|
|
2349
|
+
}
|
|
2350
|
+
function toOriginal(window, content, start, end) {
|
|
2351
|
+
const last = window.origin[end - 1];
|
|
2352
|
+
const width = content.codePointAt(last) > 65535 ? 2 : 1;
|
|
2353
|
+
return [window.origin[start], last + width];
|
|
2354
|
+
}
|
|
2355
|
+
function containedIn(spans, start, end) {
|
|
2356
|
+
let low = 0;
|
|
2357
|
+
let high = spans.length - 1;
|
|
2358
|
+
while (low <= high) {
|
|
2359
|
+
const mid = low + high >> 1;
|
|
2360
|
+
if (spans[mid].start <= start) {
|
|
2361
|
+
low = mid + 1;
|
|
2362
|
+
} else {
|
|
2363
|
+
high = mid - 1;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
const candidate = spans[high];
|
|
2367
|
+
return candidate !== undefined && candidate.start <= start && candidate.end >= end;
|
|
2368
|
+
}
|
|
2369
|
+
function coalesce(content, spans) {
|
|
2370
|
+
const merged = [];
|
|
2371
|
+
for (const span of spans) {
|
|
2372
|
+
const last = merged[merged.length - 1];
|
|
2373
|
+
if (last === undefined || span.start >= last.end) {
|
|
2374
|
+
merged.push(span);
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
const end = Math.max(last.end, span.end);
|
|
2378
|
+
const union = { ...last, end, value: content.slice(last.start, end) };
|
|
2379
|
+
if (rescanTag.has(last) && rescanTag.has(span)) {
|
|
2380
|
+
rescanTag.add(union);
|
|
2381
|
+
}
|
|
2382
|
+
merged[merged.length - 1] = union;
|
|
2383
|
+
}
|
|
2384
|
+
return merged;
|
|
2385
|
+
}
|
|
2386
|
+
function isUniformCard(card) {
|
|
2387
|
+
const separators = new Set(card.value.replace(/\d/g, ""));
|
|
2388
|
+
return separators.size <= 1;
|
|
2389
|
+
}
|
|
2390
|
+
function dropRescansInsideCards(spans, cards) {
|
|
2391
|
+
if (cards.length === 0 || !spans.some((span) => rescanTag.has(span))) {
|
|
2392
|
+
return spans;
|
|
2393
|
+
}
|
|
2394
|
+
const sorted = cards.filter(isUniformCard).sort((a, b) => a.start - b.start);
|
|
2395
|
+
const reach = [];
|
|
2396
|
+
let farthest = 0;
|
|
2397
|
+
for (const card of sorted) {
|
|
2398
|
+
farthest = Math.max(farthest, card.end);
|
|
2399
|
+
reach.push(farthest);
|
|
2400
|
+
}
|
|
2401
|
+
return spans.filter((span) => {
|
|
2402
|
+
if (!rescanTag.has(span)) {
|
|
2403
|
+
return true;
|
|
2404
|
+
}
|
|
2405
|
+
let low = 0;
|
|
2406
|
+
let high = sorted.length - 1;
|
|
2407
|
+
while (low <= high) {
|
|
2408
|
+
const mid = low + high >> 1;
|
|
2409
|
+
if (sorted[mid].start < span.end) {
|
|
2410
|
+
low = mid + 1;
|
|
2411
|
+
} else {
|
|
2412
|
+
high = mid - 1;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
return high < 0 || reach[high] <= span.start;
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
function viewsOf(content, nonAscii) {
|
|
2419
|
+
const views = [];
|
|
2420
|
+
if (nonAscii) {
|
|
2421
|
+
views.push({});
|
|
2422
|
+
if (FULLWIDTH_CAPITAL.test(content)) {
|
|
2423
|
+
views.push({ letters: true });
|
|
2424
|
+
}
|
|
2425
|
+
if (SOFT_HYPHEN.test(content)) {
|
|
2426
|
+
views.push({ shyHyphen: true });
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
if (NUMBER_BREAK.test(content)) {
|
|
2430
|
+
views.push({ breaks: true });
|
|
2431
|
+
}
|
|
2432
|
+
return views;
|
|
2433
|
+
}
|
|
2434
|
+
function detectPii(content) {
|
|
2435
|
+
const perRule = scanRules(content);
|
|
2436
|
+
const nonAscii = NON_ASCII.test(content);
|
|
2437
|
+
if (nonAscii) {
|
|
2438
|
+
const emailRule = RULES[0];
|
|
2439
|
+
const ascii = scanRule(content, { ...emailRule, regex: asciiEmailScanner });
|
|
2440
|
+
const unicode = perRule[0];
|
|
2441
|
+
const extraEmails = ascii.filter((match) => !containedIn(unicode, match.start, match.end));
|
|
2442
|
+
if (extraEmails.length > 0) {
|
|
2443
|
+
perRule[0] = [...unicode, ...extraEmails].sort((x, y) => x.start - y.start || y.end - x.end);
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
const views = viewsOf(content, nonAscii);
|
|
2447
|
+
for (const options of views) {
|
|
2448
|
+
for (let from = 0;from < content.length; ) {
|
|
2449
|
+
let start = from;
|
|
2450
|
+
let to = Math.min(content.length, from + NORMALIZE_CHUNK);
|
|
2451
|
+
if (start > 0 && (content.charCodeAt(start) & 64512) === 56320) {
|
|
2452
|
+
start -= 1;
|
|
2453
|
+
}
|
|
2454
|
+
if (to < content.length && (content.charCodeAt(to - 1) & 64512) === 55296) {
|
|
2455
|
+
to += 1;
|
|
2456
|
+
}
|
|
2457
|
+
const window = normalizeWindow(content, start, to, options);
|
|
2458
|
+
if (window !== null) {
|
|
2459
|
+
const extra = scanRules(window.text);
|
|
2460
|
+
extra.forEach((found, index) => {
|
|
2461
|
+
if (options.breaks && !NUMBER_RULES.has(index)) {
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
const numbers = NUMBER_RULES.has(index);
|
|
2465
|
+
const base = perRule[index];
|
|
2466
|
+
const added = [];
|
|
2467
|
+
for (const match of found) {
|
|
2468
|
+
const [a, b] = toOriginal(window, content, match.start, match.end);
|
|
2469
|
+
if (!numbers && b - a !== match.end - match.start) {
|
|
2470
|
+
continue;
|
|
2471
|
+
}
|
|
2472
|
+
if (start > 0 && a < start + NORMALIZE_SKIP) {
|
|
2473
|
+
continue;
|
|
2474
|
+
}
|
|
2475
|
+
if (!containedIn(base, a, b) && !containedIn(added, a, b)) {
|
|
2476
|
+
const copy = { ...match, start: a, end: b, value: content.slice(a, b) };
|
|
2477
|
+
if (rescanTag.has(match)) {
|
|
2478
|
+
rescanTag.add(copy);
|
|
2479
|
+
}
|
|
2480
|
+
added.push(copy);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
if (added.length > 0) {
|
|
2484
|
+
perRule[index] = coalesce(content, [...base, ...added].sort((x, y) => x.start - y.start || y.end - x.end));
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
if (to >= content.length) {
|
|
2489
|
+
break;
|
|
2490
|
+
}
|
|
2491
|
+
from = to - NORMALIZE_OVERLAP;
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
perRule[PHONE_RULE] = dropRescansInsideCards(perRule[PHONE_RULE], perRule[CARD_RULE]);
|
|
2495
|
+
return perRule.flat();
|
|
2496
|
+
}
|
|
2497
|
+
var UNICODE_EMAIL, ASCII_EMAIL, emailScanner, asciiEmailScanner, PHONE_FROM, PHONE_RUN_CHAR, PHONE_START_BLOCKER, DIGIT, PHONE_DIAL_GROUP, PHONE_FIRST_GROUP, PHONE_SEPARATOR, PHONE_NANP_SPACED, WHITESPACE, LINE_BREAK, PHONE_MAX_DIGITS = 15, PHONE_MIN_DIGITS = 9, rescanned, PHONE_JOINED_PIECE, PHONE_CARD_SHAPE, PHONE_GLUE, PHONE_GLUE_LETTER, PHONE_GLUE_EVIDENCE, phoneGlueScanner, phoneScanner, IBAN_START, IBAN_MAX_GROUPS = 8, IBAN_STATES, ibanFailed, ibanStamp = 0, ibanScanner, IBAN_LENGTHS, IBAN_GLUE_START, ibanGluedScanner, CARD_SEPARATOR = "[ \\t\\r\\n._/-]", CARD_EXTRA, CARD_GROUP_SEPARATOR, GLUE_LETTER_BEFORE, GLUE_LETTER_AFTER, NON_HEX_LETTER, GLUE_FLANK = 8, UUID_ANYWHERE, UUID_REACH = 36, SSN_BREAK_EXTRA, IPV4_EXTRA, RULES, CALENDAR_DATE, IDENTIFIER_CHAR, TOKEN_SCAN_LIMIT = 64, UUID_TOKEN, rescanTag, NORMALIZE_CHUNK, NORMALIZE_OVERLAP = 512, NORMALIZE_SKIP = 256, NON_ASCII, DECIMAL_DIGIT, digitValues, BREAK_CHAR, PHONE_RULE, CARD_RULE, NUMBER_RULES, FULLWIDTH_CAPITAL, SOFT_HYPHEN, NUMBER_BREAK;
|
|
2498
|
+
var init_pii = __esm(() => {
|
|
2499
|
+
UNICODE_EMAIL = {
|
|
2500
|
+
from: /(?:(?<=[\p{L}\p{N}\p{M}_])(?![\p{L}\p{N}\p{M}_])|(?<![\p{L}\p{N}\p{M}_])(?=[\p{L}\p{N}\p{M}_]))[\p{L}\p{N}\p{M}._%+-]+@[\p{L}\p{N}\p{M}.-]+\.[\p{L}\p{M}]{2,}(?![\p{L}\p{N}\p{M}_])/uy,
|
|
2501
|
+
localChar: /[\p{L}\p{N}\p{M}._%+-]/u,
|
|
2502
|
+
boundary: /(?:(?<=[\p{L}\p{N}\p{M}_])(?![\p{L}\p{N}\p{M}_])|(?<![\p{L}\p{N}\p{M}_])(?=[\p{L}\p{N}\p{M}_]))/uy
|
|
2503
|
+
};
|
|
2504
|
+
ASCII_EMAIL = {
|
|
2505
|
+
from: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/y,
|
|
2506
|
+
localChar: /[A-Za-z0-9._%+-]/,
|
|
2507
|
+
boundary: /\b/y
|
|
2508
|
+
};
|
|
2509
|
+
emailScanner = emailScannerFor(UNICODE_EMAIL);
|
|
2510
|
+
asciiEmailScanner = emailScannerFor(ASCII_EMAIL);
|
|
2511
|
+
PHONE_FROM = /(?<![\w.])(\+?\d[\d\s().-]{7,}\d)(?!\w|\.\w)/y;
|
|
2512
|
+
PHONE_RUN_CHAR = /[\d\s().-]/;
|
|
2513
|
+
PHONE_START_BLOCKER = /[\w.]/;
|
|
2514
|
+
DIGIT = /\d/;
|
|
2515
|
+
PHONE_DIAL_GROUP = /^\(?\d{2,4}\)?(?:[-.]\d{2,4})*$/;
|
|
2516
|
+
PHONE_FIRST_GROUP = /^\+?\d{1,4}$/;
|
|
2517
|
+
PHONE_SEPARATOR = /[+()-]/;
|
|
2518
|
+
PHONE_NANP_SPACED = /^(?:\d{1,3} )?\d{3} \d{3} \d{4}$/;
|
|
2519
|
+
WHITESPACE = /\s/;
|
|
2520
|
+
LINE_BREAK = /[\n\r\u2028\u2029]/;
|
|
2521
|
+
rescanned = new WeakSet;
|
|
2522
|
+
PHONE_JOINED_PIECE = /^\+?\d{1,4}(?:[-.]\d{1,4})+$/;
|
|
2523
|
+
PHONE_CARD_SHAPE = /^\d{4}(?:[-.]\d{4})+$/;
|
|
2524
|
+
PHONE_GLUE = /(?<![\d.])\+?\d[\d().-]{7,}\d/g;
|
|
2525
|
+
PHONE_GLUE_LETTER = /[A-Za-z_]/;
|
|
2526
|
+
PHONE_GLUE_EVIDENCE = /[+()-]|\./;
|
|
2527
|
+
phoneGlueScanner = {
|
|
2528
|
+
lastIndex: 0,
|
|
2529
|
+
exec(content) {
|
|
2530
|
+
PHONE_GLUE.lastIndex = this.lastIndex;
|
|
2531
|
+
let m;
|
|
2532
|
+
while ((m = PHONE_GLUE.exec(content)) !== null) {
|
|
2533
|
+
const start = m.index;
|
|
2534
|
+
const end = start + m[0].length;
|
|
2535
|
+
const glued = start > 0 && PHONE_GLUE_LETTER.test(content[start - 1]) || end < content.length && PHONE_GLUE_LETTER.test(content[end]);
|
|
2536
|
+
if (glued && PHONE_GLUE_EVIDENCE.test(m[0]) && phoneVerdict(content, start, end)) {
|
|
2537
|
+
this.lastIndex = end;
|
|
2538
|
+
return { index: start, 0: m[0], 1: m[0] };
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
this.lastIndex = 0;
|
|
2542
|
+
return null;
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
phoneScanner = {
|
|
2546
|
+
lastIndex: 0,
|
|
2547
|
+
pending: [],
|
|
2548
|
+
cursor: 0,
|
|
2549
|
+
runEnd: -1,
|
|
2550
|
+
runBaseline: -1,
|
|
2551
|
+
tailEnd: 0,
|
|
2552
|
+
reset() {
|
|
2553
|
+
this.lastIndex = 0;
|
|
2554
|
+
this.pending = [];
|
|
2555
|
+
this.cursor = 0;
|
|
2556
|
+
this.runEnd = -1;
|
|
2557
|
+
this.runBaseline = -1;
|
|
2558
|
+
this.tailEnd = 0;
|
|
2559
|
+
},
|
|
2560
|
+
tail(content) {
|
|
2561
|
+
for (let pos = this.lastIndex;pos < this.tailEnd; pos += 1) {
|
|
2562
|
+
if (!DIGIT.test(content[pos]) || PHONE_START_BLOCKER.test(content[pos - 1])) {
|
|
1833
2563
|
continue;
|
|
1834
2564
|
}
|
|
1835
|
-
if (
|
|
1836
|
-
|
|
2565
|
+
if (this.tailEnd - pos < 9) {
|
|
2566
|
+
break;
|
|
1837
2567
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
2568
|
+
const value = content.slice(pos, this.tailEnd);
|
|
2569
|
+
return { index: pos, 0: value, 1: value };
|
|
2570
|
+
}
|
|
2571
|
+
this.lastIndex = this.tailEnd;
|
|
2572
|
+
this.tailEnd = 0;
|
|
2573
|
+
return null;
|
|
2574
|
+
},
|
|
2575
|
+
prefix(content, start, end) {
|
|
2576
|
+
if (this.runEnd !== end) {
|
|
2577
|
+
this.runEnd = end;
|
|
2578
|
+
this.runBaseline = phoneBaselineEnd(content, end);
|
|
2579
|
+
}
|
|
2580
|
+
let digits = 0;
|
|
2581
|
+
let limit = end;
|
|
2582
|
+
const ends = [];
|
|
2583
|
+
for (let i = start;i < end; i += 1) {
|
|
2584
|
+
const ch = content[i];
|
|
2585
|
+
if (DIGIT.test(ch)) {
|
|
2586
|
+
digits += 1;
|
|
2587
|
+
if (digits > PHONE_MAX_DIGITS) {
|
|
2588
|
+
limit = i;
|
|
2589
|
+
break;
|
|
2590
|
+
}
|
|
2591
|
+
} else if (WHITESPACE.test(ch) && DIGIT.test(content[i - 1]) && digits >= PHONE_MIN_DIGITS) {
|
|
2592
|
+
ends.push(i);
|
|
1840
2593
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
2594
|
+
}
|
|
2595
|
+
const baseline = this.runBaseline;
|
|
2596
|
+
if (baseline < end && baseline >= start + 9 && baseline <= limit && phoneVerdict(content, start, baseline)) {
|
|
2597
|
+
return { end: baseline, baseline: true };
|
|
2598
|
+
}
|
|
2599
|
+
for (let k = ends.length - 1;k >= 0; k -= 1) {
|
|
2600
|
+
const e = ends[k];
|
|
2601
|
+
const value = content.slice(start, e);
|
|
2602
|
+
if (e !== baseline && hasPhoneEvidence(value, !WHITESPACE.test(value)) && phoneVerdict(content, start, e)) {
|
|
2603
|
+
return { end: e, baseline: false };
|
|
1843
2604
|
}
|
|
1844
2605
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
2606
|
+
return null;
|
|
2607
|
+
},
|
|
2608
|
+
exec(content) {
|
|
2609
|
+
for (;; ) {
|
|
2610
|
+
if (this.cursor < this.pending.length) {
|
|
2611
|
+
return this.pending[this.cursor++];
|
|
2612
|
+
}
|
|
2613
|
+
this.pending = [];
|
|
2614
|
+
this.cursor = 0;
|
|
2615
|
+
let m;
|
|
2616
|
+
if (this.tailEnd > this.lastIndex) {
|
|
2617
|
+
m = this.tail(content);
|
|
2618
|
+
if (m === null) {
|
|
2619
|
+
continue;
|
|
2620
|
+
}
|
|
2621
|
+
} else {
|
|
2622
|
+
m = execPhone(content, this.lastIndex);
|
|
2623
|
+
if (m === null) {
|
|
2624
|
+
this.lastIndex = 0;
|
|
2625
|
+
return null;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
const end = m.index + (m[0] ?? "").length;
|
|
2629
|
+
this.lastIndex = end;
|
|
2630
|
+
this.tailEnd = 0;
|
|
2631
|
+
if (phoneVerdict(content, m.index, end)) {
|
|
2632
|
+
return m;
|
|
2633
|
+
}
|
|
2634
|
+
const prefix = this.prefix(content, m.index, end);
|
|
2635
|
+
if (prefix !== null) {
|
|
2636
|
+
const value = content.slice(m.index, prefix.end);
|
|
2637
|
+
const found = { index: m.index, 0: value, 1: value };
|
|
2638
|
+
if (!prefix.baseline) {
|
|
2639
|
+
rescanned.add(found);
|
|
2640
|
+
}
|
|
2641
|
+
this.lastIndex = prefix.end;
|
|
2642
|
+
this.tailEnd = end;
|
|
2643
|
+
return found;
|
|
2644
|
+
}
|
|
2645
|
+
this.pending = withJoined(joinedPhones(content, m.index, end), rescanPhoneRun(content, m.index, end));
|
|
2646
|
+
for (const found of this.pending) {
|
|
2647
|
+
rescanned.add(found);
|
|
2648
|
+
}
|
|
1847
2649
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2650
|
+
}
|
|
2651
|
+
};
|
|
2652
|
+
IBAN_START = /(?<!\w)[A-Z]{2}\d{2}/g;
|
|
2653
|
+
IBAN_STATES = (4 + IBAN_MAX_GROUPS * 5 + 1) * (IBAN_MAX_GROUPS + 1);
|
|
2654
|
+
ibanFailed = new Uint32Array(IBAN_STATES);
|
|
2655
|
+
ibanScanner = {
|
|
2656
|
+
lastIndex: 0,
|
|
2657
|
+
exec(content) {
|
|
2658
|
+
IBAN_START.lastIndex = this.lastIndex;
|
|
2659
|
+
let m;
|
|
2660
|
+
while ((m = IBAN_START.exec(content)) !== null) {
|
|
2661
|
+
ibanStamp += 1;
|
|
2662
|
+
if (ibanStamp === 4294967295) {
|
|
2663
|
+
ibanFailed.fill(0);
|
|
2664
|
+
ibanStamp = 1;
|
|
2665
|
+
}
|
|
2666
|
+
const end = ibanEnd(content, m.index, m.index + 4, 0);
|
|
2667
|
+
if (end >= 0) {
|
|
2668
|
+
this.lastIndex = end;
|
|
2669
|
+
return { index: m.index, 0: content.slice(m.index, end) };
|
|
2670
|
+
}
|
|
2671
|
+
IBAN_START.lastIndex = m.index + 1;
|
|
1862
2672
|
}
|
|
2673
|
+
this.lastIndex = 0;
|
|
2674
|
+
return null;
|
|
1863
2675
|
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2676
|
+
};
|
|
2677
|
+
IBAN_LENGTHS = {
|
|
2678
|
+
AD: 24,
|
|
2679
|
+
AE: 23,
|
|
2680
|
+
AL: 28,
|
|
2681
|
+
AT: 20,
|
|
2682
|
+
AZ: 28,
|
|
2683
|
+
BA: 20,
|
|
2684
|
+
BE: 16,
|
|
2685
|
+
BG: 22,
|
|
2686
|
+
BH: 22,
|
|
2687
|
+
BR: 29,
|
|
2688
|
+
BY: 28,
|
|
2689
|
+
CH: 21,
|
|
2690
|
+
CR: 22,
|
|
2691
|
+
CY: 28,
|
|
2692
|
+
CZ: 24,
|
|
2693
|
+
DE: 22,
|
|
2694
|
+
DK: 18,
|
|
2695
|
+
DO: 28,
|
|
2696
|
+
EE: 20,
|
|
2697
|
+
EG: 29,
|
|
2698
|
+
ES: 24,
|
|
2699
|
+
FI: 18,
|
|
2700
|
+
FO: 18,
|
|
2701
|
+
FR: 27,
|
|
2702
|
+
GB: 22,
|
|
2703
|
+
GE: 22,
|
|
2704
|
+
GI: 23,
|
|
2705
|
+
GL: 18,
|
|
2706
|
+
GR: 27,
|
|
2707
|
+
GT: 28,
|
|
2708
|
+
HR: 21,
|
|
2709
|
+
HU: 28,
|
|
2710
|
+
IE: 22,
|
|
2711
|
+
IL: 23,
|
|
2712
|
+
IQ: 23,
|
|
2713
|
+
IS: 26,
|
|
2714
|
+
IT: 27,
|
|
2715
|
+
JO: 30,
|
|
2716
|
+
KW: 30,
|
|
2717
|
+
KZ: 20,
|
|
2718
|
+
LB: 28,
|
|
2719
|
+
LC: 32,
|
|
2720
|
+
LI: 21,
|
|
2721
|
+
LT: 20,
|
|
2722
|
+
LU: 20,
|
|
2723
|
+
LV: 21,
|
|
2724
|
+
MC: 27,
|
|
2725
|
+
MD: 24,
|
|
2726
|
+
ME: 22,
|
|
2727
|
+
MK: 19,
|
|
2728
|
+
MR: 27,
|
|
2729
|
+
MT: 31,
|
|
2730
|
+
MU: 30,
|
|
2731
|
+
NL: 18,
|
|
2732
|
+
NO: 15,
|
|
2733
|
+
PK: 24,
|
|
2734
|
+
PL: 28,
|
|
2735
|
+
PS: 29,
|
|
2736
|
+
PT: 25,
|
|
2737
|
+
QA: 29,
|
|
2738
|
+
RO: 24,
|
|
2739
|
+
RS: 22,
|
|
2740
|
+
SA: 24,
|
|
2741
|
+
SC: 31,
|
|
2742
|
+
SE: 24,
|
|
2743
|
+
SI: 19,
|
|
2744
|
+
SK: 24,
|
|
2745
|
+
SM: 27,
|
|
2746
|
+
ST: 25,
|
|
2747
|
+
SV: 28,
|
|
2748
|
+
TL: 23,
|
|
2749
|
+
TN: 24,
|
|
2750
|
+
TR: 26,
|
|
2751
|
+
UA: 29,
|
|
2752
|
+
VA: 22,
|
|
2753
|
+
VG: 24,
|
|
2754
|
+
XK: 20
|
|
2755
|
+
};
|
|
2756
|
+
IBAN_GLUE_START = /[A-Z]{2}\d{2}/g;
|
|
2757
|
+
ibanGluedScanner = {
|
|
2758
|
+
lastIndex: 0,
|
|
2759
|
+
exec(content) {
|
|
2760
|
+
IBAN_GLUE_START.lastIndex = this.lastIndex;
|
|
2761
|
+
let m;
|
|
2762
|
+
while ((m = IBAN_GLUE_START.exec(content)) !== null) {
|
|
2763
|
+
const length = IBAN_LENGTHS[m[0].slice(0, 2)];
|
|
2764
|
+
if (length !== undefined) {
|
|
2765
|
+
let count = 0;
|
|
2766
|
+
let i = m.index;
|
|
2767
|
+
while (i < content.length && count < length) {
|
|
2768
|
+
const code = content.charCodeAt(i);
|
|
2769
|
+
if (isIbanChar(code)) {
|
|
2770
|
+
count += 1;
|
|
2771
|
+
} else if (!(code === 32 && count >= 4 && isIbanChar(content.charCodeAt(i + 1)))) {
|
|
2772
|
+
break;
|
|
2773
|
+
}
|
|
2774
|
+
i += 1;
|
|
2775
|
+
}
|
|
2776
|
+
if (count === length) {
|
|
2777
|
+
this.lastIndex = i;
|
|
2778
|
+
return { index: m.index, 0: content.slice(m.index, i) };
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
IBAN_GLUE_START.lastIndex = m.index + 1;
|
|
2782
|
+
}
|
|
2783
|
+
this.lastIndex = 0;
|
|
2784
|
+
return null;
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
CARD_EXTRA = new RegExp(`(?<!\\d${CARD_SEPARATOR}{0,2})[2-6](?:${CARD_SEPARATOR}{0,2}\\d){12,18}(?!\\d)(?!${CARD_SEPARATOR}{1,2}\\d)`, "g");
|
|
2788
|
+
CARD_GROUP_SEPARATOR = /[^\d]+/g;
|
|
2789
|
+
GLUE_LETTER_BEFORE = /[A-Za-z_]+$/;
|
|
2790
|
+
GLUE_LETTER_AFTER = /^[A-Za-z_]+/;
|
|
2791
|
+
NON_HEX_LETTER = /[G-Zg-z_]/;
|
|
2792
|
+
UUID_ANYWHERE = /[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}/g;
|
|
2793
|
+
SSN_BREAK_EXTRA = /(?<![0-9])\d{3}(?:\r\n|[\t\r\n])\d{2}(?:\r\n|[\t\r\n])\d{4}(?![0-9])/g;
|
|
2794
|
+
IPV4_EXTRA = /(?<![0-9.])(?<!\b[vV])\d{1,3}(?:[\t\r\n]{0,2}\.[\t\r\n]{0,2}\d{1,3}){3}(?![0-9])/g;
|
|
1869
2795
|
RULES = [
|
|
1870
2796
|
{
|
|
1871
2797
|
policyId: "pii.email",
|
|
1872
2798
|
mask: "email",
|
|
1873
|
-
regex:
|
|
2799
|
+
regex: emailScanner,
|
|
1874
2800
|
severity: "medium",
|
|
1875
2801
|
confidence: 0.85
|
|
1876
2802
|
},
|
|
1877
2803
|
{
|
|
1878
2804
|
policyId: "pii.phone",
|
|
1879
2805
|
mask: "phone",
|
|
1880
|
-
regex:
|
|
2806
|
+
regex: phoneScanner,
|
|
1881
2807
|
severity: "medium",
|
|
1882
2808
|
confidence: 0.7,
|
|
1883
|
-
valueGroup: 1
|
|
2809
|
+
valueGroup: 1,
|
|
2810
|
+
also: [{ regex: phoneGlueScanner }]
|
|
1884
2811
|
},
|
|
1885
2812
|
{
|
|
1886
2813
|
policyId: "pii.address",
|
|
@@ -1892,7 +2819,7 @@ var init_pii = __esm(() => {
|
|
|
1892
2819
|
{
|
|
1893
2820
|
policyId: "pii.person-name",
|
|
1894
2821
|
mask: "name",
|
|
1895
|
-
regex: /\b(?:name\s+is|customer|client|user|patient|employee)
|
|
2822
|
+
regex: /\b(?:name\s+is|customer|client|user|patient|employee)(?:\s*:)?\s+([A-Z][a-z]+\s+[A-Z][a-z]+)\b/g,
|
|
1896
2823
|
severity: "low",
|
|
1897
2824
|
confidence: 0.45,
|
|
1898
2825
|
valueGroup: 1
|
|
@@ -1900,10 +2827,11 @@ var init_pii = __esm(() => {
|
|
|
1900
2827
|
{
|
|
1901
2828
|
policyId: "pii.iban",
|
|
1902
2829
|
mask: "iban",
|
|
1903
|
-
regex:
|
|
2830
|
+
regex: ibanScanner,
|
|
1904
2831
|
severity: "high",
|
|
1905
2832
|
confidence: 0.9,
|
|
1906
|
-
validate: isValidIban
|
|
2833
|
+
validate: isValidIban,
|
|
2834
|
+
also: [{ regex: ibanGluedScanner }]
|
|
1907
2835
|
},
|
|
1908
2836
|
{
|
|
1909
2837
|
policyId: "pii.credit-card",
|
|
@@ -1911,15 +2839,24 @@ var init_pii = __esm(() => {
|
|
|
1911
2839
|
regex: /\b\d(?:[ -]?\d){12,18}\b/g,
|
|
1912
2840
|
severity: "high",
|
|
1913
2841
|
confidence: 0.9,
|
|
1914
|
-
validate: isValidCreditCard
|
|
2842
|
+
validate: isValidCreditCard,
|
|
2843
|
+
accept: outsideUuid,
|
|
2844
|
+
also: [
|
|
2845
|
+
{
|
|
2846
|
+
regex: CARD_EXTRA,
|
|
2847
|
+
validate: (value) => isCardCandidate(value) && isValidCreditCard(value.replace(/\D/g, "")),
|
|
2848
|
+
accept: hasNonHexGlue
|
|
2849
|
+
}
|
|
2850
|
+
]
|
|
1915
2851
|
},
|
|
1916
2852
|
{
|
|
1917
2853
|
policyId: "pii.ssn",
|
|
1918
2854
|
mask: "ssn",
|
|
1919
|
-
regex:
|
|
2855
|
+
regex: /(?<![0-9])\d{3}-\d{2}-\d{4}(?![0-9])/g,
|
|
1920
2856
|
severity: "high",
|
|
1921
2857
|
confidence: 0.85,
|
|
1922
|
-
validate: isValidSsn
|
|
2858
|
+
validate: isValidSsn,
|
|
2859
|
+
also: [{ regex: SSN_BREAK_EXTRA, validate: (value) => isValidSsn(value.replace(/\r\n|[\t\r\n]/g, "-")) }]
|
|
1923
2860
|
},
|
|
1924
2861
|
{
|
|
1925
2862
|
policyId: "pii.ip",
|
|
@@ -1927,12 +2864,25 @@ var init_pii = __esm(() => {
|
|
|
1927
2864
|
regex: /\b(?:(?:\d{1,3}\.){3}\d{1,3}|(?:[0-9A-Fa-f]{1,4}:){2,7}[0-9A-Fa-f]{1,4}|::1)\b/g,
|
|
1928
2865
|
severity: "low",
|
|
1929
2866
|
confidence: 0.6,
|
|
1930
|
-
validate: isValidIp
|
|
2867
|
+
validate: isValidIp,
|
|
2868
|
+
also: [{ regex: IPV4_EXTRA, validate: (value) => isValidIp(value.replace(/[\t\r\n]/g, "")) }]
|
|
1931
2869
|
}
|
|
1932
2870
|
];
|
|
1933
2871
|
CALENDAR_DATE = /(?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])/;
|
|
1934
2872
|
IDENTIFIER_CHAR = /[0-9A-Za-z_-]/;
|
|
1935
2873
|
UUID_TOKEN = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/;
|
|
2874
|
+
rescanTag = new WeakSet;
|
|
2875
|
+
NORMALIZE_CHUNK = 1 << 21;
|
|
2876
|
+
NON_ASCII = /[\u0080-\uffff]/;
|
|
2877
|
+
DECIMAL_DIGIT = /\p{Nd}/u;
|
|
2878
|
+
digitValues = new Map;
|
|
2879
|
+
BREAK_CHAR = /[\t\r\n]/;
|
|
2880
|
+
PHONE_RULE = RULES.findIndex((rule) => rule.policyId === "pii.phone");
|
|
2881
|
+
CARD_RULE = RULES.findIndex((rule) => rule.policyId === "pii.credit-card");
|
|
2882
|
+
NUMBER_RULES = new Set(RULES.flatMap((rule, index) => ["pii.phone", "pii.iban", "pii.credit-card", "pii.ssn", "pii.ip"].includes(rule.policyId) ? [index] : []));
|
|
2883
|
+
FULLWIDTH_CAPITAL = /[\uff21-\uff3a]/;
|
|
2884
|
+
SOFT_HYPHEN = /\u00ad/;
|
|
2885
|
+
NUMBER_BREAK = /[-.][\t\r\n]|[\t\r\n][-.]/;
|
|
1936
2886
|
});
|
|
1937
2887
|
|
|
1938
2888
|
// src/security/detect/secrets.ts
|
|
@@ -4348,17 +5298,20 @@ function locationFor(content, match) {
|
|
|
4348
5298
|
};
|
|
4349
5299
|
}
|
|
4350
5300
|
function applyRedaction(content, matches) {
|
|
4351
|
-
const redactable = matches.filter((m) => m.mask !== undefined).sort((a, b) => a.start - b.start);
|
|
5301
|
+
const redactable = matches.filter((m) => m.mask !== undefined).sort((a, b) => a.start - b.start || b.end - a.end);
|
|
4352
5302
|
let out = "";
|
|
4353
5303
|
let cursor = 0;
|
|
4354
5304
|
for (const match of redactable) {
|
|
4355
|
-
|
|
4356
|
-
|
|
5305
|
+
if (match.end <= cursor) {
|
|
5306
|
+
continue;
|
|
5307
|
+
}
|
|
5308
|
+
if (match.start < cursor) {
|
|
5309
|
+
cursor = match.end;
|
|
4357
5310
|
continue;
|
|
4358
5311
|
}
|
|
4359
|
-
out += content.slice(cursor, start);
|
|
5312
|
+
out += content.slice(cursor, match.start);
|
|
4360
5313
|
out += maskFor(match);
|
|
4361
|
-
cursor =
|
|
5314
|
+
cursor = match.end;
|
|
4362
5315
|
}
|
|
4363
5316
|
out += content.slice(cursor);
|
|
4364
5317
|
return out;
|
|
@@ -20145,7 +21098,7 @@ var package_default;
|
|
|
20145
21098
|
var init_package = __esm(() => {
|
|
20146
21099
|
package_default = {
|
|
20147
21100
|
name: "@mrciphersmith/keryx",
|
|
20148
|
-
version: "0.3.
|
|
21101
|
+
version: "0.3.79",
|
|
20149
21102
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
20150
21103
|
private: false,
|
|
20151
21104
|
publishConfig: {
|
|
@@ -20223,10 +21176,10 @@ var init_package = __esm(() => {
|
|
|
20223
21176
|
qs: "^6.16.0",
|
|
20224
21177
|
"@hono/node-server": "^1.19.15",
|
|
20225
21178
|
protobufjs: "7.6.6",
|
|
20226
|
-
sharp: "0.35.
|
|
21179
|
+
sharp: "0.35.5"
|
|
20227
21180
|
},
|
|
20228
21181
|
optionalDependencies: {
|
|
20229
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
21182
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
20230
21183
|
"@opentui/core": "^0.4.5",
|
|
20231
21184
|
"web-tree-sitter": "^0.22.6"
|
|
20232
21185
|
},
|
|
@@ -24868,6 +25821,7 @@ var init_service2 = __esm(() => {
|
|
|
24868
25821
|
init_path_scan();
|
|
24869
25822
|
init_read_source();
|
|
24870
25823
|
init_redact();
|
|
25824
|
+
init_pii();
|
|
24871
25825
|
init_secrets();
|
|
24872
25826
|
init_entropy();
|
|
24873
25827
|
init_incidents();
|
|
@@ -109172,6 +110126,22 @@ var init_theme_text = __esm(() => {
|
|
|
109172
110126
|
init_theme();
|
|
109173
110127
|
});
|
|
109174
110128
|
|
|
110129
|
+
// src/tui/transcript-history.ts
|
|
110130
|
+
function trackTranscriptNode(node, policy) {
|
|
110131
|
+
policies.set(node, policy);
|
|
110132
|
+
}
|
|
110133
|
+
function transcriptNodeProtected(node) {
|
|
110134
|
+
return policies.get(node)?.protected() === true;
|
|
110135
|
+
}
|
|
110136
|
+
function forgetTranscriptNode(node) {
|
|
110137
|
+
policies.get(node)?.evict?.();
|
|
110138
|
+
policies.delete(node);
|
|
110139
|
+
}
|
|
110140
|
+
var TRANSCRIPT_HISTORY_LIMIT = 256, policies;
|
|
110141
|
+
var init_transcript_history = __esm(() => {
|
|
110142
|
+
policies = new WeakMap;
|
|
110143
|
+
});
|
|
110144
|
+
|
|
109175
110145
|
// src/tui/transcript-blocks.ts
|
|
109176
110146
|
function createBlockRegistry(options = {}) {
|
|
109177
110147
|
const maxBlocks = options.maxBlocks ?? DEFAULT_MAX_BLOCKS;
|
|
@@ -109232,6 +110202,15 @@ function createBlockRegistry(options = {}) {
|
|
|
109232
110202
|
}
|
|
109233
110203
|
return id;
|
|
109234
110204
|
},
|
|
110205
|
+
remove: (id) => {
|
|
110206
|
+
const index = blocks.findIndex((block) => block.id === id);
|
|
110207
|
+
if (index < 0)
|
|
110208
|
+
return;
|
|
110209
|
+
blocks.splice(index, 1);
|
|
110210
|
+
if (focusIndex > index)
|
|
110211
|
+
focusIndex -= 1;
|
|
110212
|
+
focusIndex = Math.min(focusIndex, blocks.length - 1);
|
|
110213
|
+
},
|
|
109235
110214
|
get: (id) => {
|
|
109236
110215
|
const block = find(id);
|
|
109237
110216
|
return block === undefined ? undefined : snapshot(block);
|
|
@@ -109629,8 +110608,13 @@ function createAssistantMessageStream(otui, renderer, parent) {
|
|
|
109629
110608
|
flexDirection: "column",
|
|
109630
110609
|
flexShrink: 0
|
|
109631
110610
|
});
|
|
110611
|
+
let live = true;
|
|
110612
|
+
trackTranscriptNode(container, { protected: () => live });
|
|
110613
|
+
const complete = () => {
|
|
110614
|
+
live = false;
|
|
110615
|
+
};
|
|
109632
110616
|
parent.add(container);
|
|
109633
|
-
return { container, segmenter: createStreamSegmenter(), views: [], frozen: 0 };
|
|
110617
|
+
return { complete, container, segmenter: createStreamSegmenter(), views: [], frozen: 0 };
|
|
109634
110618
|
};
|
|
109635
110619
|
const paint = (m, segments, frozen) => {
|
|
109636
110620
|
for (let i = m.frozen;i < segments.length; i++) {
|
|
@@ -109673,9 +110657,10 @@ function createAssistantMessageStream(otui, renderer, parent) {
|
|
|
109673
110657
|
m.frozen = 0;
|
|
109674
110658
|
const segments = segmentMarkdown(text);
|
|
109675
110659
|
paint(m, segments, segments.length);
|
|
110660
|
+
m.complete();
|
|
109676
110661
|
if (segments.length === 0) {
|
|
109677
110662
|
try {
|
|
109678
|
-
|
|
110663
|
+
m.container.destroyRecursively();
|
|
109679
110664
|
} catch {}
|
|
109680
110665
|
}
|
|
109681
110666
|
message = undefined;
|
|
@@ -109685,11 +110670,12 @@ function createAssistantMessageStream(otui, renderer, parent) {
|
|
|
109685
110670
|
if (message === undefined) {
|
|
109686
110671
|
return;
|
|
109687
110672
|
}
|
|
110673
|
+
message.complete();
|
|
109688
110674
|
for (const view of message.views.splice(0)) {
|
|
109689
110675
|
view.destroy();
|
|
109690
110676
|
}
|
|
109691
110677
|
try {
|
|
109692
|
-
|
|
110678
|
+
message.container.destroyRecursively();
|
|
109693
110679
|
} catch {}
|
|
109694
110680
|
message = undefined;
|
|
109695
110681
|
},
|
|
@@ -109707,6 +110693,8 @@ function createBlockView(otui, renderer, parent, block, options = {}) {
|
|
|
109707
110693
|
});
|
|
109708
110694
|
const header = new otui.TextRenderable(renderer, { id: `${id}-h`, content: "" });
|
|
109709
110695
|
box.add(header);
|
|
110696
|
+
let protectedView = false;
|
|
110697
|
+
trackTranscriptNode(box, { protected: () => protectedView, evict: () => options.onHistoryEvict?.() });
|
|
109710
110698
|
parent.add(box);
|
|
109711
110699
|
let body;
|
|
109712
110700
|
let bodyText;
|
|
@@ -109779,6 +110767,9 @@ function createBlockView(otui, renderer, parent, block, options = {}) {
|
|
|
109779
110767
|
return {
|
|
109780
110768
|
id: block.id,
|
|
109781
110769
|
render: (state, opts = {}) => {
|
|
110770
|
+
if (box.isDestroyed)
|
|
110771
|
+
return;
|
|
110772
|
+
protectedView = opts.focused === true;
|
|
109782
110773
|
paintHeader(state, opts.focused === true);
|
|
109783
110774
|
if (state.collapsed) {
|
|
109784
110775
|
if (options.collapsedPreview !== undefined) {
|
|
@@ -109835,7 +110826,14 @@ function createBlockMount(otui, renderer, parent, registry) {
|
|
|
109835
110826
|
}
|
|
109836
110827
|
const state = registry.get(id);
|
|
109837
110828
|
if (state !== undefined) {
|
|
109838
|
-
views.set(id, createBlockView(otui, renderer, parent, state,
|
|
110829
|
+
views.set(id, createBlockView(otui, renderer, parent, state, {
|
|
110830
|
+
...options,
|
|
110831
|
+
onHistoryEvict: () => {
|
|
110832
|
+
views.delete(id);
|
|
110833
|
+
registry.remove(id);
|
|
110834
|
+
options.onHistoryEvict?.();
|
|
110835
|
+
}
|
|
110836
|
+
}));
|
|
109839
110837
|
}
|
|
109840
110838
|
return id;
|
|
109841
110839
|
},
|
|
@@ -110061,6 +111059,7 @@ var init_transcript_blocks = __esm(() => {
|
|
|
110061
111059
|
init_md_blocks();
|
|
110062
111060
|
init_theme();
|
|
110063
111061
|
init_theme_text();
|
|
111062
|
+
init_transcript_history();
|
|
110064
111063
|
});
|
|
110065
111064
|
|
|
110066
111065
|
// src/tui/modal-host.ts
|
|
@@ -110109,6 +111108,20 @@ function resolveModalInnerWidth(availableWidth) {
|
|
|
110109
111108
|
function formatModalFooter(actions) {
|
|
110110
111109
|
return actions.map((action) => `${action.key} ${action.label}`).join(" \xB7 ");
|
|
110111
111110
|
}
|
|
111111
|
+
function modalFocusFallback(node) {
|
|
111112
|
+
return modalHandBack.get(node) ?? null;
|
|
111113
|
+
}
|
|
111114
|
+
function focusIsLive(node, root) {
|
|
111115
|
+
let current = node;
|
|
111116
|
+
while (current !== null) {
|
|
111117
|
+
if (current.isDestroyed || !current.visible)
|
|
111118
|
+
return false;
|
|
111119
|
+
if (current === root)
|
|
111120
|
+
return true;
|
|
111121
|
+
current = current.parent;
|
|
111122
|
+
}
|
|
111123
|
+
return false;
|
|
111124
|
+
}
|
|
110112
111125
|
function destroyModalHost(renderer) {
|
|
110113
111126
|
hosts.get(renderer)?.unsubTheme();
|
|
110114
111127
|
}
|
|
@@ -110123,6 +111136,10 @@ function containsNode(root, node) {
|
|
|
110123
111136
|
}
|
|
110124
111137
|
return false;
|
|
110125
111138
|
}
|
|
111139
|
+
function modalHides(renderer, node) {
|
|
111140
|
+
const state = hosts.get(renderer);
|
|
111141
|
+
return state !== undefined && state.open && !containsNode(state.backdrop, node);
|
|
111142
|
+
}
|
|
110126
111143
|
function resolveInitialTab(tabs, initialTab) {
|
|
110127
111144
|
const first = tabs[0];
|
|
110128
111145
|
if (first === undefined) {
|
|
@@ -110207,7 +111224,15 @@ function closeHost(state, opts) {
|
|
|
110207
111224
|
if (state.savedScrollTop !== undefined && scroll !== undefined) {
|
|
110208
111225
|
scroll.scrollTop = state.savedScrollTop;
|
|
110209
111226
|
}
|
|
110210
|
-
state.chrome.
|
|
111227
|
+
const r = state.chrome.renderer;
|
|
111228
|
+
const current = r.currentFocusedRenderable;
|
|
111229
|
+
if (current === null || containsNode(state.backdrop, current)) {
|
|
111230
|
+
const previous = state.savedFocus;
|
|
111231
|
+
if (previous !== null && focusIsLive(previous, r.root))
|
|
111232
|
+
previous.focus();
|
|
111233
|
+
else
|
|
111234
|
+
state.chrome.focusComposer();
|
|
111235
|
+
}
|
|
110211
111236
|
}
|
|
110212
111237
|
if (opts.runOnClose && onClose !== undefined) {
|
|
110213
111238
|
onClose();
|
|
@@ -110337,6 +111362,7 @@ function ensureHost(otui, chrome) {
|
|
|
110337
111362
|
releaseOverlay: undefined,
|
|
110338
111363
|
unsubKeys: undefined,
|
|
110339
111364
|
savedScrollTop: undefined,
|
|
111365
|
+
savedFocus: null,
|
|
110340
111366
|
unsubTheme: () => {}
|
|
110341
111367
|
};
|
|
110342
111368
|
state.releaseOverlay = chrome.addOverlaySource(() => state.open, { kind: "modal" });
|
|
@@ -110461,6 +111487,8 @@ function openModal(otui, chrome, input) {
|
|
|
110461
111487
|
state.onClose = undefined;
|
|
110462
111488
|
previousClose?.();
|
|
110463
111489
|
} else {
|
|
111490
|
+
state.savedFocus = chrome.renderer.currentFocusedRenderable;
|
|
111491
|
+
modalHandBack.set(state.tabStrip, state.savedFocus);
|
|
110464
111492
|
state.savedScrollTop = chrome.scroll.scrollTop;
|
|
110465
111493
|
chrome.hideMenu();
|
|
110466
111494
|
chrome.blurComposer();
|
|
@@ -110487,7 +111515,7 @@ function openModal(otui, chrome, input) {
|
|
|
110487
111515
|
}, 0);
|
|
110488
111516
|
return makeHandle(state, generation);
|
|
110489
111517
|
}
|
|
110490
|
-
var BACKDROP_ID = "modal-backdrop", PANEL_ID = "modal-panel", BACKDROP_ALPHA = 1, MODAL_PANEL_MARGIN = 1, MODAL_PANEL_CHROME_X = 4, MODAL_PANEL_MIN_WIDTH = 72, MODAL_PANEL_MIN_HEIGHT = 18, MODAL_PANEL_SIZE_RATIO = 0.95, MODAL_PANEL_MAX_HEIGHT_RATIO = 0.85, MODAL_CHROME_ROWS = 5, MODAL_PANEL_WIDTH, MODAL_PANEL_HEIGHT, MODAL_PANEL_INNER_WIDTH, CLOSE_HINT = "[x] esc", DEFAULT_FOOTER, hosts, MODAL_BODY_SCROLLBAR_COLS = 1;
|
|
111518
|
+
var BACKDROP_ID = "modal-backdrop", PANEL_ID = "modal-panel", BACKDROP_ALPHA = 1, MODAL_PANEL_MARGIN = 1, MODAL_PANEL_CHROME_X = 4, MODAL_PANEL_MIN_WIDTH = 72, MODAL_PANEL_MIN_HEIGHT = 18, MODAL_PANEL_SIZE_RATIO = 0.95, MODAL_PANEL_MAX_HEIGHT_RATIO = 0.85, MODAL_CHROME_ROWS = 5, MODAL_PANEL_WIDTH, MODAL_PANEL_HEIGHT, MODAL_PANEL_INNER_WIDTH, CLOSE_HINT = "[x] esc", DEFAULT_FOOTER, modalHandBack, hosts, MODAL_BODY_SCROLLBAR_COLS = 1;
|
|
110491
111519
|
var init_modal_host = __esm(() => {
|
|
110492
111520
|
init_theme();
|
|
110493
111521
|
init_theme_text();
|
|
@@ -110499,6 +111527,7 @@ var init_modal_host = __esm(() => {
|
|
|
110499
111527
|
{ key: "\u2190/\u2192", label: "tabs" },
|
|
110500
111528
|
{ key: "esc", label: "close" }
|
|
110501
111529
|
];
|
|
111530
|
+
modalHandBack = new WeakMap;
|
|
110502
111531
|
hosts = new WeakMap;
|
|
110503
111532
|
});
|
|
110504
111533
|
|
|
@@ -111336,6 +112365,22 @@ async function attemptJevRequest(fetchFn, apiKey, body, timeoutMs, external) {
|
|
|
111336
112365
|
}
|
|
111337
112366
|
}
|
|
111338
112367
|
}
|
|
112368
|
+
async function cancellableJevSleep(sleep, ms, signal, timeoutMs) {
|
|
112369
|
+
if (signal === undefined)
|
|
112370
|
+
return sleep(ms);
|
|
112371
|
+
if (signal.aborted)
|
|
112372
|
+
throw new JevTimeoutError(timeoutMs);
|
|
112373
|
+
let onAbort = () => {};
|
|
112374
|
+
const aborted = new Promise((_, reject) => {
|
|
112375
|
+
onAbort = () => reject(new JevTimeoutError(timeoutMs));
|
|
112376
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
112377
|
+
});
|
|
112378
|
+
try {
|
|
112379
|
+
await Promise.race([sleep(ms), aborted]);
|
|
112380
|
+
} finally {
|
|
112381
|
+
signal.removeEventListener("abort", onAbort);
|
|
112382
|
+
}
|
|
112383
|
+
}
|
|
111339
112384
|
async function callJevSystemOne(fetchFn = globalThis.fetch, input, opts) {
|
|
111340
112385
|
const externalSetting = await resolveExternalSetting({ cwd: opts?.cwd ?? process.cwd(), ...opts?.dir !== undefined ? { dir: opts.dir } : {} });
|
|
111341
112386
|
if (externalSetting.value === "off") {
|
|
@@ -111361,6 +112406,8 @@ async function callJevSystemOne(fetchFn = globalThis.fetch, input, opts) {
|
|
|
111361
112406
|
let text;
|
|
111362
112407
|
let attempt = 0;
|
|
111363
112408
|
for (;; ) {
|
|
112409
|
+
if (external?.aborted)
|
|
112410
|
+
throw new JevTimeoutError(timeoutMs);
|
|
111364
112411
|
attempt += 1;
|
|
111365
112412
|
const outcome = await attemptJevRequest(fetchFn, apiKey, body, timeoutMs, external);
|
|
111366
112413
|
if (outcome.kind === "timeout") {
|
|
@@ -111370,7 +112417,7 @@ async function callJevSystemOne(fetchFn = globalThis.fetch, input, opts) {
|
|
|
111370
112417
|
if (attempt >= JEV_MAX_ATTEMPTS) {
|
|
111371
112418
|
throw new JevNetworkError(attempt, outcome.cause);
|
|
111372
112419
|
}
|
|
111373
|
-
await sleepFn
|
|
112420
|
+
await cancellableJevSleep(sleepFn, jevBackoffMs(attempt), external, timeoutMs);
|
|
111374
112421
|
continue;
|
|
111375
112422
|
}
|
|
111376
112423
|
if (outcome.ok) {
|
|
@@ -111381,7 +112428,7 @@ async function callJevSystemOne(fetchFn = globalThis.fetch, input, opts) {
|
|
|
111381
112428
|
text = outcome.text;
|
|
111382
112429
|
if (JEV_RETRYABLE_STATUSES.has(status) && attempt < JEV_MAX_ATTEMPTS) {
|
|
111383
112430
|
const retryAfterMs = retryAfterMsFromHeader(outcome.retryAfterHeader);
|
|
111384
|
-
await sleepFn
|
|
112431
|
+
await cancellableJevSleep(sleepFn, retryAfterMs ?? jevBackoffMs(attempt), external, timeoutMs);
|
|
111385
112432
|
continue;
|
|
111386
112433
|
}
|
|
111387
112434
|
if (status === 401) {
|
|
@@ -133873,14 +134920,60 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
133873
134920
|
let liveStatus;
|
|
133874
134921
|
const originalTranscriptAdd = transcript.add.bind(transcript);
|
|
133875
134922
|
const originalTranscriptRemove = transcript.remove.bind(transcript);
|
|
134923
|
+
let historyNotice;
|
|
134924
|
+
let historyAnchor;
|
|
134925
|
+
const restoreHistoryAnchor = () => {
|
|
134926
|
+
const anchor = historyAnchor;
|
|
134927
|
+
historyAnchor = undefined;
|
|
134928
|
+
if (anchor === undefined || anchor.node.isDestroyed || scroll.stickyScroll || scroll.scrollTop !== anchor.scrollTop)
|
|
134929
|
+
return;
|
|
134930
|
+
scroll.scrollTop += anchor.node.y - anchor.y;
|
|
134931
|
+
};
|
|
134932
|
+
r.on(otui.CliRenderEvents.FRAME, restoreHistoryAnchor);
|
|
134933
|
+
const trimHistory = () => {
|
|
134934
|
+
const completed = transcript.getChildren().filter((node) => node !== liveStatus && node !== historyNotice && !transcriptNodeProtected(node));
|
|
134935
|
+
const excess = completed.length - TRANSCRIPT_HISTORY_LIMIT;
|
|
134936
|
+
if (excess <= 0)
|
|
134937
|
+
return;
|
|
134938
|
+
if (!scroll.stickyScroll && historyAnchor === undefined) {
|
|
134939
|
+
const evicted = new Set(completed.slice(0, excess));
|
|
134940
|
+
const node = transcript.getChildren().find((child) => child !== liveStatus && child !== historyNotice && !evicted.has(child) && child.y + child.height > scroll.y && child.y < scroll.y + scroll.height);
|
|
134941
|
+
if (node !== undefined)
|
|
134942
|
+
historyAnchor = { node, y: node.y, scrollTop: scroll.scrollTop };
|
|
134943
|
+
}
|
|
134944
|
+
for (const node of completed.slice(0, excess)) {
|
|
134945
|
+
forgetTranscriptNode(node);
|
|
134946
|
+
node.destroyRecursively();
|
|
134947
|
+
}
|
|
134948
|
+
for (let i = userPrompts.length - 1;i >= 0; i--) {
|
|
134949
|
+
if (userPrompts[i]?.box.isDestroyed)
|
|
134950
|
+
userPrompts.splice(i, 1);
|
|
134951
|
+
}
|
|
134952
|
+
if (pinnedPrompt?.box.isDestroyed) {
|
|
134953
|
+
pinnedPrompt = undefined;
|
|
134954
|
+
promptPin.visible = false;
|
|
134955
|
+
}
|
|
134956
|
+
if (historyNotice === undefined || historyNotice.isDestroyed) {
|
|
134957
|
+
historyNotice = new otui.TextRenderable(r, {
|
|
134958
|
+
id: "transcript-history-notice",
|
|
134959
|
+
content: "Older output is no longer mounted in this view.",
|
|
134960
|
+
fg: getTheme().muted,
|
|
134961
|
+
flexShrink: 0
|
|
134962
|
+
});
|
|
134963
|
+
originalTranscriptAdd(historyNotice, 0);
|
|
134964
|
+
}
|
|
134965
|
+
};
|
|
133876
134966
|
transcript.add = (child, index) => {
|
|
133877
134967
|
if (liveStatus !== undefined && child !== liveStatus) {
|
|
133878
134968
|
originalTranscriptRemove(liveStatus);
|
|
133879
134969
|
const added = originalTranscriptAdd(child, index);
|
|
133880
134970
|
originalTranscriptAdd(liveStatus);
|
|
134971
|
+
trimHistory();
|
|
133881
134972
|
return added;
|
|
133882
134973
|
}
|
|
133883
|
-
|
|
134974
|
+
const added = originalTranscriptAdd(child, index);
|
|
134975
|
+
trimHistory();
|
|
134976
|
+
return added;
|
|
133884
134977
|
};
|
|
133885
134978
|
const queueDock = new otui.BoxRenderable(r, {
|
|
133886
134979
|
id: "queue-dock",
|
|
@@ -134421,6 +135514,7 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
134421
135514
|
} catch {}
|
|
134422
135515
|
try {
|
|
134423
135516
|
r.off(otui.CliRenderEvents.FRAME, syncPromptPin);
|
|
135517
|
+
r.off(otui.CliRenderEvents.FRAME, restoreHistoryAnchor);
|
|
134424
135518
|
} catch {}
|
|
134425
135519
|
}
|
|
134426
135520
|
};
|
|
@@ -134433,6 +135527,7 @@ var init_shell_chrome = __esm(() => {
|
|
|
134433
135527
|
init_modal_host();
|
|
134434
135528
|
init_debug_log();
|
|
134435
135529
|
init_renderer_debug();
|
|
135530
|
+
init_transcript_history();
|
|
134436
135531
|
SIDEBAR_TEXT_WIDTH = SIDEBAR_WIDTH - SIDEBAR_BORDER_LEFT - SIDEBAR_PADDING_LEFT - SIDEBAR_PADDING_RIGHT;
|
|
134437
135532
|
SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
134438
135533
|
});
|
|
@@ -135770,7 +136865,7 @@ function openRouting(otui, chrome, options) {
|
|
|
135770
136865
|
cwd: options.cwd,
|
|
135771
136866
|
...options.userConfigDir !== undefined ? { userConfigDir: options.userConfigDir } : {}
|
|
135772
136867
|
};
|
|
135773
|
-
const loadProviders = options.providers ??
|
|
136868
|
+
const loadProviders = options.providers ?? loadRoutingProviders;
|
|
135774
136869
|
const loadProfiles = options.loadProfiles ?? ((dir) => loadModelProfiles(dir));
|
|
135775
136870
|
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
135776
136871
|
const panelRows = typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13;
|
|
@@ -135985,7 +137080,7 @@ function describePickerRowProfile(opt, profiles) {
|
|
|
135985
137080
|
parts.push("non-chat/free \u2014 never auto-derived");
|
|
135986
137081
|
return parts.join(" \xB7 ");
|
|
135987
137082
|
}
|
|
135988
|
-
async function
|
|
137083
|
+
async function loadRoutingProviders() {
|
|
135989
137084
|
await Promise.resolve().then(() => init_provider_catalog());
|
|
135990
137085
|
await Promise.resolve().then(() => init_shell_config());
|
|
135991
137086
|
const catalog = await loadOrRefreshProviderCatalog({ fetch, env: envWithSavedApiKeys() });
|
|
@@ -144312,52 +145407,77 @@ class NullClassifier {
|
|
|
144312
145407
|
}
|
|
144313
145408
|
|
|
144314
145409
|
// src/harness/decision/classify-turn.ts
|
|
144315
|
-
async function withTimeout4(source, run, timeoutMs) {
|
|
145410
|
+
async function withTimeout4(source, run, timeoutMs, external) {
|
|
145411
|
+
const cancelled = () => ({ ok: false, reason: "classification cancelled" });
|
|
145412
|
+
if (external?.aborted)
|
|
145413
|
+
return cancelled();
|
|
145414
|
+
if (timeoutMs <= 0)
|
|
145415
|
+
return { ok: false, reason: "classification deadline exceeded" };
|
|
144316
145416
|
const controller = new AbortController;
|
|
145417
|
+
const onExternalAbort = () => controller.abort();
|
|
145418
|
+
external?.addEventListener("abort", onExternalAbort, { once: true });
|
|
144317
145419
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
145420
|
+
let onAbort = () => {};
|
|
144318
145421
|
try {
|
|
144319
|
-
|
|
144320
|
-
|
|
144321
|
-
|
|
144322
|
-
|
|
144323
|
-
|
|
144324
|
-
]);
|
|
145422
|
+
const aborted = new Promise((resolve) => {
|
|
145423
|
+
onAbort = () => resolve(external?.aborted ? cancelled() : { ok: false, reason: `${source} classifier timed out after ${timeoutMs}ms` });
|
|
145424
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
145425
|
+
});
|
|
145426
|
+
return await Promise.race([run(controller.signal), aborted]);
|
|
144325
145427
|
} catch (error) {
|
|
144326
145428
|
return { ok: false, reason: `${source} classifier errored: ${error instanceof Error ? error.message : String(error)}` };
|
|
144327
145429
|
} finally {
|
|
144328
145430
|
clearTimeout(timer);
|
|
145431
|
+
external?.removeEventListener("abort", onExternalAbort);
|
|
145432
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
145433
|
+
controller.abort();
|
|
144329
145434
|
}
|
|
144330
145435
|
}
|
|
144331
145436
|
async function classifyTurn(line, categories, opts = {}) {
|
|
144332
145437
|
if (isSlashCommandLine(line) || categories.length === 0)
|
|
144333
145438
|
return;
|
|
145439
|
+
if (opts.signal?.aborted)
|
|
145440
|
+
return { result: { ok: false, reason: "classification cancelled" }, trace: [] };
|
|
144334
145441
|
const deterministic = classifyDeterministic(line);
|
|
144335
145442
|
if (deterministic !== undefined && categories.includes(deterministic)) {
|
|
144336
145443
|
const result = { ok: true, category: deterministic, confidence: 1, source: "deterministic" };
|
|
144337
145444
|
return { result, trace: [{ source: "deterministic", result }] };
|
|
144338
145445
|
}
|
|
144339
145446
|
const timeoutMs = opts.timeoutMs ?? CLASSIFY_TURN_TIMEOUT_MS;
|
|
145447
|
+
const deadline = performance.now() + Math.max(0, timeoutMs);
|
|
145448
|
+
const remaining = () => Math.max(0, deadline - performance.now());
|
|
145449
|
+
const fallback = opts.fallbackModel !== undefined ? opts.fallbackModel : opts.sessionProvider !== undefined && opts.sessionModel !== undefined ? { providerId: opts.sessionProvider, modelId: opts.sessionModel } : undefined;
|
|
144340
145450
|
const trace = [];
|
|
145451
|
+
const stopped = () => opts.signal?.aborted ? { result: { ok: false, reason: "classification cancelled" }, trace } : remaining() <= 0 ? { result: { ok: false, reason: "classification deadline exceeded" }, trace } : undefined;
|
|
145452
|
+
const initialStop = stopped();
|
|
145453
|
+
if (initialStop)
|
|
145454
|
+
return initialStop;
|
|
144341
145455
|
if (opts.jevEnabled === true) {
|
|
144342
145456
|
const credential = resolveJevClassifierCredential(opts.env, opts.dir);
|
|
144343
145457
|
if (credential.key !== undefined) {
|
|
144344
145458
|
const jev = new JevTaskClassifier({ ...opts.fetch !== undefined ? { fetch: opts.fetch } : {}, ...opts.env !== undefined ? { env: opts.env } : {}, ...opts.dir !== undefined ? { dir: opts.dir } : {}, timeoutMs });
|
|
144345
|
-
const jevResult = await withTimeout4("jev", (signal) => jev.classify(line, categories, { signal }), timeoutMs);
|
|
145459
|
+
const jevResult = await withTimeout4("jev", (signal) => jev.classify(line, categories, { signal }), Math.min(remaining(), fallback != null ? timeoutMs / 2 : timeoutMs), opts.signal);
|
|
144346
145460
|
trace.push({ source: "jev", result: jevResult });
|
|
145461
|
+
const jevStop = stopped();
|
|
145462
|
+
if (jevStop)
|
|
145463
|
+
return jevStop;
|
|
144347
145464
|
if (jevResult.ok)
|
|
144348
145465
|
return { result: jevResult, trace };
|
|
144349
145466
|
}
|
|
144350
145467
|
}
|
|
144351
|
-
if (
|
|
145468
|
+
if (fallback != null) {
|
|
144352
145469
|
const mainModel = new MainModelTaskClassifier({
|
|
144353
|
-
provider:
|
|
144354
|
-
model:
|
|
145470
|
+
provider: fallback.providerId,
|
|
145471
|
+
model: fallback.modelId,
|
|
144355
145472
|
...opts.env !== undefined ? { env: opts.env } : {},
|
|
144356
145473
|
...opts.fetch !== undefined ? { fetch: opts.fetch } : {},
|
|
144357
145474
|
...opts.providerFactory !== undefined ? { providerFactory: opts.providerFactory } : {}
|
|
144358
145475
|
});
|
|
144359
|
-
const mainModelResult = await withTimeout4("main-model", (signal) => mainModel.classify(line, categories, { signal }),
|
|
145476
|
+
const mainModelResult = await withTimeout4("main-model", (signal) => mainModel.classify(line, categories, { signal }), remaining(), opts.signal);
|
|
144360
145477
|
trace.push({ source: "main-model", result: mainModelResult });
|
|
145478
|
+
const mainStop = stopped();
|
|
145479
|
+
if (mainStop)
|
|
145480
|
+
return mainStop;
|
|
144361
145481
|
if (mainModelResult.ok)
|
|
144362
145482
|
return { result: mainModelResult, trace };
|
|
144363
145483
|
}
|
|
@@ -144371,42 +145491,151 @@ var init_classify_turn = __esm(() => {
|
|
|
144371
145491
|
init_main_model_classifier();
|
|
144372
145492
|
});
|
|
144373
145493
|
|
|
145494
|
+
// src/harness/routing/classifier-model.ts
|
|
145495
|
+
function selectClassifierModel(detected, profiles, allowed) {
|
|
145496
|
+
const candidates = [];
|
|
145497
|
+
for (const provider of detected) {
|
|
145498
|
+
for (const modelId of provider.models ?? []) {
|
|
145499
|
+
const profile = profiles[profileKey(provider.name, modelId)];
|
|
145500
|
+
if (!profile?.available || !isModelDerivable(profile))
|
|
145501
|
+
continue;
|
|
145502
|
+
if (profile.strengthTier.value === "light" || profile.strengthTier.source === "unknown" || profile.strengthTier.source === "guessed")
|
|
145503
|
+
continue;
|
|
145504
|
+
if (allowed(provider.name, modelId))
|
|
145505
|
+
candidates.push(profile);
|
|
145506
|
+
}
|
|
145507
|
+
}
|
|
145508
|
+
candidates.sort((a, b) => Number(a.strengthTier.value === "deep") - Number(b.strengthTier.value === "deep") || b.priority.value - a.priority.value || profileKey(a.providerId, a.modelId).localeCompare(profileKey(b.providerId, b.modelId)));
|
|
145509
|
+
const selected = candidates[0];
|
|
145510
|
+
return selected === undefined ? undefined : { providerId: selected.providerId, modelId: selected.modelId };
|
|
145511
|
+
}
|
|
145512
|
+
var init_classifier_model = __esm(() => {
|
|
145513
|
+
init_model_profile();
|
|
145514
|
+
});
|
|
145515
|
+
|
|
144374
145516
|
// src/tui/routing-classifier-source.ts
|
|
145517
|
+
async function loadTurnProviders(load, signal, timeoutMs) {
|
|
145518
|
+
if (signal?.aborted || timeoutMs <= 0)
|
|
145519
|
+
return;
|
|
145520
|
+
let stop;
|
|
145521
|
+
const stopped = new Promise((resolve) => {
|
|
145522
|
+
stop = () => resolve(undefined);
|
|
145523
|
+
});
|
|
145524
|
+
signal?.addEventListener("abort", stop, { once: true });
|
|
145525
|
+
const timer = setTimeout(stop, timeoutMs);
|
|
145526
|
+
try {
|
|
145527
|
+
return await Promise.race([Promise.resolve().then(load), stopped]);
|
|
145528
|
+
} finally {
|
|
145529
|
+
clearTimeout(timer);
|
|
145530
|
+
signal?.removeEventListener("abort", stop);
|
|
145531
|
+
}
|
|
145532
|
+
}
|
|
144375
145533
|
async function runRoutingClassifierForTurn(line, opts) {
|
|
145534
|
+
if (opts.signal?.aborted)
|
|
145535
|
+
return;
|
|
144376
145536
|
if (!opts.enabled)
|
|
144377
145537
|
return;
|
|
145538
|
+
const deadline = performance.now() + Math.max(0, opts.timeoutMs ?? CLASSIFY_TURN_TIMEOUT_MS);
|
|
145539
|
+
const profiles = loadModelProfiles(opts.userConfigDir);
|
|
145540
|
+
const providers = opts.providers === undefined ? opts.detected : await loadTurnProviders(opts.providers, opts.signal, Math.max(0, deadline - performance.now()));
|
|
145541
|
+
if (providers === undefined)
|
|
145542
|
+
return;
|
|
145543
|
+
if (opts.signal?.aborted)
|
|
145544
|
+
return;
|
|
145545
|
+
const env = envWithSavedApiKeys(opts.env ?? process.env);
|
|
145546
|
+
const external = await resolveExternalSetting({ cwd: opts.cwd, ...opts.userConfigDir !== undefined ? { dir: opts.userConfigDir } : {} });
|
|
145547
|
+
if (opts.signal?.aborted)
|
|
145548
|
+
return;
|
|
145549
|
+
const externalAllowed = externalAllowedConnectedPredicate(connectedPredicateFrom(providers), external.value === "on", loadExternalProvidersConfig(opts.userConfigDir).config);
|
|
145550
|
+
const fallbackModel = selectClassifierModel(providers, profiles, (providerId, modelId) => externalAllowed(providerId, modelId) && (opts.classifierAllowed?.(providerId, modelId) ?? true) && (opts.providerFactory !== undefined || hasCredential(providerId, env)));
|
|
144378
145551
|
const classification = await classifyTurn(line, TURN_CLASSIFIER_CATEGORIES, {
|
|
144379
|
-
jevEnabled: opts.jevEnabled,
|
|
144380
|
-
|
|
144381
|
-
|
|
145552
|
+
jevEnabled: opts.jevEnabled && opts.jevAllowed !== false && external.value === "on",
|
|
145553
|
+
...opts.signal !== undefined ? { signal: opts.signal } : {},
|
|
145554
|
+
fallbackModel: fallbackModel ?? null,
|
|
145555
|
+
...opts.providerFactory !== undefined ? { providerFactory: opts.providerFactory } : {},
|
|
144382
145556
|
...opts.env !== undefined ? { env: opts.env } : {},
|
|
144383
145557
|
...opts.fetch !== undefined ? { fetch: opts.fetch } : {},
|
|
144384
|
-
timeoutMs:
|
|
145558
|
+
timeoutMs: Math.max(0, deadline - performance.now())
|
|
144385
145559
|
});
|
|
144386
|
-
if (
|
|
145560
|
+
if (opts.signal?.aborted)
|
|
145561
|
+
return;
|
|
145562
|
+
if (classification === undefined)
|
|
144387
145563
|
return;
|
|
145564
|
+
const baseline = { providerId: opts.sessionProvider, modelId: opts.sessionModel };
|
|
145565
|
+
const skippedJev = opts.jevEnabled && opts.jevAllowed === false ? "jev: caller policy denied" : opts.jevEnabled && external.value !== "on" ? "jev: external policy denied" : opts.jevEnabled && resolveJevClassifierCredential(opts.env).key === undefined ? "jev: credentials unavailable" : undefined;
|
|
145566
|
+
if (!classification.result.ok) {
|
|
145567
|
+
const failures = classification.trace.flatMap((stage) => stage.result.ok ? [] : [`${stage.source}: ${stage.result.reason}`]);
|
|
145568
|
+
return {
|
|
145569
|
+
classification,
|
|
145570
|
+
baseline,
|
|
145571
|
+
assignment: { kind: "session-default" },
|
|
145572
|
+
fallbackReason: redactSensitiveText([
|
|
145573
|
+
skippedJev,
|
|
145574
|
+
...failures,
|
|
145575
|
+
fallbackModel === undefined ? "no available authorized sufficient classifier" : undefined,
|
|
145576
|
+
classification.result.reason
|
|
145577
|
+
].filter(Boolean).join("; "))
|
|
145578
|
+
};
|
|
145579
|
+
}
|
|
144388
145580
|
const category = classification.result.category;
|
|
144389
145581
|
const location = { cwd: opts.cwd, ...opts.userConfigDir !== undefined ? { userConfigDir: opts.userConfigDir } : {} };
|
|
144390
145582
|
const [project, user] = await Promise.all([loadRoutingConfig("project", location), loadRoutingConfig("user", location)]);
|
|
144391
|
-
|
|
144392
|
-
|
|
145583
|
+
if (opts.signal?.aborted)
|
|
145584
|
+
return;
|
|
145585
|
+
const connected = connectedPredicateFrom(providers);
|
|
144393
145586
|
const available = availablePredicateFromProfiles(profiles);
|
|
144394
|
-
|
|
145587
|
+
let fallbackReason;
|
|
145588
|
+
const connectedForTurn = (providerId, modelId) => {
|
|
145589
|
+
if (modelId !== undefined) {
|
|
145590
|
+
const reason = !connected(providerId, modelId) ? "model not connected" : !externalAllowed(providerId, modelId) ? "external policy denied" : !available(providerId, modelId) ? "model unavailable" : !(opts.executorAllowed?.(providerId, modelId) ?? true) ? "executor policy denied" : opts.providerFactory === undefined && !hasCredential(providerId, env) ? "credentials unavailable" : undefined;
|
|
145591
|
+
if (reason !== undefined) {
|
|
145592
|
+
fallbackReason ??= `${providerId}/${modelId}: ${reason}`;
|
|
145593
|
+
return false;
|
|
145594
|
+
}
|
|
145595
|
+
return true;
|
|
145596
|
+
}
|
|
145597
|
+
const defaultId = resolveProviderDefaultModelId(providerId);
|
|
145598
|
+
const reason = !connected(providerId) ? "provider not connected" : defaultId === undefined ? "no documented provider default" : !connected(providerId, defaultId) ? "default model not connected" : !externalAllowed(providerId, defaultId) ? "external policy denied" : !available(providerId, defaultId) ? "default model unavailable" : !(opts.executorAllowed?.(providerId, defaultId) ?? true) ? "executor policy denied" : opts.providerFactory === undefined && !hasCredential(providerId, env) ? "credentials unavailable" : undefined;
|
|
145599
|
+
if (reason !== undefined) {
|
|
145600
|
+
fallbackReason ??= `${providerId} (provider default): ${reason}`;
|
|
145601
|
+
return false;
|
|
145602
|
+
}
|
|
145603
|
+
return true;
|
|
145604
|
+
};
|
|
145605
|
+
const sessionProvider = providers.find((p) => p.name === opts.sessionProvider);
|
|
144395
145606
|
const models = sessionProvider?.models ?? [opts.sessionModel];
|
|
144396
145607
|
const derived = deriveDefaultTable(opts.sessionProvider, models, profiles, opts.sessionModel);
|
|
144397
|
-
const resolved = resolveCategoryDetailed(category, { project: project.table, user: user.table, derived },
|
|
144398
|
-
|
|
144399
|
-
|
|
144400
|
-
|
|
145608
|
+
const resolved = resolveCategoryDetailed(category, { project: project.table, user: user.table, derived }, connectedForTurn, available);
|
|
145609
|
+
fallbackReason ??= resolved.rejected === undefined ? skippedJev : describeRejectionNotice(resolved.rejected, resolved.assignment);
|
|
145610
|
+
const stageFailures = classification.trace.flatMap((stage) => stage.result.ok ? [] : [`${stage.source}: ${stage.result.reason}`]);
|
|
145611
|
+
const reasons = [...new Set([skippedJev, ...stageFailures, fallbackReason].filter((reason) => reason !== undefined))];
|
|
145612
|
+
fallbackReason = reasons.length === 0 ? undefined : redactSensitiveText(reasons.join("; "));
|
|
145613
|
+
const result = {
|
|
145614
|
+
classification,
|
|
145615
|
+
category,
|
|
145616
|
+
baseline,
|
|
145617
|
+
assignment: resolved.assignment,
|
|
145618
|
+
...fallbackReason !== undefined ? { fallbackReason: redactSensitiveText(fallbackReason) } : {}
|
|
145619
|
+
};
|
|
145620
|
+
if (resolved.assignment.kind === "session-default")
|
|
145621
|
+
return result;
|
|
144401
145622
|
if (resolved.assignment.kind === "model") {
|
|
144402
|
-
return {
|
|
145623
|
+
return { ...result, routed: { providerId: resolved.assignment.providerId, modelId: resolved.assignment.modelId } };
|
|
144403
145624
|
}
|
|
144404
|
-
|
|
145625
|
+
const modelId = resolveProviderDefaultModelId(resolved.assignment.providerId);
|
|
145626
|
+
return modelId === undefined ? result : { ...result, routed: { providerId: resolved.assignment.providerId, modelId } };
|
|
145627
|
+
}
|
|
145628
|
+
function classificationSource(result) {
|
|
145629
|
+
if (!result.classification.result.ok)
|
|
145630
|
+
return "none";
|
|
145631
|
+
const source = result.classification.result.source;
|
|
145632
|
+
return source === "jev" ? "JEV" : source === "main-model" ? "fallback" : source;
|
|
144405
145633
|
}
|
|
144406
145634
|
function renderRoutingFallbackLine(result) {
|
|
144407
145635
|
if (result === undefined)
|
|
144408
145636
|
return "[route fallback: no classifier category; using session model]";
|
|
144409
|
-
|
|
145637
|
+
const target = result.baseline === undefined ? "session model" : `${result.baseline.providerId}/${result.baseline.modelId}`;
|
|
145638
|
+
return redactSensitiveText(`[route fallback: ${result.category ?? "no classifier category"} -> ${describeAssignment(result.assignment)}; classifier: ${classificationSource(result)}${result.fallbackReason !== undefined ? `; ${result.fallbackReason}` : ""}; using ${target}]`);
|
|
144410
145639
|
}
|
|
144411
145640
|
function pct6(n) {
|
|
144412
145641
|
return `${Math.round(n * 100)}%`;
|
|
@@ -144414,9 +145643,9 @@ function pct6(n) {
|
|
|
144414
145643
|
function renderRoutingTagLine(result) {
|
|
144415
145644
|
if (result.routed === undefined)
|
|
144416
145645
|
return;
|
|
144417
|
-
const source = result
|
|
145646
|
+
const source = classificationSource(result);
|
|
144418
145647
|
const confidence = result.classification.result.ok ? ` ${pct6(result.classification.result.confidence)}` : "";
|
|
144419
|
-
return `[${result.category} -> ${result.routed.providerId}/${result.routed.modelId}] (${source}${confidence})`;
|
|
145648
|
+
return `[${result.category} -> ${result.routed.providerId}/${result.routed.modelId}] (${source}${confidence}${result.fallbackReason !== undefined ? `; fallback: ${result.fallbackReason}` : ""})`;
|
|
144420
145649
|
}
|
|
144421
145650
|
function renderRoutingSidebarValue(enabled, routedCount, lastCategory) {
|
|
144422
145651
|
if (!enabled)
|
|
@@ -144429,6 +145658,14 @@ var init_routing_classifier_source = __esm(() => {
|
|
|
144429
145658
|
init_table();
|
|
144430
145659
|
init_config2();
|
|
144431
145660
|
init_derive_default_table();
|
|
145661
|
+
init_provider_default();
|
|
145662
|
+
init_classifier_model();
|
|
145663
|
+
init_single_turn();
|
|
145664
|
+
init_shell_config();
|
|
145665
|
+
init_service2();
|
|
145666
|
+
init_jev_classifier();
|
|
145667
|
+
init_external_switch();
|
|
145668
|
+
init_external_providers();
|
|
144432
145669
|
init_model_profile();
|
|
144433
145670
|
TURN_CLASSIFIER_CATEGORIES = ROUTING_CATEGORIES.filter((c) => c !== "default");
|
|
144434
145671
|
});
|
|
@@ -150462,6 +151699,53 @@ function onKeypress3(r, handler) {
|
|
|
150462
151699
|
r._internalKeyInput.onInternal("keypress", handler);
|
|
150463
151700
|
return () => r._internalKeyInput.offInternal("keypress", handler);
|
|
150464
151701
|
}
|
|
151702
|
+
function focusedNode(r) {
|
|
151703
|
+
return r.currentFocusedRenderable;
|
|
151704
|
+
}
|
|
151705
|
+
function canTakeKeyboard(node, root) {
|
|
151706
|
+
let current = node;
|
|
151707
|
+
while (current !== null && current !== undefined) {
|
|
151708
|
+
if (current.isDestroyed === true || current.visible === false) {
|
|
151709
|
+
return false;
|
|
151710
|
+
}
|
|
151711
|
+
if (current === root) {
|
|
151712
|
+
return true;
|
|
151713
|
+
}
|
|
151714
|
+
current = current.parent;
|
|
151715
|
+
}
|
|
151716
|
+
return false;
|
|
151717
|
+
}
|
|
151718
|
+
function resolveHandBack(start, root) {
|
|
151719
|
+
const seen = new Set;
|
|
151720
|
+
let candidate = start;
|
|
151721
|
+
while (candidate !== null && !seen.has(candidate)) {
|
|
151722
|
+
if (canTakeKeyboard(candidate, root)) {
|
|
151723
|
+
return candidate;
|
|
151724
|
+
}
|
|
151725
|
+
seen.add(candidate);
|
|
151726
|
+
candidate = handBackFrom.get(candidate) ?? modalFocusFallback(candidate);
|
|
151727
|
+
}
|
|
151728
|
+
return;
|
|
151729
|
+
}
|
|
151730
|
+
function handBackFocus(r, previous, mounted, title) {
|
|
151731
|
+
if (r.isDestroyed === true) {
|
|
151732
|
+
return;
|
|
151733
|
+
}
|
|
151734
|
+
const current = focusedNode(r);
|
|
151735
|
+
if (current !== null && !mounted.some((node) => containsNode(node, current))) {
|
|
151736
|
+
debugEvent("choice.focus-kept", { title, focused: current.id ?? null });
|
|
151737
|
+
return;
|
|
151738
|
+
}
|
|
151739
|
+
const target = resolveHandBack(previous, r.root);
|
|
151740
|
+
if (target === undefined || modalHides(r, target)) {
|
|
151741
|
+
debugEvent("choice.focus-not-restored", { title, previous: previous?.id ?? null, modal: target !== undefined });
|
|
151742
|
+
return;
|
|
151743
|
+
}
|
|
151744
|
+
try {
|
|
151745
|
+
target.focus();
|
|
151746
|
+
debugEvent("choice.focus-restored", { title, focused: target.id ?? null });
|
|
151747
|
+
} catch {}
|
|
151748
|
+
}
|
|
150465
151749
|
function dockBusy(dock) {
|
|
150466
151750
|
return (dockDepth.get(dock) ?? 0) > 0;
|
|
150467
151751
|
}
|
|
@@ -150518,6 +151802,7 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150518
151802
|
return Promise.resolve({ kind: "id", id: request.cancelId });
|
|
150519
151803
|
}
|
|
150520
151804
|
return new Promise((resolve) => {
|
|
151805
|
+
const previousFocus = focusedNode(r);
|
|
150521
151806
|
const ownEnabled = request.ownAnswer !== undefined;
|
|
150522
151807
|
const options = request.options.map((o) => ({
|
|
150523
151808
|
...o,
|
|
@@ -150568,6 +151853,7 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150568
151853
|
scrollY: true,
|
|
150569
151854
|
contentOptions: { flexDirection: "column" }
|
|
150570
151855
|
});
|
|
151856
|
+
handBackFrom.set(subtitleScroll, previousFocus);
|
|
150571
151857
|
for (const [i, line] of subtitleLines.entries()) {
|
|
150572
151858
|
subtitleScroll.content.add(new otui.TextRenderable(r, {
|
|
150573
151859
|
id: `ch-sub-line-${i}-${Date.now()}`,
|
|
@@ -150591,6 +151877,7 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150591
151877
|
contentOptions: { flexDirection: "column" }
|
|
150592
151878
|
});
|
|
150593
151879
|
dock.add(optionsScroll);
|
|
151880
|
+
handBackFrom.set(optionsScroll, previousFocus);
|
|
150594
151881
|
const optionsBox = optionsScroll.content;
|
|
150595
151882
|
const rows = [];
|
|
150596
151883
|
const paintOptions = () => {
|
|
@@ -150651,6 +151938,7 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150651
151938
|
content: otui.t`${dimChunk(otui, picked === undefined ? "Your own answer \u2014 Enter sends, Esc goes back to the options" : `Reason for "${picked.label}" \u2014 Enter sends, Esc goes back to the options`)}`
|
|
150652
151939
|
});
|
|
150653
151940
|
ownInput = new otui.InputRenderable(r, { id: `ch-own-input-${Date.now()}`, value: "", marginTop: 0 });
|
|
151941
|
+
handBackFrom.set(ownInput, previousFocus);
|
|
150654
151942
|
dock.add(ownNote);
|
|
150655
151943
|
dock.add(ownInput);
|
|
150656
151944
|
paintTitle();
|
|
@@ -150707,7 +151995,8 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150707
151995
|
id: rowId,
|
|
150708
151996
|
width: "100%",
|
|
150709
151997
|
flexDirection: "column",
|
|
150710
|
-
onMouseDown: () => {
|
|
151998
|
+
onMouseDown: (event) => {
|
|
151999
|
+
event.preventDefault();
|
|
150711
152000
|
finish(o.id, "click");
|
|
150712
152001
|
}
|
|
150713
152002
|
});
|
|
@@ -150765,6 +152054,9 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150765
152054
|
}
|
|
150766
152055
|
} catch {}
|
|
150767
152056
|
dock.visible = false;
|
|
152057
|
+
queueMicrotask(() => {
|
|
152058
|
+
handBackFocus(r, previousFocus, mounted, request.title);
|
|
152059
|
+
});
|
|
150768
152060
|
setTimeout(() => {
|
|
150769
152061
|
for (const node of mounted) {
|
|
150770
152062
|
try {
|
|
@@ -150874,12 +152166,13 @@ function presentComposerChoice(otui, r, dock, request) {
|
|
|
150874
152166
|
});
|
|
150875
152167
|
});
|
|
150876
152168
|
}
|
|
150877
|
-
var OWN_ROW_ID = "\x00own-answer", DEFAULT_ACCEPT_DELAY_MS = 400, MAX_SUBTITLE_CHARS = 8000, MAX_SUBTITLE_ROWS = 10, SUBTITLE_INLINE_MAX_CHARS = 76, OPTION_ROWS = 2, MAX_OPTIONS_ROWS = 16, dockChain, dockDepth;
|
|
152169
|
+
var OWN_ROW_ID = "\x00own-answer", DEFAULT_ACCEPT_DELAY_MS = 400, MAX_SUBTITLE_CHARS = 8000, MAX_SUBTITLE_ROWS = 10, SUBTITLE_INLINE_MAX_CHARS = 76, OPTION_ROWS = 2, MAX_OPTIONS_ROWS = 16, handBackFrom, dockChain, dockDepth;
|
|
150878
152170
|
var init_composer_choice = __esm(() => {
|
|
150879
152171
|
init_theme();
|
|
150880
152172
|
init_theme_text();
|
|
150881
152173
|
init_modal_host();
|
|
150882
152174
|
init_debug_log();
|
|
152175
|
+
handBackFrom = new WeakMap;
|
|
150883
152176
|
dockChain = new WeakMap;
|
|
150884
152177
|
dockDepth = new WeakMap;
|
|
150885
152178
|
});
|
|
@@ -155518,6 +156811,15 @@ var init_digest_summary = __esm(() => {
|
|
|
155518
156811
|
import { randomUUID as randomUUID41 } from "crypto";
|
|
155519
156812
|
import { mkdir as mkdir68, mkdtemp as mkdtemp10, rm as rm26, writeFile as writeFile44 } from "fs/promises";
|
|
155520
156813
|
import path303 from "path";
|
|
156814
|
+
function scrubDigestReportLocator(locator, env) {
|
|
156815
|
+
const scrubbed = scrubGrantedOutput(locator, env);
|
|
156816
|
+
const basename = path303.basename(locator);
|
|
156817
|
+
if (!/^dig-\d{8}T\d{6}-[a-f0-9]{8}\.md$/.test(basename))
|
|
156818
|
+
return scrubbed;
|
|
156819
|
+
const from = locator.length - basename.length;
|
|
156820
|
+
const phones = detectPii(locator).filter((m) => m.mask === "phone" && m.start >= from);
|
|
156821
|
+
return scrubbed === applyRedaction(locator, phones) ? locator : scrubbed;
|
|
156822
|
+
}
|
|
155521
156823
|
function capForDelivery(text, reportPath) {
|
|
155522
156824
|
if (text.length <= DELIVERY_TEXT_MAX)
|
|
155523
156825
|
return text;
|
|
@@ -155691,8 +156993,8 @@ async function runLocked2(projectRoot, entry, action, deps, runId, now) {
|
|
|
155691
156993
|
const statusReason = stopReason ?? (crashed !== undefined ? `the run threw: ${crashed}` : everySourceFailed ? "no source could be read" : undefined);
|
|
155692
156994
|
const statusDetail = everySourceFailed && stopReason === undefined && crashed === undefined ? partial.slice(0, 6).map((f) => `
|
|
155693
156995
|
- ${f.source}: ${f.detail}`).join("") : "";
|
|
155694
|
-
const message = statusReason !== undefined ? scrubGrantedOutput(`Digest ${entry.name} stopped \u2014 ${statusReason}${statusDetail}${reportPath !== undefined ? `
|
|
155695
|
-
Report: ${reportPath}` : ""}
|
|
156996
|
+
const message = statusReason !== undefined ? `${scrubGrantedOutput(`Digest ${entry.name} stopped \u2014 ${statusReason}${statusDetail}`, env)}${reportPath !== undefined ? `
|
|
156997
|
+
Report: ${scrubDigestReportLocator(reportPath, env)}` : ""}` : capForDelivery(scrubGrantedOutput(summaryText !== undefined ? `Summary (written by the model)
|
|
155696
156998
|
${summaryText}
|
|
155697
156999
|
|
|
155698
157000
|
${body}` : body, env), reportPath);
|
|
@@ -155769,6 +157071,7 @@ var init_digest_run = __esm(() => {
|
|
|
155769
157071
|
init_trigger_dispatch();
|
|
155770
157072
|
init_fs();
|
|
155771
157073
|
init_maintenance_lock();
|
|
157074
|
+
init_service2();
|
|
155772
157075
|
init_digest_config();
|
|
155773
157076
|
init_granted_tools();
|
|
155774
157077
|
init_schedule_verify();
|
|
@@ -160059,7 +161362,6 @@ async function launchTuiAgentShell(opts) {
|
|
|
160059
161362
|
let routingEnabled = loadShellConfig().routingClassifier?.enabled === true;
|
|
160060
161363
|
let routingRoutedCount = 0;
|
|
160061
161364
|
let routingLastCategory;
|
|
160062
|
-
let sessionModelExplicit = false;
|
|
160063
161365
|
const refreshRoutingSidebar = () => {
|
|
160064
161366
|
clearTranscriptChildren(sbRoute);
|
|
160065
161367
|
if (!routingEnabled)
|
|
@@ -161950,7 +163252,6 @@ ${formatThemeList(getThemeId())}`);
|
|
|
161950
163252
|
};
|
|
161951
163253
|
const switchTo = async (ns) => {
|
|
161952
163254
|
currentSel = ns;
|
|
161953
|
-
sessionModelExplicit = true;
|
|
161954
163255
|
deps = {
|
|
161955
163256
|
...await opts.makeAgentDeps(ns, liveSlateSession, busClientRef),
|
|
161956
163257
|
onContextCompaction,
|
|
@@ -163438,23 +164739,22 @@ ${formatThemeList(getThemeId())}`);
|
|
|
163438
164739
|
const sessionDeps = deps;
|
|
163439
164740
|
(async () => {
|
|
163440
164741
|
let deps = sessionDeps;
|
|
164742
|
+
const turnSignal = foregroundOperation.signal;
|
|
163441
164743
|
let routingOutcome;
|
|
163442
|
-
if (routingEnabled &&
|
|
163443
|
-
io.onSystem?.(`[route skipped: manual /model selection pins this session]
|
|
163444
|
-
`);
|
|
163445
|
-
}
|
|
163446
|
-
if (routingEnabled && !sessionModelExplicit && origin === "operator" && line.trim().length > 0) {
|
|
164744
|
+
if (routingEnabled && origin === "operator" && line.trim().length > 0) {
|
|
163447
164745
|
try {
|
|
163448
164746
|
routingOutcome = await runRoutingClassifierForTurn(line, {
|
|
163449
164747
|
enabled: routingEnabled,
|
|
164748
|
+
signal: turnSignal,
|
|
163450
164749
|
jevEnabled: routingEnabled,
|
|
163451
164750
|
cwd: opts.session?.cwd ?? process.cwd(),
|
|
163452
164751
|
detected: opts.detected ?? [],
|
|
164752
|
+
providers: loadRoutingProviders,
|
|
163453
164753
|
sessionProvider: currentSel.provider,
|
|
163454
164754
|
sessionModel: currentSel.model
|
|
163455
164755
|
});
|
|
163456
164756
|
} catch {}
|
|
163457
|
-
if (routingOutcome?.routed !== undefined) {
|
|
164757
|
+
if (!turnSignal.aborted && routingOutcome?.routed !== undefined) {
|
|
163458
164758
|
try {
|
|
163459
164759
|
deps = {
|
|
163460
164760
|
...await opts.makeAgentDeps({ provider: routingOutcome.routed.providerId, model: routingOutcome.routed.modelId }, liveSlateSession, busClientRef),
|
|
@@ -163463,19 +164763,22 @@ ${formatThemeList(getThemeId())}`);
|
|
|
163463
164763
|
...sessionDeps.busAck !== undefined ? { busAck: sessionDeps.busAck } : {},
|
|
163464
164764
|
...sessionDeps.busLeases !== undefined ? { busLeases: sessionDeps.busLeases } : {}
|
|
163465
164765
|
};
|
|
163466
|
-
|
|
163467
|
-
|
|
163468
|
-
|
|
163469
|
-
|
|
163470
|
-
|
|
163471
|
-
|
|
164766
|
+
if (!turnSignal.aborted) {
|
|
164767
|
+
routingRoutedCount += 1;
|
|
164768
|
+
routingLastCategory = routingOutcome.category;
|
|
164769
|
+
refreshRoutingSidebar();
|
|
164770
|
+
const tag = renderRoutingTagLine(routingOutcome);
|
|
164771
|
+
if (tag !== undefined)
|
|
164772
|
+
io.onSystem?.(`${tag}
|
|
163472
164773
|
`);
|
|
164774
|
+
}
|
|
163473
164775
|
} catch {
|
|
163474
164776
|
deps = sessionDeps;
|
|
163475
|
-
|
|
164777
|
+
if (!turnSignal.aborted)
|
|
164778
|
+
io.onSystem?.(`[route fallback: selected model could not be prepared; check availability and external policy; using session model]
|
|
163476
164779
|
`);
|
|
163477
164780
|
}
|
|
163478
|
-
} else {
|
|
164781
|
+
} else if (!turnSignal.aborted) {
|
|
163479
164782
|
io.onSystem?.(`${renderRoutingFallbackLine(routingOutcome)}
|
|
163480
164783
|
`);
|
|
163481
164784
|
}
|
|
@@ -163485,12 +164788,11 @@ ${formatThemeList(getThemeId())}`);
|
|
|
163485
164788
|
syncArchive();
|
|
163486
164789
|
rewindRecorder.beginTurn({ archiveIndex: archive.length, prompt: line });
|
|
163487
164790
|
const foregroundIo = createForegroundAgentIoFacade(foregroundOperation, operation, io, telegramTurn ? telegramTurnIo : {});
|
|
163488
|
-
|
|
163489
|
-
runAgentTurn(foregroundIo, deps, history, line, {
|
|
164791
|
+
(turnSignal.aborted ? Promise.resolve() : runAgentTurn(foregroundIo, deps, history, line, {
|
|
163490
164792
|
signal: turnSignal,
|
|
163491
164793
|
...origin === "operator" ? {} : { origin },
|
|
163492
164794
|
...slateSession !== undefined ? { slateSession, pruneArchive: true } : {}
|
|
163493
|
-
}).finally(() => {
|
|
164795
|
+
})).finally(() => {
|
|
163494
164796
|
foregroundOperation.settle(operation);
|
|
163495
164797
|
remoteQueue.turnSettled({ failed: turnFailed, aborted: turnSignal.aborted });
|
|
163496
164798
|
if (foregroundOperation.isDisposed)
|
|
@@ -201678,7 +202980,22 @@ async function startServeListener(input) {
|
|
|
201678
202980
|
} catch (error) {
|
|
201679
202981
|
console.error(`keryx serve: remote surface did not close cleanly: ${error instanceof Error ? error.message : String(error)}`);
|
|
201680
202982
|
}
|
|
201681
|
-
|
|
202983
|
+
if (input.remote === undefined) {
|
|
202984
|
+
await server.stop(true);
|
|
202985
|
+
} else {
|
|
202986
|
+
let timer;
|
|
202987
|
+
try {
|
|
202988
|
+
await Promise.race([
|
|
202989
|
+
server.stop(false),
|
|
202990
|
+
new Promise((resolve) => {
|
|
202991
|
+
timer = setTimeout(resolve, 250);
|
|
202992
|
+
})
|
|
202993
|
+
]);
|
|
202994
|
+
await server.stop(true);
|
|
202995
|
+
} finally {
|
|
202996
|
+
clearTimeout(timer);
|
|
202997
|
+
}
|
|
202998
|
+
}
|
|
201682
202999
|
state = "stopped";
|
|
201683
203000
|
}
|
|
201684
203001
|
}
|
|
@@ -213769,7 +215086,7 @@ function parseCategory(raw) {
|
|
|
213769
215086
|
return;
|
|
213770
215087
|
return raw;
|
|
213771
215088
|
}
|
|
213772
|
-
async function
|
|
215089
|
+
async function defaultProviders() {
|
|
213773
215090
|
await Promise.resolve().then(() => init_provider_catalog());
|
|
213774
215091
|
const catalog = await loadOrRefreshProviderCatalog({ fetch, env: envWithSavedApiKeys() });
|
|
213775
215092
|
return catalogToFlatPickerProviders(catalog);
|
|
@@ -213782,7 +215099,7 @@ function defaultSession(userConfigDir) {
|
|
|
213782
215099
|
return { providerId: cfg.provider, modelId: cfg.model };
|
|
213783
215100
|
}
|
|
213784
215101
|
async function runList5(args, location, deps) {
|
|
213785
|
-
const loadProviders = deps.providers ??
|
|
215102
|
+
const loadProviders = deps.providers ?? defaultProviders;
|
|
213786
215103
|
const loadSession = deps.session ?? (() => defaultSession(location.userConfigDir));
|
|
213787
215104
|
const [project, user, providers] = await Promise.all([
|
|
213788
215105
|
loadRoutingConfig("project", location),
|