@yeaft/webchat-agent 0.1.682 → 0.1.684

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.682",
3
+ "version": "0.1.684",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -442,6 +442,42 @@ export class ConversationStore {
442
442
  return this.#loadFromDir(this.#msgDir, Infinity);
443
443
  }
444
444
 
445
+ /**
446
+ * Load recent hot messages stamped with `groupId`, sorted chronologically.
447
+ * Group-history-isolation (Bug 7): a message lives in exactly one group.
448
+ * Messages without a `groupId` frontmatter (legacy / pre-grouping) are
449
+ * NOT returned — they would otherwise leak into every group's stream.
450
+ *
451
+ * Implementation note: filters AFTER reading the most recent N files
452
+ * because the on-disk order is global by sequence id. We over-read by
453
+ * loading all hot files and slicing the tail of the filtered set so
454
+ * `limit` reflects "N most recent messages in this group", not "N most
455
+ * recent messages on disk that happen to be in this group". For typical
456
+ * inboxes (≤ a few thousand hot messages) this is cheap; if it ever
457
+ * becomes a hot path we add a per-group on-disk index.
458
+ *
459
+ * @param {string} groupId — required; null/empty returns []
460
+ * @param {number} [limit=50]
461
+ * @returns {object[]}
462
+ */
463
+ loadRecentByGroup(groupId, limit = 50) {
464
+ if (!groupId) return [];
465
+ const all = this.#loadFromDir(this.#msgDir, Infinity);
466
+ const filtered = all.filter(m => m && m.groupId === groupId);
467
+ if (limit === Infinity || limit < 0) return filtered;
468
+ return filtered.slice(-limit);
469
+ }
470
+
471
+ /**
472
+ * Load every hot message stamped with `groupId`.
473
+ *
474
+ * @param {string} groupId
475
+ * @returns {object[]}
476
+ */
477
+ loadAllByGroup(groupId) {
478
+ return this.loadRecentByGroup(groupId, Infinity);
479
+ }
480
+
445
481
  /**
446
482
  * Count hot messages.
447
483
  *
@@ -1384,8 +1384,18 @@ export function handleUnifyModelSwitch(msg) {
1384
1384
  /**
1385
1385
  * Handle history load request. Loads recent messages from ConversationStore
1386
1386
  * and replays them through the standard claude_output pipeline.
1387
+ *
1388
+ * Group-history-isolation (Bug 7): when `msg.groupId` is provided the
1389
+ * replay AND the engine's bootstrap context are filtered to that group.
1390
+ * Messages tagged with another groupId — and legacy messages with no
1391
+ * groupId at all — are excluded so a stale `grp_default` (or any other
1392
+ * group) never bleeds into the active group's pane.
1387
1393
  */
1388
1394
  export async function handleUnifyLoadHistory(msg) {
1395
+ const groupId = (msg && typeof msg.groupId === 'string' && msg.groupId) || null;
1396
+ const pickRecent = (store, lim) =>
1397
+ groupId ? store.loadRecentByGroup(groupId, lim) : store.loadRecent(lim);
1398
+
1389
1399
  if (!session) {
1390
1400
  const yeaftDir = ctx.CONFIG?.yeaftDir;
1391
1401
  session = await loadSession({
@@ -1397,7 +1407,12 @@ export async function handleUnifyLoadHistory(msg) {
1397
1407
 
1398
1408
  unifyConversationId = `unify-${Date.now()}`;
1399
1409
 
1400
- restoreHistoryFromRecent(session.conversationStore.loadRecent(50));
1410
+ restoreHistoryFromRecent(pickRecent(session.conversationStore, 50));
1411
+ } else if (groupId) {
1412
+ // Re-entering an existing session with a (possibly new) group filter:
1413
+ // re-seed the engine's flat history so it doesn't carry messages from
1414
+ // another group into the next turn's context.
1415
+ restoreHistoryFromRecent(pickRecent(session.conversationStore, 50));
1401
1416
  }
1402
1417
 
1403
1418
  // Always replay session_ready so refresh / reconnect rebuilds UI state.
@@ -1413,7 +1428,7 @@ export async function handleUnifyLoadHistory(msg) {
1413
1428
  sendGroupSnapshotBroadcast();
1414
1429
 
1415
1430
  const limit = (typeof msg.limit === 'number') ? msg.limit : 50;
1416
- const messages = limit > 0 ? session.conversationStore.loadRecent(limit) : [];
1431
+ const messages = limit > 0 ? pickRecent(session.conversationStore, limit) : [];
1417
1432
  const compactSummary = session.conversationStore.readCompactSummary();
1418
1433
 
1419
1434
  for (const m of messages) {
@@ -1434,6 +1449,7 @@ export async function handleUnifyLoadHistory(msg) {
1434
1449
  hasCompactSummary: !!compactSummary,
1435
1450
  totalHot: session.conversationStore.countHot(),
1436
1451
  totalCold: session.conversationStore.countCold(),
1452
+ groupId,
1437
1453
  });
1438
1454
  }
1439
1455