@yeaft/webchat-agent 0.1.787 → 0.1.788

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.787",
3
+ "version": "0.1.788",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -107,6 +107,13 @@ function serializeMessage(msg) {
107
107
  // the speaker so the UI can render the message on the correct VP track.
108
108
  // For real user messages this is unset.
109
109
  if (msg.speakerVpId) fm.push(`speakerVpId: ${msg.speakerVpId}`);
110
+ // Internal/synthetic rows must round-trip so refresh/history replay can
111
+ // keep them out of the user-visible conversation. Reflection folding uses
112
+ // `_reflection`; other engine-only rows may use one of the explicit flags.
113
+ if (msg._reflection) fm.push('_reflection: true');
114
+ if (msg.internal) fm.push('internal: true');
115
+ if (msg.systemOnly) fm.push('systemOnly: true');
116
+ if (msg.systemOnlyMessage) fm.push('systemOnlyMessage: true');
110
117
 
111
118
  // Token estimate
112
119
  const content = msg.content || '';
@@ -179,6 +186,10 @@ export function parseMessage(raw) {
179
186
  case 'sourceThreadId': msg.sourceThreadId = value; break;
180
187
  case 'groupId': msg.groupId = value; break;
181
188
  case 'speakerVpId': msg.speakerVpId = value; break;
189
+ case '_reflection': msg._reflection = value === 'true'; break;
190
+ case 'internal': msg.internal = value === 'true'; break;
191
+ case 'systemOnly': msg.systemOnly = value === 'true'; break;
192
+ case 'systemOnlyMessage': msg.systemOnlyMessage = value === 'true'; break;
182
193
  // toolCalls are multi-line YAML — handled separately below
183
194
  }
184
195
  }
@@ -366,7 +366,11 @@ function makeGroupContextStub() {
366
366
  function projectPersistedToHistoryEntry(m) {
367
367
  if (!m) return null;
368
368
  if (m.role !== 'user' && m.role !== 'assistant' && m.role !== 'tool') return null;
369
+ if (m._reflection || m.internal || m.systemOnly || m.systemOnlyMessage) return null;
369
370
  const entry = { role: m.role, content: m.content };
371
+ if (m.id) entry.id = m.id;
372
+ if (m.groupId) entry.groupId = m.groupId;
373
+ if (m.speakerVpId) entry.speakerVpId = m.speakerVpId;
370
374
  if (m.toolCallId) entry.toolCallId = m.toolCallId;
371
375
  if (Array.isArray(m.toolCalls) && m.toolCalls.length > 0) {
372
376
  entry.toolCalls = m.toolCalls.map(tc => ({
@@ -2986,20 +2990,22 @@ export async function handleUnifyLoadHistory(msg) {
2986
2990
  const compactSummary = session.conversationStore.readCompactSummary();
2987
2991
 
2988
2992
  for (const m of messages) {
2989
- if (m.role === 'user') {
2990
- sendUnifyOutput({ type: 'user', message: { content: m.content } }, { groupId: m.groupId || null });
2991
- } else if (m.role === 'assistant') {
2993
+ const entry = projectPersistedToHistoryEntry(m);
2994
+ if (!entry) continue;
2995
+ if (entry.role === 'user') {
2996
+ sendUnifyOutput({ type: 'user', message: { content: entry.content, id: entry.id || null } }, { groupId: entry.groupId || null });
2997
+ } else if (entry.role === 'assistant') {
2992
2998
  // speakerVpId rides on the envelope so the frontend can route this
2993
2999
  // replayed assistant text to the correct VP track. Without it, the
2994
3000
  // history replay would merge replies from different VPs onto one
2995
3001
  // anonymous assistant turn.
2996
3002
  const envelopeOpts = {
2997
- groupId: m.groupId || null,
3003
+ groupId: entry.groupId || null,
2998
3004
  };
2999
- if (m.speakerVpId) envelopeOpts.vpId = m.speakerVpId;
3005
+ if (entry.speakerVpId) envelopeOpts.vpId = entry.speakerVpId;
3000
3006
  sendUnifyOutput({
3001
3007
  type: 'assistant',
3002
- message: { content: [{ type: 'text', text: m.content }] },
3008
+ message: { id: entry.id || null, content: [{ type: 'text', text: entry.content }] },
3003
3009
  }, envelopeOpts);
3004
3010
  sendUnifyOutput({ type: 'result', result_text: '' }, envelopeOpts);
3005
3011
  }
@@ -3081,22 +3087,19 @@ export async function handleUnifyLoadMoreHistory(msg) {
3081
3087
  result = { messages: [], oldestSeq: null, hasMore: false };
3082
3088
  }
3083
3089
 
3084
- // Wire shape mirrors handleUnifyLoadHistory's projection: only user /
3085
- // assistant text rows. Tool_use / tool_result replay is out of scope
3086
- // for this PR (today's bootstrap path drops them too).
3087
- //
3088
- // We intentionally do NOT ship `id` or `time` over the wire:
3089
- // `handleUnifyHistoryChunk` reads only role / content / groupId. The
3090
- // pagination cursor (`oldestSeq`) is sent once at the envelope level,
3091
- // so per-row ids are dead weight. `time` would be useful if we render
3092
- // "5 days ago" stamps on older history rows — when that ships, add it
3093
- // back here and consume it in conversationHandler.
3090
+ // Wire shape mirrors handleUnifyLoadHistory's projection: only visible
3091
+ // user / assistant text rows. Internal reflection/system-only rows stay
3092
+ // server-side, and stable ids + speaker attribution ride with each row
3093
+ // so older-history prepend renders exactly like refresh replay.
3094
3094
  const projected = (result.messages || [])
3095
+ .map(projectPersistedToHistoryEntry)
3095
3096
  .filter(m => m && (m.role === 'user' || m.role === 'assistant'))
3096
3097
  .map(m => ({
3098
+ ...(m.id ? { id: m.id } : {}),
3097
3099
  role: m.role,
3098
3100
  content: m.content,
3099
3101
  groupId: m.groupId || null,
3102
+ ...(m.speakerVpId ? { speakerVpId: m.speakerVpId } : {}),
3100
3103
  }));
3101
3104
 
3102
3105
  emit({