@yeaft/webchat-agent 0.1.1096 → 0.1.1099
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
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal-control conversation row helpers.
|
|
3
|
+
*
|
|
4
|
+
* Some older transcripts persisted VP-only control prompts without reliable
|
|
5
|
+
* `internal: true` metadata. Treat those legacy content signatures as hidden
|
|
6
|
+
* conversation rows so they never replay into user-visible history or future
|
|
7
|
+
* model context.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function isInternalControlContent(content) {
|
|
11
|
+
if (typeof content !== 'string') return false;
|
|
12
|
+
const text = content.trimStart();
|
|
13
|
+
return text.startsWith('<task-result ')
|
|
14
|
+
|| /^\[system note\] You have called \S+ with the same arguments \d+ times\./.test(text);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isHiddenConversationRow(row) {
|
|
18
|
+
if (!row) return true;
|
|
19
|
+
if (row._reflection || row.internal || row.systemOnly || row.systemOnlyMessage) return true;
|
|
20
|
+
if (row.kind === 'compact_summary' || row._compactSummary) return true;
|
|
21
|
+
return isInternalControlContent(row.content);
|
|
22
|
+
}
|
|
@@ -28,6 +28,7 @@ import { join, basename } from 'path';
|
|
|
28
28
|
import { isPermissionError } from '../init.js';
|
|
29
29
|
import { pairSanitize } from '../pair-sanitize.js';
|
|
30
30
|
import { countTurns, indexOfNthTurnFromEnd, sliceLastNTurns, stripVpMentionPrefix } from '../turn-utils.js';
|
|
31
|
+
import { isHiddenConversationRow } from './internal-control.js';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Default cold-start "recent window" size, expressed in TURNS (not raw
|
|
@@ -899,7 +900,7 @@ export class ConversationStore {
|
|
|
899
900
|
loadRecentBySession(sessionId, turnsLimit = DEFAULT_RECENT_TURNS) {
|
|
900
901
|
if (!sessionId) return [];
|
|
901
902
|
const all = this.#loadSessionMessages(sessionId);
|
|
902
|
-
const filtered = all.filter(m => m && m.sessionId === sessionId);
|
|
903
|
+
const filtered = all.filter(m => m && m.sessionId === sessionId && !isHiddenConversationRow(m));
|
|
903
904
|
if (turnsLimit === Infinity || turnsLimit < 0) return pairSanitize(filtered);
|
|
904
905
|
const sliced = sliceLastNTurns(filtered, turnsLimit);
|
|
905
906
|
// Warn once per (sessionId, storeDir) when truncation drops turns
|
|
@@ -943,8 +944,10 @@ export class ConversationStore {
|
|
|
943
944
|
* block contract and would never appear in another VP's context).
|
|
944
945
|
* - OTHER VPs' tool result rows (role:'tool'): DROP — they pair with
|
|
945
946
|
* stripped tool_use ids and would orphan on replay.
|
|
946
|
-
* -
|
|
947
|
-
*
|
|
947
|
+
* - Hidden conversation rows (`_reflection`, `internal`, `systemOnly`,
|
|
948
|
+
* `systemOnlyMessage`, compact summaries, and legacy internal-control
|
|
949
|
+
* content signatures): DROP — they are engine-private and never enter
|
|
950
|
+
* visible history or another VP's context.
|
|
948
951
|
*
|
|
949
952
|
* The output is pair-safe by construction for THIS VP's tool arcs and
|
|
950
953
|
* carries only summary-relevant text for the other VPs.
|
|
@@ -959,7 +962,7 @@ export class ConversationStore {
|
|
|
959
962
|
const out = [];
|
|
960
963
|
for (const m of all) {
|
|
961
964
|
if (!m || m.sessionId !== sessionId) continue;
|
|
962
|
-
if (m
|
|
965
|
+
if (isHiddenConversationRow(m)) continue;
|
|
963
966
|
if (m.role === 'user') {
|
|
964
967
|
out.push(m);
|
|
965
968
|
continue;
|
|
@@ -1033,6 +1036,7 @@ export class ConversationStore {
|
|
|
1033
1036
|
const all = [...cold, ...hot];
|
|
1034
1037
|
const cutoff = Number.isFinite(beforeSeq) ? beforeSeq : Infinity;
|
|
1035
1038
|
const prefix = all.filter(m => m && m.sessionId === sessionId
|
|
1039
|
+
&& !isHiddenConversationRow(m)
|
|
1036
1040
|
&& parseSeqFromId(m.id) < cutoff);
|
|
1037
1041
|
if (prefix.length === 0) return { messages: [], oldestSeq: null, hasMore: false };
|
|
1038
1042
|
const sliced = pairSanitize(sliceLastNTurns(prefix, turnsLimit));
|
|
@@ -1121,7 +1125,7 @@ export class ConversationStore {
|
|
|
1121
1125
|
const all = [...cold, ...hot].sort(compareMessagesBySeq);
|
|
1122
1126
|
const after = all.filter((m) => {
|
|
1123
1127
|
if (!m || m.sessionId !== sessionId) return false;
|
|
1124
|
-
if (m
|
|
1128
|
+
if (isHiddenConversationRow(m)) return false;
|
|
1125
1129
|
const seq = parseSeqFromId(m.id);
|
|
1126
1130
|
return Number.isFinite(seq) && seq > cutoff;
|
|
1127
1131
|
});
|
|
@@ -1605,7 +1609,7 @@ export class ConversationStore {
|
|
|
1605
1609
|
...this.#chatMessageDirs('messages', chatId).flatMap(dir => this.#loadFromDir(dir, Infinity)),
|
|
1606
1610
|
...this.#chatMessageDirs('cold', chatId).flatMap(dir => this.#loadFromDir(dir, Infinity)),
|
|
1607
1611
|
].sort(compareMessagesBySeq);
|
|
1608
|
-
const filtered = all.filter(m => m && m.chatId === chatId);
|
|
1612
|
+
const filtered = all.filter(m => m && m.chatId === chatId && !isHiddenConversationRow(m));
|
|
1609
1613
|
if (turnsLimit === Infinity || turnsLimit < 0) return pairSanitize(filtered);
|
|
1610
1614
|
return pairSanitize(sliceLastNTurns(filtered, turnsLimit));
|
|
1611
1615
|
}
|
|
@@ -1620,7 +1624,7 @@ export class ConversationStore {
|
|
|
1620
1624
|
const out = [];
|
|
1621
1625
|
for (const m of all) {
|
|
1622
1626
|
if (!m || m.chatId !== chatId) continue;
|
|
1623
|
-
if (m
|
|
1627
|
+
if (isHiddenConversationRow(m)) continue;
|
|
1624
1628
|
if (m.role === 'user') { out.push(m); continue; }
|
|
1625
1629
|
if (m.role === 'assistant') {
|
|
1626
1630
|
// Chat is 1:1 — every assistant row is "ours".
|
|
@@ -1789,7 +1793,7 @@ export class ConversationStore {
|
|
|
1789
1793
|
|
|
1790
1794
|
const parsed = parseMessage(raw);
|
|
1791
1795
|
if (!parsed || parsed.sessionId !== sessionId) continue;
|
|
1792
|
-
if (parsed
|
|
1796
|
+
if (isHiddenConversationRow(parsed)) continue;
|
|
1793
1797
|
if (parsed.role !== 'user' && parsed.role !== 'assistant') continue;
|
|
1794
1798
|
|
|
1795
1799
|
if (boundaryCanonical !== null) {
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from './history-compact.js';
|
|
63
63
|
import { persistYeaftAttachments, attachmentsForPersistence, persistedAttachmentPreviewPayload } from './attachments.js';
|
|
64
64
|
import { ConversationStore, parseSeqFromId } from './conversation/persist.js';
|
|
65
|
+
import { isHiddenConversationRow } from './conversation/internal-control.js';
|
|
65
66
|
import { sliceLastNTurns } from './turn-utils.js';
|
|
66
67
|
import { pairSanitize } from './pair-sanitize.js';
|
|
67
68
|
import { filterSnapshotForVp } from './snapshot-filter.js';
|
|
@@ -664,10 +665,7 @@ export function __testNormalizePersistedVisibleContent(content) {
|
|
|
664
665
|
}
|
|
665
666
|
|
|
666
667
|
function isPersistedInternalMessage(m) {
|
|
667
|
-
|
|
668
|
-
if (m._reflection || m.internal || m.systemOnly || m.systemOnlyMessage) return true;
|
|
669
|
-
if (m.kind === 'compact_summary' || m._compactSummary) return true;
|
|
670
|
-
return false;
|
|
668
|
+
return isHiddenConversationRow(m);
|
|
671
669
|
}
|
|
672
670
|
|
|
673
671
|
/**
|
|
@@ -5119,6 +5117,7 @@ export async function handleYeaftMcpReload(msg = {}) {
|
|
|
5119
5117
|
}
|
|
5120
5118
|
|
|
5121
5119
|
export const __testHooks = {
|
|
5120
|
+
loadVisibleGroupHistoryPage,
|
|
5122
5121
|
setSessionForTest(nextSession) {
|
|
5123
5122
|
session = nextSession || null;
|
|
5124
5123
|
},
|