@yeaft/webchat-agent 1.0.208 → 1.0.209
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/connection/message-router.js +5 -1
- package/index.js +1 -1
- package/local-runtime/server/handlers/agent-output.js +18 -0
- package/local-runtime/server/handlers/client-conversation.js +18 -0
- package/local-runtime/server/ws-agent.js +1 -1
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +137 -115
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/conversation/persist.js +124 -18
- package/yeaft/web-bridge.js +42 -10
|
Binary file
|
package/package.json
CHANGED
|
@@ -1577,40 +1577,72 @@ export class ConversationStore {
|
|
|
1577
1577
|
const limit = Math.min(50, Math.max(1, Number.isFinite(opts.limit) ? Math.floor(opts.limit) : 20));
|
|
1578
1578
|
const beforeSeq = Number.isFinite(opts.beforeSeq) ? opts.beforeSeq : Infinity;
|
|
1579
1579
|
const results = [];
|
|
1580
|
-
const seen = new Set();
|
|
1581
1580
|
let hasMore = false;
|
|
1582
1581
|
|
|
1583
|
-
for (const
|
|
1584
|
-
|
|
1585
|
-
if (message.role !== 'user' && message.role !== 'assistant') continue;
|
|
1586
|
-
if (!message.id || seen.has(message.id)) continue;
|
|
1587
|
-
seen.add(message.id);
|
|
1588
|
-
|
|
1589
|
-
const text = this.#visibleSearchText(message.content);
|
|
1582
|
+
for (const entry of this.#iterateVisibleResponseEntries(sessionId, { beforeSeq })) {
|
|
1583
|
+
const text = entry.textParts.join(' ');
|
|
1590
1584
|
const matchIndex = text.toLocaleLowerCase().indexOf(needle);
|
|
1591
1585
|
if (matchIndex < 0) continue;
|
|
1592
1586
|
if (results.length >= limit) {
|
|
1593
1587
|
hasMore = true;
|
|
1594
1588
|
break;
|
|
1595
1589
|
}
|
|
1596
|
-
|
|
1597
|
-
const seq = parseSeqFromId(message.id);
|
|
1598
|
-
if (!Number.isFinite(seq)) continue;
|
|
1599
1590
|
results.push({
|
|
1600
|
-
|
|
1601
|
-
turnId: message.turnId || message.threadId || message.id,
|
|
1602
|
-
seq,
|
|
1603
|
-
role: message.role,
|
|
1604
|
-
speakerVpId: message.speakerVpId || null,
|
|
1605
|
-
timestamp: message.ts || message.time || null,
|
|
1591
|
+
...this.#projectVisibleResponseEntry(entry),
|
|
1606
1592
|
snippet: this.#searchSnippet(text, matchIndex, needle.length),
|
|
1607
1593
|
});
|
|
1608
1594
|
}
|
|
1609
1595
|
|
|
1596
|
+
const lastResult = results[results.length - 1] || null;
|
|
1597
|
+
return {
|
|
1598
|
+
results: results.map(({ _beforeSeq, ...result }) => result),
|
|
1599
|
+
hasMore,
|
|
1600
|
+
nextBeforeSeq: hasMore && lastResult ? lastResult._beforeSeq : null,
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
* Load a lightweight outline page for one Session. Only user and assistant
|
|
1606
|
+
* text metadata is projected; tool payloads, attachments and full message
|
|
1607
|
+
* bodies never leave the Agent through this API.
|
|
1608
|
+
*
|
|
1609
|
+
* @param {string} sessionId
|
|
1610
|
+
* @param {{ limit?: number, beforeSeq?: number|null, includeTotal?: boolean }} [opts]
|
|
1611
|
+
* @returns {{ results: object[], hasMore: boolean, nextBeforeSeq: number|null, totalCount: number|null }}
|
|
1612
|
+
*/
|
|
1613
|
+
loadVisibleOutlineBySession(sessionId, opts = {}) {
|
|
1614
|
+
if (!sessionId) return { results: [], hasMore: false, nextBeforeSeq: null, totalCount: 0 };
|
|
1615
|
+
|
|
1616
|
+
const limit = Math.min(100, Math.max(1, Number.isFinite(opts.limit) ? Math.floor(opts.limit) : 50));
|
|
1617
|
+
const beforeSeq = Number.isFinite(opts.beforeSeq) ? opts.beforeSeq : Infinity;
|
|
1618
|
+
const newestFirst = [];
|
|
1619
|
+
let hasMore = false;
|
|
1620
|
+
|
|
1621
|
+
for (const entry of this.#iterateVisibleResponseEntries(sessionId, { beforeSeq })) {
|
|
1622
|
+
if (newestFirst.length >= limit) {
|
|
1623
|
+
hasMore = true;
|
|
1624
|
+
break;
|
|
1625
|
+
}
|
|
1626
|
+
const projected = this.#projectVisibleResponseEntry(entry);
|
|
1627
|
+
newestFirst.push({
|
|
1628
|
+
...projected,
|
|
1629
|
+
snippet: this.#outlineSnippet(entry.textParts.join(' ')),
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
let totalCount = null;
|
|
1634
|
+
if (opts.includeTotal !== false) {
|
|
1635
|
+
totalCount = 0;
|
|
1636
|
+
for (const _entry of this.#iterateVisibleResponseEntries(sessionId)) totalCount += 1;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
const oldestEntry = newestFirst[newestFirst.length - 1] || null;
|
|
1640
|
+
const results = newestFirst.reverse().map(({ _beforeSeq, ...entry }) => entry);
|
|
1610
1641
|
return {
|
|
1611
1642
|
results,
|
|
1612
1643
|
hasMore,
|
|
1613
|
-
nextBeforeSeq: hasMore &&
|
|
1644
|
+
nextBeforeSeq: hasMore && oldestEntry ? oldestEntry._beforeSeq : null,
|
|
1645
|
+
totalCount,
|
|
1614
1646
|
};
|
|
1615
1647
|
}
|
|
1616
1648
|
|
|
@@ -2381,6 +2413,75 @@ export class ConversationStore {
|
|
|
2381
2413
|
};
|
|
2382
2414
|
}
|
|
2383
2415
|
|
|
2416
|
+
*#iterateVisibleResponseEntries(sessionId, opts = {}) {
|
|
2417
|
+
const beforeSeq = Number.isFinite(opts.beforeSeq) ? opts.beforeSeq : Infinity;
|
|
2418
|
+
const seen = new Set();
|
|
2419
|
+
let current = null;
|
|
2420
|
+
|
|
2421
|
+
const visibleRow = (message) => {
|
|
2422
|
+
if (!message || message.sessionId !== sessionId || isHiddenConversationRow(message)) return null;
|
|
2423
|
+
if (message.role !== 'user' && message.role !== 'assistant') return null;
|
|
2424
|
+
if (!message.id || seen.has(message.id)) return null;
|
|
2425
|
+
seen.add(message.id);
|
|
2426
|
+
const seq = parseSeqFromId(message.id);
|
|
2427
|
+
if (!Number.isFinite(seq)) return null;
|
|
2428
|
+
const text = this.#visibleSearchText(message.content);
|
|
2429
|
+
const speakerVpId = message.speakerVpId || null;
|
|
2430
|
+
return {
|
|
2431
|
+
message,
|
|
2432
|
+
seq,
|
|
2433
|
+
text,
|
|
2434
|
+
speakerVpId,
|
|
2435
|
+
groupKey: message.role === 'assistant'
|
|
2436
|
+
? `assistant:${message.turnId || message.id}:${speakerVpId || ''}`
|
|
2437
|
+
: `user:${message.id}`,
|
|
2438
|
+
};
|
|
2439
|
+
};
|
|
2440
|
+
const startEntry = (row) => ({
|
|
2441
|
+
groupKey: row.groupKey,
|
|
2442
|
+
role: row.message.role,
|
|
2443
|
+
turnId: row.message.turnId || row.message.threadId || row.message.id,
|
|
2444
|
+
speakerVpId: row.speakerVpId,
|
|
2445
|
+
oldestSeq: row.seq,
|
|
2446
|
+
anchor: row,
|
|
2447
|
+
anchorHasText: !!row.text,
|
|
2448
|
+
textParts: row.text ? [row.text] : [],
|
|
2449
|
+
});
|
|
2450
|
+
const mergeRow = (entry, row) => {
|
|
2451
|
+
entry.oldestSeq = Math.min(entry.oldestSeq, row.seq);
|
|
2452
|
+
if (row.text) entry.textParts.unshift(row.text);
|
|
2453
|
+
if (!entry.anchorHasText && row.text) {
|
|
2454
|
+
entry.anchor = row;
|
|
2455
|
+
entry.anchorHasText = true;
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
for (const message of this.#iterateSessionRows(sessionId, { beforeSeq, desc: true })) {
|
|
2460
|
+
const row = visibleRow(message);
|
|
2461
|
+
if (!row) continue;
|
|
2462
|
+
if (current && current.groupKey === row.groupKey) {
|
|
2463
|
+
mergeRow(current, row);
|
|
2464
|
+
continue;
|
|
2465
|
+
}
|
|
2466
|
+
if (current) yield current;
|
|
2467
|
+
current = startEntry(row);
|
|
2468
|
+
}
|
|
2469
|
+
if (current) yield current;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
#projectVisibleResponseEntry(entry) {
|
|
2473
|
+
return {
|
|
2474
|
+
messageId: entry.anchor.message.id,
|
|
2475
|
+
...(entry.anchor.message.clientMessageId ? { clientMessageId: entry.anchor.message.clientMessageId } : {}),
|
|
2476
|
+
turnId: entry.turnId,
|
|
2477
|
+
seq: entry.anchor.seq,
|
|
2478
|
+
role: entry.role,
|
|
2479
|
+
speakerVpId: entry.speakerVpId,
|
|
2480
|
+
timestamp: entry.anchor.message.ts || entry.anchor.message.time || null,
|
|
2481
|
+
_beforeSeq: entry.oldestSeq,
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2384
2485
|
#visibleSearchText(content) {
|
|
2385
2486
|
if (typeof content === 'string') return content.replace(/\s+/g, ' ').trim();
|
|
2386
2487
|
if (!Array.isArray(content)) return '';
|
|
@@ -2399,6 +2500,11 @@ export class ConversationStore {
|
|
|
2399
2500
|
return `${start > 0 ? '…' : ''}${text.slice(start, end)}${end < text.length ? '…' : ''}`;
|
|
2400
2501
|
}
|
|
2401
2502
|
|
|
2503
|
+
#outlineSnippet(text) {
|
|
2504
|
+
const limit = 180;
|
|
2505
|
+
return text.length > limit ? `${text.slice(0, limit).trimEnd()}…` : text;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2402
2508
|
#readSegmentRows(conversationDir, opts = {}) {
|
|
2403
2509
|
return this.#segmentStoreForConversationDir(conversationDir).readAll(opts);
|
|
2404
2510
|
}
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -6537,18 +6537,50 @@ export async function handleYeaftLoadHistory(msg) {
|
|
|
6537
6537
|
}
|
|
6538
6538
|
|
|
6539
6539
|
/**
|
|
6540
|
-
*
|
|
6541
|
-
*
|
|
6542
|
-
*
|
|
6543
|
-
* `
|
|
6544
|
-
* PREPEND these older messages above what it already has).
|
|
6540
|
+
* Load one lightweight Conversation Outline page. The response contains only
|
|
6541
|
+
* visible user/assistant metadata and bounded snippets; full message bodies and
|
|
6542
|
+
* tool payloads stay on the Agent. `beforeSeq` is an exclusive older-page
|
|
6543
|
+
* cursor, and `includeTotal` avoids recounting after the first page.
|
|
6545
6544
|
*
|
|
6546
|
-
*
|
|
6547
|
-
* `handleYeaftLoadHistory` (user / assistant text only). On any internal
|
|
6548
|
-
* failure we still emit an empty chunk so the spinner clears.
|
|
6549
|
-
*
|
|
6550
|
-
* @param {object} msg — { sessionId, beforeSeq, turns }
|
|
6545
|
+
* @param {object} msg — { sessionId, beforeSeq, limit, includeTotal }
|
|
6551
6546
|
*/
|
|
6547
|
+
export async function handleYeaftLoadHistoryOutline(msg) {
|
|
6548
|
+
const sessionId = typeof msg?.sessionId === 'string' ? msg.sessionId.trim() : '';
|
|
6549
|
+
const requestId = typeof msg?.requestId === 'string' ? msg.requestId : null;
|
|
6550
|
+
const beforeSeq = Number.isFinite(msg?.beforeSeq) ? msg.beforeSeq : null;
|
|
6551
|
+
const limit = Math.min(100, Math.max(1, Number.isFinite(msg?.limit) ? Math.floor(msg.limit) : 50));
|
|
6552
|
+
const response = {
|
|
6553
|
+
type: 'yeaft_history_outline',
|
|
6554
|
+
requestId,
|
|
6555
|
+
sessionId: sessionId || null,
|
|
6556
|
+
results: [],
|
|
6557
|
+
hasMore: false,
|
|
6558
|
+
nextBeforeSeq: null,
|
|
6559
|
+
totalCount: null,
|
|
6560
|
+
_requestClientId: msg?._requestClientId || null,
|
|
6561
|
+
};
|
|
6562
|
+
|
|
6563
|
+
if (!sessionId) {
|
|
6564
|
+
sendToServer({ ...response, error: 'invalid_session' });
|
|
6565
|
+
return;
|
|
6566
|
+
}
|
|
6567
|
+
|
|
6568
|
+
try {
|
|
6569
|
+
const defaultYeaftDir = ctx.CONFIG?.yeaftDir || DEFAULT_YEAFT_DIR;
|
|
6570
|
+
const storeDir = resolveSessionYeaftDir(defaultYeaftDir, sessionId);
|
|
6571
|
+
const store = new ConversationStore(storeDir);
|
|
6572
|
+
const result = store.loadVisibleOutlineBySession(sessionId, {
|
|
6573
|
+
limit,
|
|
6574
|
+
beforeSeq,
|
|
6575
|
+
includeTotal: msg?.includeTotal !== false,
|
|
6576
|
+
});
|
|
6577
|
+
sendToServer({ ...response, ...result });
|
|
6578
|
+
} catch (err) {
|
|
6579
|
+
console.error('[Yeaft] Session history outline failed:', err?.message || err);
|
|
6580
|
+
sendToServer({ ...response, error: 'outline_failed' });
|
|
6581
|
+
}
|
|
6582
|
+
}
|
|
6583
|
+
|
|
6552
6584
|
export async function handleYeaftSearchHistory(msg) {
|
|
6553
6585
|
const sessionId = typeof msg?.sessionId === 'string' ? msg.sessionId.trim() : '';
|
|
6554
6586
|
const query = typeof msg?.query === 'string' ? msg.query.trim().slice(0, 500) : '';
|