@yeaft/webchat-agent 1.0.33 → 1.0.34
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/package.json +1 -1
- package/yeaft/conversation/persist.js +28 -118
package/package.json
CHANGED
|
@@ -1030,10 +1030,7 @@ export class ConversationStore {
|
|
|
1030
1030
|
*/
|
|
1031
1031
|
loadOlderBySession(sessionId, beforeSeq, turnsLimit = DEFAULT_RECENT_TURNS) {
|
|
1032
1032
|
if (!sessionId) return { messages: [], oldestSeq: null, hasMore: false };
|
|
1033
|
-
const
|
|
1034
|
-
const cold = this.#loadSessionColdMessages(sessionId);
|
|
1035
|
-
// Cold ids strictly < hot ids by construction → chronological concat.
|
|
1036
|
-
const all = [...cold, ...hot];
|
|
1033
|
+
const all = this.#loadSessionMessages(sessionId);
|
|
1037
1034
|
const cutoff = Number.isFinite(beforeSeq) ? beforeSeq : Infinity;
|
|
1038
1035
|
const prefix = all.filter(m => m && m.sessionId === sessionId
|
|
1039
1036
|
&& !isHiddenConversationRow(m)
|
|
@@ -1072,24 +1069,28 @@ export class ConversationStore {
|
|
|
1072
1069
|
if (!sessionId || !(turnsLimit > 0)) return { messages: [], oldestSeq: null, hasMore: false };
|
|
1073
1070
|
|
|
1074
1071
|
const cutoff = Number.isFinite(beforeSeq) ? beforeSeq : Infinity;
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
turnsLimit
|
|
1085
|
-
);
|
|
1072
|
+
const all = this.#loadSessionMessages(sessionId);
|
|
1073
|
+
const visible = all.filter(m => m && m.sessionId === sessionId
|
|
1074
|
+
&& !isHiddenConversationRow(m)
|
|
1075
|
+
&& (m.role === 'user' || m.role === 'assistant')
|
|
1076
|
+
&& parseSeqFromId(m.id) < cutoff);
|
|
1077
|
+
|
|
1078
|
+
if (visible.length === 0) return { messages: [], oldestSeq: null, hasMore: false };
|
|
1079
|
+
|
|
1080
|
+
const sliced = sliceLastNTurns(visible, turnsLimit);
|
|
1086
1081
|
|
|
1087
|
-
//
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1082
|
+
// Turn-based hasMore: an EARLIER turn boundary was dropped.
|
|
1083
|
+
const oldestSlicedSeq = sliced.length ? parseSeqFromId(sliced[0].id) : NaN;
|
|
1084
|
+
const oldestVisibleSeq = parseSeqFromId(visible[0].id);
|
|
1085
|
+
const hasMore = sliced.length > 0
|
|
1086
|
+
&& Number.isFinite(oldestSlicedSeq)
|
|
1087
|
+
&& Number.isFinite(oldestVisibleSeq)
|
|
1088
|
+
&& oldestSlicedSeq > oldestVisibleSeq;
|
|
1089
|
+
|
|
1090
|
+
// Visible history is for UI replay, not LLM context. Strip tool-call
|
|
1091
|
+
// metadata — don't run pairSanitize (it can incorrectly drop assistant
|
|
1092
|
+
// messages that reference tool calls stripped from the UI).
|
|
1093
|
+
const messages = sliced.map(m => {
|
|
1093
1094
|
if (m && m.role === 'assistant' && Array.isArray(m.toolCalls) && m.toolCalls.length > 0) {
|
|
1094
1095
|
const { toolCalls, ...rest } = m;
|
|
1095
1096
|
return { ...rest, toolSummaryCount: toolCalls.length };
|
|
@@ -1101,7 +1102,7 @@ export class ConversationStore {
|
|
|
1101
1102
|
return {
|
|
1102
1103
|
messages,
|
|
1103
1104
|
oldestSeq: Number.isFinite(oldestSeq) ? oldestSeq : null,
|
|
1104
|
-
hasMore
|
|
1105
|
+
hasMore,
|
|
1105
1106
|
};
|
|
1106
1107
|
}
|
|
1107
1108
|
|
|
@@ -1120,9 +1121,7 @@ export class ConversationStore {
|
|
|
1120
1121
|
const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? opts.limit : 500;
|
|
1121
1122
|
const cutoff = Number.isFinite(afterSeq) && afterSeq >= 0 ? afterSeq : null;
|
|
1122
1123
|
if (cutoff === null) return { messages: [], latestSeq: null };
|
|
1123
|
-
const
|
|
1124
|
-
const cold = this.#loadSessionColdMessages(sessionId);
|
|
1125
|
-
const all = [...cold, ...hot].sort(compareMessagesBySeq);
|
|
1124
|
+
const all = this.#loadSessionMessages(sessionId);
|
|
1126
1125
|
const after = all.filter((m) => {
|
|
1127
1126
|
if (!m || m.sessionId !== sessionId) return false;
|
|
1128
1127
|
if (isHiddenConversationRow(m)) return false;
|
|
@@ -1707,21 +1706,13 @@ export class ConversationStore {
|
|
|
1707
1706
|
}
|
|
1708
1707
|
|
|
1709
1708
|
#loadSessionHotMessages(sessionId = null) {
|
|
1710
|
-
return
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
].sort(compareMessagesBySeq);
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
#loadSessionColdMessages(sessionId = null) {
|
|
1717
|
-
return [
|
|
1718
|
-
...this.#loadFromDir(this.#legacyColdDir, Infinity).filter(m => m?.sessionId),
|
|
1719
|
-
...this.#sessionMessageDirs('cold', sessionId).flatMap(dir => this.#loadFromDir(dir, Infinity)),
|
|
1720
|
-
].sort(compareMessagesBySeq);
|
|
1709
|
+
return this.#sessionMessageDirs('messages', sessionId)
|
|
1710
|
+
.flatMap(dir => this.#loadFromDir(dir, Infinity))
|
|
1711
|
+
.sort(compareMessagesBySeq);
|
|
1721
1712
|
}
|
|
1722
1713
|
|
|
1723
1714
|
#loadSessionMessages(sessionId = null) {
|
|
1724
|
-
return
|
|
1715
|
+
return this.#loadSessionHotMessages(sessionId);
|
|
1725
1716
|
}
|
|
1726
1717
|
|
|
1727
1718
|
#loadAllMessages() {
|
|
@@ -1748,87 +1739,6 @@ export class ConversationStore {
|
|
|
1748
1739
|
return total;
|
|
1749
1740
|
}
|
|
1750
1741
|
|
|
1751
|
-
#loadVisibleWindowBySession(dirs, sessionId, beforeSeq, turnsLimit) {
|
|
1752
|
-
const candidates = [];
|
|
1753
|
-
const seen = new Set();
|
|
1754
|
-
for (const dir of dirs) {
|
|
1755
|
-
if (!existsSync(dir)) continue;
|
|
1756
|
-
let files = [];
|
|
1757
|
-
try {
|
|
1758
|
-
files = readdirSync(dir);
|
|
1759
|
-
} catch (err) {
|
|
1760
|
-
if (!isPermissionError(err)) throw err;
|
|
1761
|
-
continue;
|
|
1762
|
-
}
|
|
1763
|
-
for (const file of files) {
|
|
1764
|
-
if (!file.endsWith('.md')) continue;
|
|
1765
|
-
const seq = parseSeqFromId(basename(file, '.md'));
|
|
1766
|
-
if (!Number.isFinite(seq) || seq >= beforeSeq) continue;
|
|
1767
|
-
const path = join(dir, file);
|
|
1768
|
-
if (seen.has(path)) continue;
|
|
1769
|
-
seen.add(path);
|
|
1770
|
-
candidates.push({ seq, path });
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
candidates.sort((a, b) => b.seq - a.seq);
|
|
1775
|
-
|
|
1776
|
-
const selected = [];
|
|
1777
|
-
const pendingBoundaryTail = [];
|
|
1778
|
-
let turnsSeen = 0;
|
|
1779
|
-
let openCanonical = null;
|
|
1780
|
-
let boundaryCanonical = null;
|
|
1781
|
-
let hasMore = false;
|
|
1782
|
-
|
|
1783
|
-
for (const candidate of candidates) {
|
|
1784
|
-
let raw = '';
|
|
1785
|
-
try {
|
|
1786
|
-
raw = readFileSync(candidate.path, 'utf8');
|
|
1787
|
-
} catch (err) {
|
|
1788
|
-
if (!isPermissionError(err)) throw err;
|
|
1789
|
-
continue;
|
|
1790
|
-
}
|
|
1791
|
-
if (!raw.includes(`sessionId: ${sessionId}`)) continue;
|
|
1792
|
-
if (!raw.includes('role: user') && !raw.includes('role: assistant')) continue;
|
|
1793
|
-
|
|
1794
|
-
const parsed = parseMessage(raw);
|
|
1795
|
-
if (!parsed || parsed.sessionId !== sessionId) continue;
|
|
1796
|
-
if (isHiddenConversationRow(parsed)) continue;
|
|
1797
|
-
if (parsed.role !== 'user' && parsed.role !== 'assistant') continue;
|
|
1798
|
-
|
|
1799
|
-
if (boundaryCanonical !== null) {
|
|
1800
|
-
if (parsed.role !== 'user') {
|
|
1801
|
-
pendingBoundaryTail.push(parsed);
|
|
1802
|
-
continue;
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
const canonical = canonicalUserTurnContent(parsed.content);
|
|
1806
|
-
if (canonical !== boundaryCanonical) {
|
|
1807
|
-
hasMore = true;
|
|
1808
|
-
break;
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
if (pendingBoundaryTail.length > 0) {
|
|
1812
|
-
selected.push(...pendingBoundaryTail.splice(0));
|
|
1813
|
-
}
|
|
1814
|
-
selected.push(parsed);
|
|
1815
|
-
continue;
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
selected.push(parsed);
|
|
1819
|
-
if (parsed.role === 'user') {
|
|
1820
|
-
const canonical = canonicalUserTurnContent(parsed.content);
|
|
1821
|
-
if (canonical != null && canonical !== openCanonical) {
|
|
1822
|
-
turnsSeen += 1;
|
|
1823
|
-
openCanonical = canonical;
|
|
1824
|
-
if (turnsSeen === turnsLimit) boundaryCanonical = canonical;
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
return { messages: selected.reverse(), hasMore };
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
1742
|
// task-314: per-thread sub-directory for forked threads.
|
|
1833
1743
|
#threadMsgDir(threadId) {
|
|
1834
1744
|
return join(this.#convDir, 'threads', threadId, 'messages');
|